使用cut重新排列列


134

我有以下格式的文件

列1列2
str1 1
str2 2
str3 3

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

剪切-f2,1 file.txt

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

谢谢。

Answers:


148

对于cut(1)手册页:

请仅使用-b,-c或-f中的一个。每个LIST由一个范围或由逗号分隔的多个范围组成。所选输入的写入顺序与读取的顺序相同,并且仅写入一次。

它首先到达字段1,以便进行打印,然后到达字段2。

使用awk来代替:

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

12
太糟糕了cut,不支持此直观的重新排序命令。无论如何,还有一个小窍门:你可以使用awk-FS,并-OFS选择使用自定义输入和输出字段分隔符(如-d--output-delimitercut)。
马拉纳

12
对不起,FS是一个选择,OFS是一个变量。例如awk -v OFS=";" -F"\t" '{print $2,$1}'
malana

2
Windows的Git Bash用户注意:如果您从上面的命令中获得了奇怪的输出,看起来像是相互覆盖的列,则应该归咎于回车。将文件中的EOL从CRLF更改为LF。
jakub.g 2015年

1
另外,如果您不想更改输入文件,则可以| sed 's/\r//' | 先将其通过管道传输到awk
jakub.g 2015年

2
这很简单,但可能对某些用户有用,只需用\ t替换空格以通过选项卡重新排序,并且如果您想要更多列,则可以这样做,例如awk '{print $4 "\t" $2 "\t" $6 "\t" $7}' file
FatihSarigol

63

您也可以结合cutpaste

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

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

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

3
不确定是否符合条件,但是:f = file.txt粘贴<(cut -f2 $ f)<(cut -f1 $ f)。另外,我注意到当您有很多列并且想要在它们的大块之间移动时,此方法是最简单的。
Michael Rusch

不适用于同一列中长度可变的单元格
kraymer '16

2
@kraymer是什么意思? cut只要您具有唯一的列分隔符,就可以很好地用于变长列。
三人

1
要消除冗余文件,您可以使用tee:
JJW5432

2
通过执行以下操作可以避免bashisms并删除其中的一个实例cutpaste file.txt file.txt | cut -f2,3
agc

7

仅使用外壳,

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

这通常效率很低。通常,您会发现例如相应的Awk脚本要快得多。您还应该小心引用这些值,"$col2"并且"$col1"-数据中可能包含shell元字符或其他恶名。
3:08三胞胎

7

您可以为此使用Perl:

perl -ane 'print "$F[1] $F[0]\n"' < file.txt
  • -e选项表示在其后执行命令
  • -n表示逐行读取(在这种情况下为STDOUT,打开文件,然后循环遍历)
  • -a表示将这样的行拆分为一个称为@F的向量(“ F”-类似于Field)。Perl索引从0开始的向量,这与cut索引从1开始的字段不同。
  • 您可以添加-F 模式(-F和pattern之间没有空格)以在读取文件时将pattern用作字段分隔符,而不是默认的空白

运行perl的优点是(如果您知道Perl),与重新排列列相比,您可以在F上执行更多的计算。


perlrun(1)声称-a隐式设置了-n,但是如果我在没有-n set的情况下运行,它似乎不会循环。奇。
特伦顿

什么版本的?perl -ae print工作原理cat为我
的PWE

5

使用join

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

笔记:

  • -t $'\t'GNU join更直观-t '\t' 没有$失败,(coreutils的 v8.28和更早?); 这可能是一个错误,必须采取类似的解决方法$。请参阅:unix加入分隔符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

3

刚刚从事非常相似的工作,我不是专家,但我想我会分享我使用的命令。我有一个多列的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

诚然,它确实很粗糙且可以使用,但是可以进行调整以适合!


这不能回答提出的问题。本着堆栈溢出的精神,请在发布之前花时间回答问题。
Bill Gale

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脚本后无法解析。至少这是我的经验(这促使我采用这种方法)。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.