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

太麻烦:

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

试试这个:

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

使用削减

$ cut -f4-13 file

或者如果你坚持13美元是最后一场

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

else

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

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

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

当条件为真时,在 awk 中打印是默认操作。

一个不增加额外前置或后置 空格的解决方案:

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 使用三元运算符 < strong > 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月之前的所有其他答案都不错,但要加上额外的空格:

目前几乎所有的答案都添加了前导空格、尾随空格或其他分隔符问题。要从第四个字段中选择,其中分隔符是空格,而输出分隔符是使用 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

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

例如,跳过前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

如果你有一个在字符集中不能被否定的 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

然后你会遇到和所有重新分配字段的基于循环的解决方案一样的问题—— FSS 被转换成 OFS。如果这是一个问题,那么您需要查看 GNU awks 的 patsplit ()函数。

这与之前的一些答案相差不大,但确实解决了一些问题:

返回文章页面

#!/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

The %-5s aligns the result as 5-character-wide columns; if this isn't enough, increase the number, or use %s (with a space) instead if you don't care about alignment.

基于 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) }'
$

I can't believe nobody offered plain shell:

while read -r a b c d; do echo "$d"; done < file
awk '{$1=$2=$3="";$0=$0;$1=$1}1'

输入

1 2 3 4 5 6 7

输出

4 5 6 7

Perl 解决方案,该解决方案不添加前导空格或后跟空格:

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

Perl@F自动分割数组从索引 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

选项1到3有多个空格的问题(但是很简单)。 That is the reason to develop options 4 and 5, which process multiple white spaces with no problem. 当然,如果选项4或5与 n=0一起使用,两者都将保留任何前导空格,因为 n=0意味着没有分裂。

Option 1

一个简单的解决方案(使用单分隔符) :

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

选择二

强制 awk re-calc 有时可以解决添加前导空格的问题(适用于某些版本的 awk) :

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

选择三

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。

选择四

使用 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”] *”是接受带有前导空格的输入。

选择五

构建一个不添加额外的前导空格或尾随空格的解决方案并使用 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 用于分隔行的两个部分,字段的尾部空白仍然被打印出来。

Note1: ["FS"]* is used to allow leading spaces in the input line.

Cut 有一个—— complete 标志,这使得删除列变得容易(且快速)。结果语法与您想要做的类似——使解决方案更容易阅读/理解。对于要删除非连续列的情况,也可以使用补语。

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

For me the most compact and compliant solution to the request is

$ 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

Thanks your forum.

由于我对第一个高度赞成但错误的答案感到恼火,我找到了足够的答案在那里写一个回复,这里错误的答案被标记为这样,这里是我的位。我不喜欢提出的解决方案,因为我看不出有什么理由使答案如此复杂。

I have a log where after $5 with an IP address can be more text or no text. I need everything from the IP address to the end of the line should there be anything after $5. In my case, this is actualy withn an awk program, not an awk oneliner so awk must solve the problem. When I try to remove the first 4 fields using the old nice looking and most upvoted but completely wrong answer:

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]

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

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

Note: 1. For tabular data and from column $1 to $14

使用削减:

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

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

Run : cut -d . -f1,3 file1 will print car.is.nice