我的文件格式如下
Column1 Column2 str1 1 str2 2 str3 3
我希望重新排列列。我试过下面的命令
cut -f2,1 file.txt
该命令不对列重新排序。知道为什么它不起作用吗?
谢谢。
答案 0 :(得分:133)
对于cut(1)
手册页:
使用一个,但只能使用-b,-c或-f中的一个。每个LIST都由 一 范围,或用逗号分隔的多个范围。选择的输入被写入 与读取的顺序相同,并且只写一次。
首先到达字段1,然后打印,然后是字段2.
改为使用awk
:
awk '{ print $2 " " $1}' file.txt
答案 1 :(得分:54)
您还可以合并cut
和paste
:
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
运行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