如何使用shell删除文件中的前两列(awk,sed等)


Answers:


148

您可以使用cut

cut -d " " -f 3- input_filename > output_filename

说明:

  • cut:调用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 oops,忘记cut默认使用制表符作为分隔符。查看更新的答案-刚刚经过测试,即可正常工作。在其他条件相同的情况下,我建议使用cutover awk
sampson-chen 2012年

您只需用awk即可完成操作awk '{sub(/([^ ]+ ){2}/, "")}1'。我同意,如果您有单字符字段分隔符,无论如何,cut是更好的选择。
Ed Morton 2012年

仍然还有一些空格,可以用空格awk '{$1=""; $2=""; sub(/^ +/, ""); print}'代替,或者用更短的空格awk '{$1=$2=""; sub(/^ +/, "")}1'
jirislav

26

这是使用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 'pattern{print substr($0, index($0, $3))}'

模式可以是这样的:

  • /abcdef/:使用正则表达式,默认情况下对$ 0进行运算。
  • $1 ~ /abcdef/:在特定字段上操作。
  • $1 == blabla:使用字符串比较
  • NR > 1:使用记录/行号
  • NF > 0:使用字段/列号

1
谢谢你,这比一个被接受的国际海事组织更好的答案
Alex Forbes

如何删除倒数第二列呢?
CMCDragonkai 2014年

10
如果字段#2和字段#3具有相同的内容,这将无法正常工作。
PHP学习者2015年


9
awk '{$1=$2="";$0=$0;$1=$1}1'

输入项

a b c d

输出量

c d

你能解释一下吗?:)我不明白,为什么需要$0=$0;$1=$1空格才能消失
jirislav

6

您可以使用sed

sed 's/^[^ ][^ ]* [^ ][^ ]* //'

这将查找以一个或多个非空白,一个空格,另一组一个或多个非空白和另一个空格开头的行,并删除匹配的材料,也就是前两个字段。它[^ ][^ ]*比等效但更明确的[^ ]\{1,\}符号略短,并且第二个符号可能会与GNU发生问题sed(尽管如果您将其--posix用作选项,即使GNUsed也无法解决)。OTOH,如果要重复的字符类别更为复杂,则为简明起见,用数字表示法是可以取胜的。很容易将其扩展为将“空白或制表符”作为分隔符,或“多个空白”或“多个空白或制表符”。还可以对其进行修改,以处理第一个字段之前的可选前导空白(或制表符),等等。

对于awkcut,请参阅Sampson-Chen答案。还有其他编写awk脚本的方法,但是从本质上说,它们并不比给出的答案更好。请注意,如果您不希望将制表符视为分隔符,则可能需要显式设置字段分隔符(-F" "),awk否则字段之间可能会有多个空白。POSIX标准cut不支持字段之间的多个分隔符。GNUcut具有有用但非标准的-i选项,以允许在字段之间使用多个分隔符。

您也可以在纯shell中执行此操作:

while read junk1 junk2 residue
do echo "$residue"
done < in-file > out-file

如果residue可以包含反斜杠,则上述读取内容将对其进行解释,而不会在输出中复制它。始终使用while IFS= read -r ...
Ed Morton 2012年

如果bash用一个简单的解释内容read,那么bash(再次)被破坏。原始shell中的read命令并没有废话。我认为POSIX shell不需要它。这会激怒我,bash让您发现自己确实做到了—我已经与该程序建立了爱恨交织的关系,因为它在很多方面做得很好,但是有些事情做得不好,并且不断变化遗留行为是最糟糕的行为之一,并且要求启用旧的标准行为的选项非常烦人。看来你是对的;bash很无聊!
乔纳森·勒夫勒


我看到我没有明确说出来,但您需要IFS =的原因是,如果输入中的第一个字段为空,则默认字段拆分将去除前导空格,因此residue将从字段4(或更高版本)而不是字段3开始。
埃德莫顿

该死...好; POSIX令人厌烦,但bash它紧随POSIX2008 。我从不希望在超过四分之一世纪的Shell编程中使用该功能,但我想我应该很少。
乔纳森·勒夫勒

6

仅使用外壳即可做到这一点

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
robert

4

perl:

perl -lane 'print join(' ',@F[2..$#F])' File

awk:

awk '{$1=$2=""}1' File

2

使用awk并基于下面的某些选项,使用for循环会稍微灵活一些;有时我可能想删除前9列(例如,如果我执行“ ls -lrt”),那么我将2更改为9,就这样:

awk '{ for(i=0;i++<2;){$i=""}; print $0 }' your_file.txt


1

这可能对您有用(GNU sed):

sed -r 's/^([^ ]+ ){2}//' file

或对于由一个或多个空格分隔的列:

sed -r 's/^(\S+\s+){2}//' file

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.