shell 使用剪切重新排列列

pkbketx9  于 2022-12-13  发布在  Shell
关注(0)|答案(9)|浏览(139)

我有一个以下格式的文件

Column1    Column2
str1       1
str2       2
str3       3

我想重新排列列。我试过下面的命令
cut -f2,1个文件. txt
命令没有重新排序列。知道为什么它不起作用吗?

anhgbhbe

anhgbhbe1#

有关cut(1)手册页,请参阅:
请使用-B、-c或-f中的一个,且只能使用其中一个。每个LIST由一个范围或多个范围组成,范围之间用逗号分隔。所选输入的写入顺序与读取顺序相同,并且只写入一次。
它首先到达字段1,以便打印,然后是字段2。
请改用awk

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

vom3gejh2#

您也可以将cutpaste结合使用:

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

通过评论:可以通过以下方式避免害羞并消除一个剪切示例:

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

c9x0cxw03#

使用join

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

备注:

  • -t $'\t'GNUjoin中,更直观的-t '\t' * 没有 * $失败,(coreutils**v8.28 和更早版本?);这可能是一个bug,需要使用$之类的解决方法。请参见: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
8ehkhllq

8ehkhllq4#

仅使用 shell ,

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

bnl4lu3b5#

您可以使用Perl来实现这一点:

perl -ane 'print "$F[1] $F[0]\n"' < file.txt
  • -e选项表示在它之后执行命令
  • -n表示逐行读取(打开文件,在本例中为STDOUT,并循环遍历各行)
  • -a意味着将这些行拆分为一个名为@F(“F”- like Field)的向量。Perl从0开始索引向量,而cut从1开始索引字段。
  • 您可以添加-F pattern(-F和 pattern 之间没有空格),以便在阅读文件时使用 pattern 作为字段分隔符,而不是默认的空格

运行Perl的好处是(如果您了解Perl),与重新排列列相比,您可以在F上执行更多的计算。

ds97pgxw

ds97pgxw6#

只是一直在做一些非常相似的事情,我不是Maven,但我想我会分享我用过的命令。我有一个多列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

诚然,这是真的粗糙和准备,但它可以调整,以适应!

vu8f3i0k

vu8f3i0k7#

正如对建议复制列然后执行cut的回答的补充。对于复制,paste等将仅适用于文件,而不适用于流。在这种情况下,请改用sed
cat file.txt | sed s/'.*'/'&\t&'/ | cut -f2,3
这对文件和流都有效,如果不是仅仅用cat阅读文件,而是在重新排列列之前做一些有趣的事情,这就很有趣了。
相比之下,以下方法不起作用:
cat file.txt | paste - - | cut -f2,3
这里,双stdin占位符paste不复制stdin,但读取下一行。

qzwqbdag

qzwqbdag8#

使用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并不适合从一个字段剪切到记录的结尾。在sed中,这可以使用正则表达式来完成,例如\(xxx.*$\),其中xxx是匹配列的表达式。
  • 当在shell脚本中实现时,使用pastecut子shell会很棘手。从命令行运行的代码在进入shell脚本时无法解析。至少这是我的经验(驱使我使用这种方法)。
js5cn81o

js5cn81o9#

扩展来自@Met的答案,同样使用Perl:
如果输入和输出以制表符分隔:

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 on *NIX)(类似于chomp),并将输出记录分隔符(\n on *NIX)添加到每个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

相关问题