使用剪切重新排列列

时间:2010-01-24 22:17:40

标签: shell

我的文件格式如下

Column1    Column2
str1       1
str2       2
str3       3

我希望重新排列列。我试过下面的命令

  

cut -f2,1 file.txt

该命令不对列重新排序。知道为什么它不起作用吗?

谢谢。

9 个答案:

答案 0 :(得分:133)

对于cut(1)手册页:

  

使用一个,但只能使用-b,-c或-f中的一个。每个LIST都由   一          范围,或用逗号分隔的多个范围。选择的输入被写入          与读取的顺序相同,并且只写一次。

首先到达字段1,然后打印,然后是字段2.

改为使用awk

awk '{ print $2 " " $1}' file.txt

答案 1 :(得分:54)

您还可以合并cutpaste

paste <(cut -f2 file.txt) <(cut -f1 file.txt)

通过评论:可以通过执行以下操作来避免bashisms并删除一个cut实例:

paste file.txt file.txt | cut -f2,3

答案 2 :(得分:7)

只使用shell,

while read -r col1 col2
do
  echo $col2 $col1
done <"file"

答案 3 :(得分:7)

您可以使用Perl:

perl -ane 'print "$F[1] $F[0]\n"' < file.txt
  • -e选项表示执行
  • 之后的命令
  • -n表示逐行读取(打开文件,在本例中为STDOUT,并在行上循环)
  • -a表示将这些行拆分为名为@F的向量(&#34; F&#34; - 类似Field)。 Perl索引向量从0开始,与cut不同,它指的是从1开始的字段。
  • 您可以在阅读时添加-F 模式(-F和模式之间没有空格)以使用模式作为字段分隔符文件而不是默认的空格

运行perl的优点是(如果你知道Perl)你可以在F上做更多的计算而不是重新排列。

答案 4 :(得分:4)

刚刚开始做类似的事情,我不是专家,但我想我会分享我用过的命令。我有一个多列csv,我只需要4列,然后我需要重新排序。

我的文件是管道'|'分隔但可以换掉。

LC_ALL=C cut -d$'|' -f1,2,3,8,10 ./file/location.txt | sed -E "s/(.*)\|(.*)\|(.*)\|(.*)\|(.*)/\3\|\5\|\1\|\2\|\4/" > ./newcsv.csv

不可否认,它确实非常粗糙,但可以调整以适应!

答案 5 :(得分:2)

使用join

join -t $'\t' -o 1.2,1.1 file.txt file.txt

注意:

  • -t $'\t' GNU join中,更直观的-t '\t' 没有 $失败, coreutils v8.28 及更低版本?);可能需要像$这样的变通办法是一个错误。参见:unix join separator char

  • join需要两个文件名,即使只有一个文件正在处理。两次使用相同的名称会诱使join执行所需的操作。

  • 对于资源较少的系统,join的占用空间比其他答案中使用的某些工具要小:

    wc -c $(realpath `which cut join sed awk perl`) | head -n -1
      43224 /usr/bin/cut
      47320 /usr/bin/join
     109840 /bin/sed
     658072 /usr/bin/gawk
    2093624 /usr/bin/perl
    

答案 6 :(得分:1)

就像对建议复制列然后执行 cut 的答案的补充。对于复制,paste 等仅适用于文件,不适用于流。在这种情况下,请改用 sed

cat file.txt | sed s/'.*'/'&\t&'/ | cut -f2,3

这适用于文件和流,如果您不只是使用 cat 从文件中读取数据,而是在重新排列列之前做一些有趣的事情,这会很有趣。 >

相比之下,以下方法不起作用:

cat file.txt | paste - - | cut -f2,3

这里,双标准输入占位符 paste 不复制标准输入,而是读取下一行。

答案 7 :(得分:0)

使用sed

与基本正则表达式的嵌套子表达式一起使用sed来捕获列内容并对其进行重新排序。在这种情况下,这种方法最适合对列进行重新排列的切割数量有限的情况。

基本思想是用\(\)包围搜索模式的有趣部分,可以用\#的替换模式播放搜索模式,其中#表示子表达式在搜索模式中的顺序位置。

例如:

$ echo "foo bar" | sed "s/\(foo\) \(bar\)/\2 \1/"

产量:

bar foo

将扫描子表达式外部的文本,但不会保留在替换字符串中以供回放。

尽管该问题并未讨论固定宽度的列,但我们将在此处进行讨论,因为这是对所提出的任何解决方案的有效衡量。为简单起见,尽管解决方案可以扩展到其他定界符,但我们假设文件是​​用空格定界的。

合拢空间

为说明最简单的用法,我们假设可以将多个空格折叠成单个空格,第二列值以EOL终止(而不是填充空格)。

文件:

bash-3.2$ cat f
Column1    Column2
str1       1
str2       2
str3       3
bash-3.2$ od -a f
0000000    C   o   l   u   m   n   1  sp  sp  sp  sp   C   o   l   u   m
0000020    n   2  nl   s   t   r   1  sp  sp  sp  sp  sp  sp  sp   1  nl
0000040    s   t   r   2  sp  sp  sp  sp  sp  sp  sp   2  nl   s   t   r
0000060    3  sp  sp  sp  sp  sp  sp  sp   3  nl 
0000072

