我有一个问题想变得更简单,如何用下划线分割文件的第二个字段(空格分隔)并将第二部分作为新字段添加到数据中(输出为选项卡)?
例如
file.txt
2 1_123
2 2_345
out.tab
:
2 1_123 123
2 2_345 345
我已经可以使用它了,但是有更简单的方法吗?这似乎太复杂了...
paste -d' ' file.txt <(cat file.txt | cut -d'_' -f 2- ) | tr [:blank:] \\t > out.temp && mv out.temp out.tab
答案1
所有转换都是在文件的同一行内对数据进行混洗,因此使用逐行操作数据的工具(例如 sed 或 awk)会简单得多。
sed -e 's/^[^ \t]*[ \t][^ \t]*_\([^_ \t]*\)/&\1\t/' -e 'y/ /\t/' <<<'2 1_123'
说明:使用正则表达式匹配第一个字段(直到第一个空格或制表符),第二个字段直到最后一个下划线,最后一个下划线之后的第二个字段(放在一个\(…\)
组中,以便可以用于替换文本)。保留相同的文本(&
在替换中),后跟匹配组的内容 ( \1
)。第二个之后的任何字段均保持不变。最后,用制表符替换所有空格。
如果您的 sed 不支持\t
表示制表符,请改用文字制表符。
答案2
对于显示只有一个下划线且位于第二个也是最后一个字段的简单示例,您可以执行以下操作:
$ sed 's/_\(.*\)/& \1/' file | tr ' ' '\t'
2 1_123 123
2 2_345 345
或者,如果您的sed
实现支持扩展正则表达式:
$ sed -E 's/_(.*)/& \1/' file | tr ' ' '\t'
2 1_123 123
2 2_345 345
这将匹配第一个_
和之后的所有内容。括号捕获匹配的字符串,我们可以在替换的右侧将其引用为\1
。&
是匹配的所有内容,因此_
后面是第二个字段的其余部分。因此,替换将打印自身、空格以及 后的字符_
。用制表符替换tr
所有空格。
对于更复杂的情况,您可以拥有任意数量的字段并且其中任何一个都可以包含_
,您可以使用perl
:
$ perl -lane 's/ +/\t/g; $F[1]=~/_(\S+)/; print "$_\t$1"' file
2 1_123 123
2 2_345 345
使得-a
将perl
其输入的空格分割到数组中@F
。第二个字段是$F[1]
因为数组从 开始计数0
。意思-n
是“逐行读取输入文件并应用-e
”给出的脚本。-l
从输入行中删除尾随换行符,并向每个调用添加一个换行符print
。
s/ +/\t/g;
用制表符替换所有出现的一个或多个空格,$F[1]=~/_(\S+)/;
匹配第二个字段中 后面的字符_
并将它们另存为,$1
并且print "$_\t$1"
将打印当前行 ( $_
),后跟匹配的内容。
基于现场数据的另一个有用工具是awk
:
$ awk '{gsub(/ /,"\t");l=$2; sub(/.*_/,"",l); print $0"\t"l}' file
2 1_123 123
2 2_345 345
在 中awk
,输入行会自动按空格分割并变为$1
m $2
... $N
。用制表符替换gsub(/ */,"\t");
所有空格;l=$2
将第二个字段另存为l
;sub(/.*_/,"",l);
删除所有直到并包括_
from 的内容l
;并print $0"\t"l
打印行 ( $0
),后跟制表符和修改后的第二个字段。
答案3
t=$(printf \\t)
sed "s/[^ _]*\(_\([^ _]*\)\)\{0,1\}[^ ]*/& \2/2;s/ */$t/g" <in >out
...应该适用于第二个字段中任意数量的 _ 或任意数量的字段。不过,它确实会将任何空格序列转换为单个制表符。如果两个连续空格应算作两个字段分隔符,请使用:
y/ /$t/
...最后在那里而不是...
s/ */$t/
...代换。