打印除前三列之外的所有列

时间:2010-04-13 00:26:51

标签: awk

太麻烦了:

awk '{print " "$4" "$5" "$6" "$7" "$8" "$9" "$10" "$11" "$12" "$13}' things

19 个答案:

答案 0 :(得分:74)

awk '{for(i=1;i<4;i++) $i="";print}' file

答案 1 :(得分:69)

使用剪切

$ cut -f4-13 file

或者如果你坚持使用awk而13美元是最后一个字段

$ awk '{$1=$2=$3="";print}' file

否则

$ awk '{for(i=4;i<=13;i++)printf "%s ",$i;printf "\n"}' file

答案 2 :(得分:47)

不添加额外的前导或尾随whitespace的解决方案:

awk '{ for(i=4; i<NF; i++) printf "%s",$i OFS; if(NF) printf "%s",$NF; printf ORS}'

### Example ###
$ echo '1 2 3 4 5 6 7' |
  awk '{for(i=4;i<NF;i++)printf"%s",$i OFS;if(NF)printf"%s",$NF;printf ORS}' |
  tr ' ' '-'
4-5-6-7

Sudo_O使用三元运算符 NF?ORS:OFS 提出了一个优雅的改进

$ echo '1 2 3 4 5 6 7' |
  awk '{ for(i=4; i<=NF; i++) printf "%s",$i (i==NF?ORS:OFS) }' |
  tr ' ' '-'
4-5-6-7

EdMorton提供了一个保留字段之间原始空格的解决方案:

$ echo '1   2 3 4   5    6 7' |
  awk '{ sub(/([^ ]+ +){3}/,"") }1' |
  tr ' ' '-'
4---5----6-7

BinaryZebra还提供了两个很棒的解决方案:
(这些解决方案甚至可以保留原始字符串的尾随空格)

$ echo -e ' 1   2\t \t3     4   5   6 7 \t 8\t ' |
  awk -v n=3 '{ for ( i=1; i<=n; i++) { sub("^["FS"]*[^"FS"]+["FS"]+","",$0);} } 1 ' |
  sed 's/ /./g;s/\t/->/g;s/^/"/;s/$/"/'
"4...5...6.7.->.8->."

$ echo -e ' 1   2\t \t3     4   5   6 7 \t 8\t ' |
  awk -v n=3 '{ print gensub("["FS"]*([^"FS"]+["FS"]+){"n"}","",1); }' |
  sed 's/ /./g;s/\t/->/g;s/^/"/;s/$/"/'
"4...5...6.7.->.8->."

评论中larsr给出的解决方案几乎是正确的:

$ echo '1 2 3 4 5 6 7' | 
  awk '{for (i=3;i<=NF;i++) $(i-2)=$i; NF=NF-2; print $0}' | tr  ' ' '-'
3-4-5-6-7

这是larsr解决方案的固定和参数化版本:

$ echo '1 2 3 4 5 6 7' | 
  awk '{for(i=n;i<=NF;i++)$(i-(n-1))=$i;NF=NF-(n-1);print $0}' n=4 | tr ' ' '-'
4-5-6-7

2013年9月之前的所有其他答案都不错,但添加了额外的空格:

答案 3 :(得分:38)

试试这个:

awk '{ $1=""; $2=""; $3=""; print $0 }'

答案 4 :(得分:24)

正确的方法是使用RE间隔,因为它可以让您简单地说明要跳过的字段数,并保留其余字段的字段间距。

e.g。考虑到我们似乎在这个问题中讨论的输入格式,跳过前3个字段而不影响剩余字段之间的间距是简单的:

$ echo '1   2 3 4   5    6' |
awk '{sub(/([^ ]+ +){3}/,"")}1'
4   5    6

如果你想容纳前导空格和非空格,但是再次使用默认的FS,那么它是:

$ echo '  1   2 3 4   5    6' |
awk '{sub(/[[:space:]]*([^[:space:]]+[[:space:]]+){3}/,"")}1'
4   5    6

如果你的FS是一个RE,你不能在字符集中否定,你可以先将它转换为单个字符(RS是理想的,如果它是单个字符,因为RS不能出现在字段中,否则考虑SUBSEP),然后应用RE间隔替换,然后转换为OFS。例如如果“。”的链将这些字段分开:

$ echo '1...2.3.4...5....6' |
awk -F'[.]+' '{gsub(FS,RS);sub("([^"RS"]+["RS"]+){3}","");gsub(RS,OFS)}1'
4 5 6

显然,如果OFS是单个字符并且它不能出现在输入字段中,则可以将其减少为:

