【问题标题】:Swap two columns - awk, sed, python, perl交换两列 - awk、sed、python、perl
【发布时间】:2012-08-15 10:30:16
【问题描述】:

我有一个大文件中的数据(280 列宽,700 万行长!),我需要交换前两列。我想我可以使用某种 awk for 循环来执行此操作,打印 $2、$1,然后是文件末尾的范围 - 但我不知道如何执行范围部分,我无法打印 $2 、1 美元、3 美元……280 美元!我在这里看到的大多数列交换答案都特定于具有可管理列数的小文件,因此我需要一些不依赖于指定每个列号的东西。

文件以制表符分隔:

Affy-id chr 0 pos NA06984 NA06985 NA06986 NA06989

【问题讨论】:

    标签: sed awk


    【解决方案1】:

    您可以通过交换前两个字段的值来做到这一点:

    awk ' { t = $1; $1 = $2; $2 = t; print; } ' input_file
    

    【讨论】:

    • 这个答案对于不同大小的列和它们的分隔符是有问题的。这里有更多可扩展的答案unix.stackexchange.com/a/31596/16920
    • 使用-F '\t'标签在最终输出中被吃掉了。有没有办法保存它们?
    • 好的,必须指定OFS=$'\t',正如下面answer 所指出的那样。 @perreal,也许值得用附加参数更新答案?
    • 如果你使用:awk '{ print $2, $1}' 是一样的 :D
    • @cornuz,没问题,请注意,您的建议仅打印 2 列。 OP 希望打印所有列,而不仅仅是前两列。
    【解决方案2】:

    我在带有制表符分隔文件的 Windows 系统上使用 cygwin 尝试了 perreal 的答案。它不起作用,因为标准分隔符是空格。

    如果您遇到同样的问题,请尝试以下方法:

    awk -F $'\t' ' { t = $1; $1 = $2; $2 = t; print; } ' OFS=$'\t' input_file
    

    传入分隔符由-F $'\t'定义,输出分隔符由OFS=$'\t'定义。

    awk -F $'\t' ' { t = $1; $1 = $2; $2 = t; print; } ' OFS=$'\t' input_file > output_file
    

    【讨论】:

    • 超级!我错过了OFS=$'\t' 参数!
    • 这种方法可能会在行首出现制表符。可能不是预期的结果。
    【解决方案3】:

    试试这个与您的问题更相关:

    awk '{printf("%s\t%s\n", $2, $1)}' inputfile
    

    【讨论】:

    • 这只会打印前两列。稍微紧凑一点的是awk '{print $2 "\t" $1}' inputfile。
    【解决方案4】:

    这可能对你有用(GNU sed):

    sed -i 's/^\([^\t]*\t\)\([^\t]*\t\)/\2\1/' file
    

    【讨论】:

    • 对我们 vim 用户来说是完美的解决方案。
    【解决方案5】:

    您是否尝试过使用 cut 命令?例如

    cat myhugefile | cut -c10-20,c1-9,c21- > myrearrangedhugefile
    

    【讨论】:

    • 我没有,但我会记住以备将来使用!
    • -c=characters ... 所以这不会交换列。
    • 它将交换输出文件中的列 - 自己尝试一下
    • 不知道字数怎么办? cat myhugefile | cut -f2,1 提供与 cat myhugefile | cut -f1,2 相同的输出
    • 您可以将每一列输出到一个中间文件。类似于: cut -f2 myhugefile > piece1 ; cut -f1 myhugefile >piece2 |粘贴piece1 piece2 > myrearrangedhugefile ; rm 片 1 ; rm片2
    【解决方案6】:

    这在 perl 中也很简单:

    perl -pe 's/^(\S+)\t(\S+)/$2\t$1/;' file > outputfile
    

    【讨论】:

      【解决方案7】:

      你可以在 Perl 中做到这一点:

      perl -F\\t -nlae 'print join("\t", @F[1,0,2..$#F])' inputfile
      

      -F 指定分隔符。在大多数 shell 中,您需要在反斜杠前面加上另一个来转义它。在某些平台上,-F 会自动暗示 -n 和 -a,因此可以删除它们。

      对于您的问题,您不需要使用-l,因为最后一列出现在输出的最后。但是如果在不同的情况下,如果最后一列需要出现在其他列之间,则必须删除换行符。 -l 开关会处理这个问题。

      连接中的"\t" 可以更改为其他任何内容,以在输出中生成不同的分隔符。

      2..$#F 指定从 2 到最后一列的范围。您可能已经猜到了,在方括号内,您可以按所需顺序放置任何单列或列范围。

      【讨论】:

        【解决方案8】:

        除了你的 shell 不需要调用其他任何东西:

        bash> while read col1 col2 rest; do 
                echo $col2 $col1 $rest
              done <input_file
        

        测试:

        bash> echo "first second a c d e f g" | 
              while read col1 col2 rest; do 
                echo $col2 $col1 $rest
              done
        second first a b c d e f g
        

        【讨论】:

          【解决方案9】:

          甚至可能使用 "inlined" Python - 就像在 shell 脚本中的 Python 脚本中一样 - 但 仅当您想在事前或事后使用 Bash 编写更多脚本时...否则它会变得不必要地复杂。

          脚本文件内容process.sh:

          #!/bin/bash
          
          # inline Python script
          read -r -d '' PYSCR << EOSCR
          from __future__ import print_function
          import codecs
          import sys
          
          encoding = "utf-8"
          fn_in = sys.argv[1]
          fn_out = sys.argv[2]
          
          # print("Input:", fn_in)
          # print("Output:", fn_out)
          
          with codecs.open(fn_in, "r", encoding) as fp_in, \
                  codecs.open(fn_out, "w", encoding) as fp_out:
              for line in fp_in:
                  # split into two columns and rest
                  col1, col2, rest = line.split("\t", 2)
                  # swap columns in output
                  fp_out.write("{}\t{}\t{}".format(col2, col1, rest))
          EOSCR
          
          # ---------------------
          # do setup work?
          # e. g. list files for processing
          
          # call python script with params
          python3 -c "$PYSCR" "$inputfile" "$outputfile"
          
          # do some more processing
          # e. g. rename outputfile to inputfile, ...
          

          如果您只需要为单个文件交换列,那么您也可以只创建一个 Python 脚本并静态定义文件名。或者只是使用上面的答案。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2017-06-16
            • 2013-11-11
            • 2020-01-24
            • 1970-01-01
            • 2023-04-10
            • 2014-06-27
            相关资源
            最近更新 更多