如何使用 shell(awk、sed 等)删除文件中的前两列
Posted
技术标签:
【中文标题】如何使用 shell(awk、sed 等)删除文件中的前两列【英文标题】:how to remove the first two columns in a file using shell (awk, sed, whatever) 【发布时间】:2012-11-06 22:18:28 【问题描述】:我有一个包含很多行的文件 在每一行中有许多列(字段),由空格“”分隔 每行的列数不同 我想删除前两列 怎么做?
【问题讨论】:
Using awk to print all columns from the nth to the last的可能重复 【参考方案1】:使用 awk,并基于下面的一些选项,使用 for 循环会更灵活一些;有时我可能想删除前 9 列(例如,如果我执行“ls -lrt”),所以我将 2 更改为 9,就是这样:
awk ' for(i=0;i++<2;)$i=""; print $0 ' your_file.txt
【讨论】:
【参考方案2】:使用kscript
kscript 'lines.split().select(-1,-2).print()' file
【讨论】:
【参考方案3】:只用 shell 很简单
while read A B C; do
echo "$C"
done < oldfile >newfile
【讨论】:
这是一个很好的答案,但是您需要使用read -r
而不是 read
。
read -r
将保留反斜杠。 read
不会。例如:echo "foo ba\r"
将产生foo ba\r
的输出。但是,echo "foo ba\r" | (while read first_column second_column; do echo "$second_column"; done)
将仅生成bar
作为输出(删除了反斜杠。添加-r
标志会生成ba\r
的正确输出【参考方案4】:
perl:
perl -lane 'print join(' ',@F[2..$#F])' File
awk:
awk '$1=$2=""1' File
【讨论】:
【参考方案5】:您可以使用cut
:
cut -d " " -f 3- input_filename > output_filename
说明:
cut
:调用剪切命令
-d " "
:使用单个空格作为分隔符(cut
默认使用 TAB)
-f
:指定要保留的字段
3-
: 以字段 3 开头的所有字段
input_filename
:使用这个文件作为输入
> output_filename
:将输出写入此文件。
或者,您可以使用awk
:
awk '$1=""; $2=""; sub(" ", " "); print' input_filename > output_filename
说明:
awk
:调用 awk 命令
$1=""; $2="";
:将字段 1 和 2 设置为空字符串
sub(...);
:清理输出字段,因为字段 1 和 2 仍将由“”分隔
print
: 打印修改后的行
input_filename > output_filename
:同上。
【讨论】:
@wenzi 哎呀,忘了cut
默认使用制表符作为分隔符。查看更新的答案 - 刚刚经过测试,它可以工作。在其他条件相同的情况下,我建议使用 cut
而不是 awk
。
您可以在 awk 中使用 awk 'sub(/([^ ]+ )2/, "")1'
。我同意如果你有一个单字符字段分隔符,无论如何 cut 是更好的选择。
还有一些空格,请改用awk '$1=""; $2=""; sub(/^ +/, ""); print'
或更短的awk '$1=$2=""; sub(/^ +/, "")1'
【参考方案6】:
感谢您发布问题。我还想添加对我有帮助的脚本。
awk ' $1=""; print $0 ' file
【讨论】:
Awk 在这种情况下不保留字段分隔符。 可以加OFS=FS
保留分隔符:unix.stackexchange.com/a/252748/112834【参考方案7】:
这是一种相对容易理解的使用 Awk 的方法:
awk 'print substr($0, index($0, $3))'
这是一个没有模式的简单 awk 命令,因此 内的操作针对每个输入行运行。
操作是简单地打印从第三个字段的位置开始的子字符串。
$0
: 整个输入行
$3
: 第三场
index(in, find)
:返回find
在字符串in
中的位置
substr(string, start)
:返回从索引 start
开始的子字符串
如果您想使用不同的分隔符,例如逗号,可以使用 -F 选项指定:
awk -F"," 'print substr($0, index($0, $3))'
您还可以通过在 中的操作之前指定模式来对输入行的子集进行操作。只有与模式匹配的行才会运行操作。
awk 'patternprint substr($0, index($0, $3))'
模式可以是这样的:
/abcdef/
:使用正则表达式,默认对$0进行操作。
$1 ~ /abcdef/
:对特定字段进行操作。
$1 == blabla
:使用字符串比较
NR > 1
:使用记录/行号
NF > 0
:使用字段/列号
【讨论】:
谢谢你,这是一个比 IMO 接受的更好的答案 去掉最后两列怎么样,倒数? 如果字段 #2 和字段 #3 的内容相同,这将无法正常工作。【参考方案8】:这可能对你有用(GNU sed):
sed -r 's/^([^ ]+ )2//' file
或者对于由一个或多个空格分隔的列:
sed -r 's/^(\S+\s+)2//' file
【讨论】:
【参考方案9】:你可以使用sed
:
sed 's/^[^ ][^ ]* [^ ][^ ]* //'
这会查找以一个或多个非空白、一个空白、另一组一个或多个非空白和另一个空白开头的行,并删除匹配的材料,即前两个字段。 [^ ][^ ]*
比等效但更明确的 [^ ]\1,\
符号略短,第二个可能会遇到 GNU sed
的问题(尽管如果您使用 --posix
作为选项,即使 GNU sed
也不能搞砸了)。 OTOH,如果要重复的字符类更复杂,则编号符号会为简洁起见。很容易将其扩展为处理“空白或制表符”作为分隔符,或“多个空白”或“多个空白或制表符”。也可以对其进行修改以处理第一个字段之前的可选前导空格(或制表符)等。
对于awk
和cut
,请参阅Sampson-Chen 的answer。还有其他方法可以编写awk
脚本,但它们并不比给出的答案更好。请注意,如果您不希望将制表符视为分隔符,或者字段之间可能有多个空格,则可能需要在 awk
中显式设置字段分隔符 (-F" "
)。 POSIX 标准cut
不支持字段之间的多个分隔符; GNU cut
具有有用但非标准的 -i
选项,允许在字段之间使用多个分隔符。
你也可以在纯shell中做:
while read junk1 junk2 residue
do echo "$residue"
done < in-file > out-file
【讨论】:
如果residue
可以包含反斜杠,上面的读取将解释它,而不是在输出中重现它。始终使用while IFS= read -r ...
。
如果bash
用普通的read
解释内容,那么bash
被破坏(再次)。原始 shell 中的 read 命令没有做这种废话;我不相信 POSIX shell 需要它。如果发现bash
做了你所说的那样,我会很生气——我已经对这个程序产生了爱/恨的关系,因为它在很多事情上都做得很好,但也有一些事情做得很糟糕,并且改变遗留行为是最糟糕的行为之一,并且需要启用旧标准行为的选项是......非常烦人。看来你是对的; bash
很无聊!
这种行为是 POSIX,参见 pubs.opengroup.org/onlinepubs/9699919799/utilities/read.html。
我明白了,我没有明确说出来,但您需要 IFS= 的原因是,如果输入中的第一个字段为空,则默认字段拆分将去除前导空格,因此 residue
将开始在字段 4(或更高版本)而不是字段 3。
该死的...好的; POSIX 很糟糕,但 bash
正在追随 POSIX 2008。在超过 25 年的 shell 编程中,我从来没有想要这个功能,但我想我一定是少数。以上是关于如何使用 shell(awk、sed 等)删除文件中的前两列的主要内容,如果未能解决你的问题,请参考以下文章