$ echo '1...2.3.4...5....6' |
awk -F'[.]+' '{gsub(FS,OFS); sub("([^"OFS"]+["OFS"]+){3}","")}1'
4 5 6

然后,您遇到与重新分配字段的所有基于循环的解决方案相同的问题 - 将FS转换为OFS。如果这是一个问题,你需要研究GNU awks的patsplit()函数。

答案 5 :(得分:10)

目前几乎所有答案都会添加前导空格,尾随空格或其他分隔符问题。要从第四个字段中选择分隔符为空格,输出分隔符是使用awk的单个空格,请选择:

awk '{for(i=4;i<=NF;i++)printf "%s",$i (i==NF?ORS:OFS)}' file

要参数化您可以执行的起始字段:

awk '{for(i=n;i<=NF;i++)printf "%s",$i (i==NF?ORS:OFS)}' n=4 file

还有结束字段:

awk '{for(i=n;i<=m=(m>NF?NF:m);i++)printf "%s",$i (i==m?ORS:OFS)}' n=4 m=10 file

答案 6 :(得分:6)

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

输入

1 2 3 4 5 6 7

输出

4 5 6 7

答案 7 :(得分:4)

echo 1 2 3 4 5| awk '{ for (i=3; i<=NF; i++) print $i }'

答案 8 :(得分:3)

避免使用print语句的另一种方法:

 $ awk '{$1=$2=$3=""}sub("^"FS"*","")' file

在条件为真时的awk中,print是默认操作。

答案 9 :(得分:3)

我无法相信没有人提供简单的外壳:

while read -r a b c d; do echo "$d"; done < file

答案 10 :(得分:3)

选项1到3存在多个空格的问题(但很简单)。 这就是开发选项4和5的原因,它可以毫无问题地处理多个空格。 当然,如果选项4或5与n=0一起使用,则两者都将保留任何前导空格,因为n=0表示不会拆分。

选项1

一个简单的剪切解决方案(适用于单个分隔符):

$ echo '1 2 3 4 5 6 7 8' | cut -d' ' -f4-
4 5 6 7 8

选项2

强制awk重新计算有时会解决添加前导空格的问题(适用于某些版本的awk):

$ echo '1 2 3 4 5 6 7 8' | awk '{ $1=$2=$3="";$0=$0;} NF=NF'
4 5 6 7 8

选项3

打印使用printf格式化的每个字段可以提供更多控制权:

$ echo '    1    2  3     4   5   6 7     8  ' |
  awk -v n=3 '{ for (i=n+1; i<=NF; i++){printf("%s%s",$i,i==NF?RS:OFS);} }'
4 5 6 7 8

但是,之前的所有答案都会将字段之间的所有FS更改为OFS。让我们为此构建一些解决方案。

选项4

带有sub以删除字段和分隔符的循环更具可移植性,并且不会触发将FS更改为OFS:

$ echo '    1    2  3     4   5   6 7     8  ' |
awk -v n=3 '{ for(i=1;i<=n;i++) { sub("^["FS"]*[^"FS"]+["FS"]+","",$0);} } 1 '
4   5   6 7     8

注意:“^ [”FS“] *”是接受带前导空格的输入。

选项5

很有可能构建一个不添加额外的前导或尾随空格的解决方案,并使用GNU awk中的函数gensub保留现有的空格,如下所示:

$ echo '    1    2  3     4   5   6 7     8  ' |
awk -v n=3 '{ print gensub("["FS"]*([^"FS"]+["FS"]+){"n"}","",1); }'
4   5   6 7     8 

它也可用于交换给定计数n的字段列表:

$ echo '    1    2  3     4   5   6 7     8  ' |
  awk -v n=3 '{ a=gensub("["FS"]*([^"FS"]+["FS"]+){"n"}","",1);
                b=gensub("^(.*)("a")","\\1",1);
                print "|"a"|","!"b"!";
               }'
|4   5   6 7     8  | !    1    2  3     !

当然,在这种情况下,OFS用于分隔线的两个部分,并且仍然打印字段的尾随空格。

注1: ["FS"]*用于允许输入行中的前导空格。

答案 11 :(得分:1)

不添加前导或尾随空格的Perl解决方案:

perl -lane 'splice @F,0,3; print join " ",@F' file

perl @F autosplit数组从索引0开始,而awk字段以$1开头

用于逗号分隔数据的Perl解决方案:

perl -F, -lane 'splice @F,0,3; print join ",",@F' file

Python解决方案:

python -c "import sys;[sys.stdout.write(' '.join(line.split()[3:]) + '\n') for line in sys.stdin]" < file

答案 12 :(得分:1)