转换:

bash-3.2$ sed "s/\([^ ]*\)[ ]*\([^ ]*\)[ ]*/\2 \1/" f
Column2 Column1
1 str1
2 str2
3 str3
bash-3.2$ sed "s/\([^ ]*\)[ ]*\([^ ]*\)[ ]*/\2 \1/" f | od -a
0000000    C   o   l   u   m   n   2  sp   C   o   l   u   m   n   1  nl
0000020    1  sp   s   t   r   1  nl   2  sp   s   t   r   2  nl   3  sp
0000040    s   t   r   3  nl
0000045

保留列宽

现在让我们将该方法扩展到具有恒定宽度的列的文件,同时允许列具有不同的宽度。

文件:

bash-3.2$ cat f2
Column1    Column2
str1       1
str2       2
str3       3
bash-3.2$ od -a f2
0000000    C   o   l   u   m   n   1  sp  sp  sp  sp   C   o   l   u   m
0000020    n   2  nl   s   t   r   1  sp  sp  sp  sp  sp  sp  sp   1  sp
0000040   sp  sp  sp  sp  sp  nl   s   t   r   2  sp  sp  sp  sp  sp  sp
0000060   sp   2  sp  sp  sp  sp  sp  sp  nl   s   t   r   3  sp  sp  sp
0000100   sp  sp  sp  sp   3  sp  sp  sp  sp  sp  sp  nl
0000114

转换:

bash-3.2$ sed "s/\([^ ]*\)\([ ]*\) \([^ ]*\)\([ ]*\)/\3\4 \1\2/" f2
Column2 Column1
1       str1      
2       str2      
3       str3      
bash-3.2$ sed "s/\([^ ]*\)\([ ]*\) \([^ ]*\)\([ ]*\)/\3\4 \1\2/" f2 | od -a
0000000    C   o   l   u   m   n   2  sp   C   o   l   u   m   n   1  sp
0000020   sp  sp  nl   1  sp  sp  sp  sp  sp  sp  sp   s   t   r   1  sp
0000040   sp  sp  sp  sp  sp  nl   2  sp  sp  sp  sp  sp  sp  sp   s   t
0000060    r   2  sp  sp  sp  sp  sp  sp  nl   3  sp  sp  sp  sp  sp  sp
0000100   sp   s   t   r   3  sp  sp  sp  sp  sp  sp  nl 
0000114

最后,尽管问题的示例没有长度不等的字符串,但此sed表达式支持这种情况。

文件:

bash-3.2$ cat f3
Column1    Column2
str1       1      
string2    2      
str3       3      

转换:

bash-3.2$ sed "s/\([^ ]*\)\([ ]*\) \([^ ]*\)\([ ]*\)/\3\4 \1\2/" f3
Column2 Column1   
1       str1      
2       string2   
3       str3    
bash-3.2$ sed "s/\([^ ]*\)\([ ]*\) \([^ ]*\)\([ ]*\)/\3\4 \1\2/" f3 | od -a
0000000    C   o   l   u   m   n   2  sp   C   o   l   u   m   n   1  sp
0000020   sp  sp  nl   1  sp  sp  sp  sp  sp  sp  sp   s   t   r   1  sp
0000040   sp  sp  sp  sp  sp  nl   2  sp  sp  sp  sp  sp  sp  sp   s   t
0000060    r   i   n   g   2  sp  sp  sp  nl   3  sp  sp  sp  sp  sp  sp
0000100   sp   s   t   r   3  sp  sp  sp  sp  sp  sp  nl 
0000114

与Shell下其他列重新排序方法相比

  • 令人惊讶的是,对于文件操作工具,awk不适合从字段切到记录结尾。可以使用正则表达式来完成此操作,例如\(xxx.*$\),其中xxx是与列匹配的表达式。

  • 在内部shell脚本中实施粘贴和剪切子shell会很棘手。将命令行脚本中的代码带入Shell脚本后无法解析。至少这是我的经验(这促使我采用这种方法)。

答案 8 :(得分:0)

也使用Perl扩展@Met的答案:
如果输入和输出用TAB分隔:

perl -F'\t' -lane 'print join "\t", @F[1, 0]' in_file

如果输入和输出用空格分隔:

perl -lane 'print join " ", @F[1, 0]' in_file

在这里,
-e告诉Perl在内联而不是在单独的脚本文件中查找代码,
-n一次读取输入1行,
-l在读取行(类似于\n)后删除输入记录分隔符(* NIX上的chomp),并将输出记录分隔符(* NIX上的\n)添加到每个print
-a将空白处的输入行拆分为数组@F
-F'\t'-a结合使用可将TAB上的输入行分割,而不是将空白分成数组@F

@F[1, 0]是由数组@F的第2个元素和第1个元素按此顺序组成的数组。请记住,Perl中的数组是零索引的,而cut中的字段是1索引的。因此,@F[0, 1]中的字段与cut -f1,2中的字段相同。

请注意,与上面发布的其他一些答案相比,这种表示法使输入的操作更加灵活(对于简单任务来说很好)。例如:

# reverses the order of fields:
perl -F'\t' -lane 'print join "\t", reverse @F' in_file

# prints last and first fields only:
perl -F'\t' -lane 'print join "\t", @F[-1, 0]' in_file