【问题标题】:Sort the tab-delimited numbers on each line of a file对文件每一行的制表符分隔的数字进行排序
【发布时间】:2015-07-01 16:50:57
【问题描述】:

我正在尝试单独对文件每一行的数字进行排序。一行内的数字由制表符分隔。 (我使用了空格,但它们实际上是制表符。)

例如,对于以下输入

5 8 7 6 
1 5 6 8
8 9 7 1

期望的输出是:

5 6 7 8
1 5 6 7
1 7 8 9

到目前为止我的尝试是:

let i=1
while read line
do
    echo "$line" | tr "    " "\n" | sort -g
    cut -f $i fileName | paste -s >> tempFile$$
    ((++i))
done < fileName

【问题讨论】:

  • 有人提议 stackoverflow.com/questions/25062169/… 重复,它有一个实际上解决了这个问题的答案,但这个问题实际上是关于洗牌整个列,以便对第一行进行排序。

标签: linux bash


【解决方案1】:

这是我得到的最好的——我确信它可以用 awk/sed/perl 用 6 个字符完成:

while read line
do
  echo $(printf "%d\n" $line | sort -n) | tr ' ' \\t >> another-file.txt
done < my-input-file.txt

【讨论】:

  • 谢谢!这就是我一直在寻找的。除了回显该行之外,是否可以对每一行中的数字进行排序并将其放回变量行以便我可以将其放入另一个文件中?
  • 我认为我的编辑解决了您的问题,但请告诉我。
  • 是的,这正是我想要的,它现在完美运行。我唯一担心的是它在最后一行输出一个 0,我不知道为什么会这样。之后我可以删除最后一行。非常感谢。
  • 密切注意使用的printf 技巧。在 bash 中,带有单个转换说明符(即%d\n)的printf 将在单独的行上打印所有行中的数字,从而允许sort -n 对它们进行排序,而echo $(command substitution) 将以原始格式在一行上打印结果。这也适用于索引数组(例如printf "%d\n" ${array[@]})(您也可以使用%s
【解决方案2】:

使用一些特定于 GNU awk 的功能:

$ awk 'BEGIN{ PROCINFO["sorted_in"] = "@ind_num_asc" }
      { delete(a); n = 0; for (i=1;i<=NF;++i) a[$i]; 
        for (i in a) printf "%s%s", i, (++n<NF?FS:RS) }' file
5 6 7 8
1 5 6 8
1 7 8 9

每个字段都设置为数组a 中的一个键。在 GNU awk 中,可以指定 for (i in a) 循环遍历数组的顺序 - 在这里,我将其设置为按数字升序进行。

【讨论】:

    【解决方案3】:

    这是一个可以做到这一点的 bash 脚本。它接受一个文件名参数或读取标准输入,在 CentOS 上测试并假设 IFS=$' \t\n'。

    #!/bin/bash
    
    if [ "$1" ] ; then exec < "$1" ; fi
    
    cat - | while read line
    do   
      set $line
      echo $(for var in "$@"; do echo $var; done | sort -n) | tr " " "\t" 
    done
    

    如果你想把输出放在另一个文件中运行它:

    cat 输入文件 |排序脚本>另一个文件

    sorting_script input_file > 另一个文件

    【讨论】:

      【解决方案4】:

      考虑为此使用perl

      perl -ape '@F=sort @F;$_="@F\n"' input.txt
      

      这里-a 开启自动字段拆分(就像awk 所做的那样)到数组@F,-p 使它执行每一行的脚本并每次打印$_-e 指定直接在命令行上编写脚本。

      恐怕还不到 6 个字符,肖恩。

      这在 awk 中应该很简单,但它并不完全具备所需的功能。如果有一个数组$@ 对应于$1$2 等字段,那么解决方案将是awk '{asort $@}' input.txt,但遗憾的是没有这样的数组存在。将字段移入数组并再次移出数组所需的循环使其比 bash 版本更长:

      awk '{for(i=1;i<=NF;i++)a[i]=$i;asort(a);for(i=1;i<=NF;i++)printf("%s ",a[i]);printf("\n")}' input.txt
      

      所以awk 不是适合这里工作的工具。同样有点奇怪的是sort本身并没有控制其排序方向的开关。

      【讨论】:

        【解决方案5】:

        使用awk

        $ cat file
        5 8 7 6 
        1 5 6 8
        8 9 7 1
        
        $ awk '{c=1;while(c!=""){c=""; for(i=1;i<NF;i++){n=i+1; if($i>$n){c=$i;$i=$n;$n=c}}}}1' file
        5 6 7 8
        1 5 6 8
        1 7 8 9
        

        可读性更好的版本

        awk '{
              c=1
              while(c!="")
              {
                 c="" 
                 for(i=1;i<NF;i++)
                 {
                    n=i+1
                    if($i>$n)
                    {
                      c=$i
                      $i=$n
                      $n=c
                    }
                 }
              }
            }1
           ' file
        

        如果你有ksh,你可以试试这个

        #!/usr/bin/env ksh
        while read line ; do 
              set -s +A  cols $line 
              echo ${cols[*]}
        done < "input_file"
        

        测试

        [akshay@localhost tmp]$ cat test.ksh
        #!/usr/bin/env ksh
        
        cat <<EOF | while read line ; do set -s +A  cols $line; echo ${cols[*]};done
        5 8 7 6 
        1 5 6 8
        8 9 7 1
        EOF
        
        [akshay@localhost tmp]$ ksh test.ksh
        5 6 7 8
        1 5 6 8
        1 7 8 9
        

        【讨论】:

          猜你喜欢
          • 2010-11-05
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-08-24
          • 2017-09-30
          • 2020-03-14
          • 2013-06-30
          相关资源
          最近更新 更多