Cut有一个--complement标志,可以轻松(快速)删除列。结果语法类似于您想要做的事情 - 使解决方案更易于阅读/理解。补充也适用于您要删除非连续列的情况。

$ foo='1 2 3 %s 5 6 7'
$ echo "$foo" | cut --complement -d' ' -f1-3
%s 5 6 7
$

答案 13 :(得分:0)

使用剪切:

cut -d <The character between characters> -f <number of first column>,<number of last column> <file name>

例如:如果您的file1包含:car.is.nice.equal.bmw

运行:cut -d . -f1,3 file1将打印car.is.nice

答案 14 :(得分:0)

我发现了另一种可能性,也许它也可能有用...

awk 'BEGIN {OFS=ORS="\t" }; {for(i=1; i<14; i++) print $i " "; print $NF "\n" }' your_file

注意: 1.对于表格数据,从$ 1列到$ 14列

答案 15 :(得分:0)

由于我对第一个高度赞成但错误的答案感到恼火,我发现在那里写了一个回复,这里错误的答案标记为这样,这是我的位。我不喜欢提出的解决方案,因为我认为没有理由让答案如此复杂。

我有一个日志,其中带有IP地址的5美元后可以是更多文本或没有文本。我需要从IP地址到行尾的所有内容,如果有5美元之后有任何东西。在我的情况下,这实际上是一个awk程序,而不是awk oneliner所以awk必须解决问题。当我尝试删除前4个字段时使用旧的漂亮且最受欢迎但完全错误的答案:

echo "  7 27.10.16. Thu 11:57:18 37.244.182.218 one two three" | awk '{$1=$2=$3=$4=""; printf "[%s]\n", $0}'

它吐出了错误和无用的反应(我添加了[]来证明):

[    37.244.182.218 one two three]

相反,如果列是固定宽度,直到需要切割点和awk,那么正确且非常简单的答案是:

echo "  7 27.10.16. Thu 11:57:18 37.244.182.218 one two three" | awk '{printf "[%s]\n", substr($0,28)}'

产生所需的输出:

[37.244.182.218 one two three]

答案 16 :(得分:0)

对我而言,最紧凑,最符合要求的解决方案是

$ a='1   2\t \t3     4   5   6 7 \t 8\t '; 
$ echo -e "$a" | awk -v n=3 '{while (i<n) {i++; sub($1 FS"*", "")}; print $0}'

如果您要处理更多行,例如文件 foo.txt ,请不要忘记将i重置为0:

$ awk -v n=3 '{i=0; while (i<n) {i++; sub($1 FS"*", "")}; print $0}' foo.txt

感谢您的论坛。

答案 17 :(得分:-1)

基于AWK printf的解决方案可以避免%问题,并且如果打印的列少于4列,则它不会返回任何内容(没有返回字符),这是唯一的:

awk 'NF > 3 { for(i=4; i<NF; i++) printf("%s ", $(i)); print $(i) }'

测试:

$ x='1 2 3 %s 4 5 6'
$ echo "$x" | awk 'NF > 3 { for(i=4; i<NF; i++) printf("%s ", $(i)); print $(i) }'
%s 4 5 6
$ x='1 2 3'
$ echo "$x" | awk 'NF > 3 { for(i=4; i<NF; i++) printf("%s ", $(i)); print $(i) }'
$ x='1 2 3 '
$ echo "$x" | awk 'NF > 3 { for(i=4; i<NF; i++) printf("%s ", $(i)); print $(i) }'
$

答案 18 :(得分:-1)

这与以前的一些答案相差甚远,但确实解决了几个问题:

cols.sh

#!/bin/bash
awk -v s=$1 '{for(i=s; i<=NF;i++) printf "%-5s", $i; print "" }'

现在可以使用将作为起始列的参数调用:

$ echo "1 2 3 4 5 6 7 8 9 10 11 12 13 14" | ./cols.sh 3 
3    4    5    6    7    8    9    10   11   12   13   14

或者:

$ echo "1 2 3 4 5 6 7 8 9 10 11 12 13 14" | ./cols.sh 7 
7    8    9    10   11   12   13   14

这是1索引;如果您希望零索引,请改用i=s + 1

此外,如果您希望获得起始索引结束索引的参数,请将文件更改为:

#!/bin/bash
awk -v s=$1 -v e=$2 '{for(i=s; i<=e;i++) printf "%-5s", $i; print "" }'

例如:

$ echo "1 2 3 4 5 6 7 8 9 10 11 12 13 14" | ./cols.sh 7 9 
7    8    9

%-5s将结果与5个字符宽的列对齐;如果这还不够,请增加数字,或使用%s(带空格),如果您不关心对齐。