使用 awk 打印从第 n 列到最后一列的所有列
- 2024-10-10 09:29:00
- admin 原创
- 116
问题描述:
这一行一直有效,直到第二个字段出现空格:
svn status | grep '!' | gawk '{print $2;}' > removedProjs
有没有办法awk
打印所有内容$2
或更大内容?($3
,$4
..直到我们没有更多的列?)
我在 Windows 环境中使用 Cygwin 执行此操作。
解决方案 1:
打印所有列:
awk '{print $0}' somefile
打印除第一列之外的所有列:
awk '{$1=""; print $0}' somefile
打印除前两列之外的所有列:
awk '{$1=$2=""; print $0}' somefile
解决方案 2:
有一个重复的问题,使用 cut可以得到更简单的答案:
svn status | grep '!' | cut -d -f2-
-d
指定分隔符(空格),-f
指定列的列表(所有列都从第二列开始)
解决方案 3:
您可以使用 for 循环来循环打印字段 $2 到 $NF(表示行上字段数的内置变量)。
编辑:由于“print”附加了一个换行符,因此您需要缓冲结果:
awk '{out = ""; for (i = 2; i <= NF; i++) {out = out " " $i}; print out}'
或者,使用 printf:
awk '{for (i = 2; i <= NF; i++) {printf "%s ", $i}; printf "
"}'
解决方案 4:
awk '{out=$2; for(i=3;i<=NF;i++){out=out" "$i}; print out}'
我的答案基于VeeArr 的答案,但我注意到它以空格开头,然后才会打印第二列(以及其余部分)。由于我只有 1 个声誉点,我无法对此发表评论,因此这里将其作为新答案:
从“out”作为第二列开始,然后添加所有其他列(如果存在)。只要有第二列,就可以顺利完成。
解决方案 5:
大多数使用 awk 的解决方案都会留下空格。此处的选项可避免该问题。
选项 1
一个简单的cut
解决方案(仅适用于单个分隔符):
command | cut -d' ' -f3-
选项 2
强制 awk 重新计算有时会删除通过删除第一个字段而留下的添加的前导空格 (OFS)(适用于某些版本的 awk):
command | awk '{ $1=$2="";$0=$0;} NF=NF'
选项 3
打印每个字段的格式printf
将提供更多的控制:
$ in=' 1 2 3 4 5 6 7 8 '
$ echo "$in"|awk -v n=2 '{ for(i=n+1;i<=NF;i++) printf("%s%s",$i,i==NF?RS:OFS);}'
3 4 5 6 7 8
但是,所有先前的答案都将字段之间所有重复的 FS 更改为 OFS。让我们构建几个不这样做的选项。
选项 4(推荐)
带有子循环以删除前面的字段和分隔符。
并使用 FS 的值而不是空间(可以改变)。
这更具可移植性,并且不会触发 FS 到 OFS 的改变:
注意:接受^[FS]*
带有前导空格的输入。
$ in=' 1 2 3 4 5 6 7 8 '
$ echo "$in" | awk '{ n=2; a="^["FS"]*[^"FS"]+["FS"]+";
for(i=1;i<=n;i++) sub( a , "" , $0 ) } 1 '
3 4 5 6 7 8
选项 5
完全有可能构建一个不添加额外(前导或尾随)空格并使用gensub
GNU awk 中的函数保留现有空格的解决方案,如下所示:
$ echo ' 1 2 3 4 5 6 7 8 ' |
awk -v n=2 'BEGIN{ a="^["FS"]*"; b="([^"FS"]+["FS"]+)"; c="{"n"}"; }
{ print(gensub(a""b""c,"",1)); }'
3 4 5 6 7 8
它还可以用于交换给定计数的一组字段n
:
$ echo ' 1 2 3 4 5 6 7 8 ' |
awk -v n=2 'BEGIN{ a="^["FS"]*"; b="([^"FS"]+["FS"]+)"; c="{"n"}"; }
{
d=gensub(a""b""c,"",1);
e=gensub("^(.*)"d,"\1",1,$0);
print("|"d"|","!"e"!");
}'
|3 4 5 6 7 8 | ! 1 2 !
当然,在这种情况下,OFS 用于分隔行的两个部分,并且仍然打印字段的尾随空格。
注意: [FS]*
用于允许在输入行中有前导空格。
解决方案 6:
我亲自尝试了上述所有答案,但大多数答案都有点复杂或不正确。从我的角度来看,最简单的方法是:
awk -F" " '{ for (i=4; i<=NF; i++) print $i }'
其中 -F" " 定义 awk 要使用的分隔符。在我的例子中是空格,这也是 awk 的默认分隔符。这意味着可以忽略 -F" "。
其中 NF 定义字段/列的总数。因此循环将从第 4 个字段开始直到最后一个字段/列。
其中 $N 检索第 N 个字段的值。因此 print $i 将根据循环计数打印当前字段/列。
解决方案 7:
awk '{ for(i=3; i<=NF; ++i) printf $i""FS; print "" }'
lauhub在这里提出了这个正确、简单、快速的解决方案
解决方案 8:
这让我非常恼火,我坐下来编写了一个cut
类似的字段规范解析器,并使用 GNU Awk 3.1.7 进行了测试。
首先,创建一个名为 的新 Awk 库脚本pfcut
,例如
sudo nano /usr/share/awk/pfcut
然后,粘贴下面的脚本并保存。之后,用法如下:
$ echo "t1 t2 t3 t4 t5 t6 t7" | awk -f pfcut --source '/^/ { pfcut("-4"); }'
t1 t2 t3 t4
$ echo "t1 t2 t3 t4 t5 t6 t7" | awk -f pfcut --source '/^/ { pfcut("2-"); }'
t2 t3 t4 t5 t6 t7
$ echo "t1 t2 t3 t4 t5 t6 t7" | awk -f pfcut --source '/^/ { pfcut("-2,4,6-"); }'
t1 t2 t4 t6 t7
为了避免输入所有这些内容,我猜最好的办法是(另请参阅使用 awk 在启动时自动加载用户功能? - Unix 和 Linux Stack Exchange)为 ; 添加一个别名~/.bashrc
,例如:
$ echo "alias awk-pfcut='awk -f pfcut --source'" >> ~/.bashrc
$ source ~/.bashrc # refresh bash aliases
...然后你就可以调用:
$ echo "t1 t2 t3 t4 t5 t6 t7" | awk-pfcut '/^/ { pfcut("-2,4,6-"); }'
t1 t2 t4 t6 t7
以下是脚本的来源pfcut
:
# pfcut - print fields like cut
#
# sdaau, GNU GPL
# Nov, 2013
function spfcut(formatstring)
{
# parse format string
numsplitscomma = split(formatstring, fsa, ",");
numspecparts = 0;
split("", parts); # clear/initialize array (for e.g. `tail` piping into `awk`)
for(i=1;i<=numsplitscomma;i++) {
commapart=fsa[i];
numsplitsminus = split(fsa[i], cpa, "-");
# assume here a range is always just two parts: "a-b"
# also assume user has already sorted the ranges
#print numsplitsminus, cpa[1], cpa[2]; # debug
if(numsplitsminus==2) {
if ((cpa[1]) == "") cpa[1] = 1;
if ((cpa[2]) == "") cpa[2] = NF;
for(j=cpa[1];j<=cpa[2];j++) {
parts[numspecparts++] = j;
}
} else parts[numspecparts++] = commapart;
}
n=asort(parts); outs="";
for(i=1;i<=n;i++) {
outs = outs sprintf("%s%s", $parts[i], (i==n)?"":OFS);
#print(i, parts[i]); # debug
}
return outs;
}
function pfcut(formatstring) {
print spfcut(formatstring);
}
解决方案 9:
这有用吗?
awk '{print substr($0,length($1)+1);}' < file
不过它在前面留下了一些空白。
解决方案 10:
打印出从 #2 开始的列(输出开头没有尾随空格):
ls -l | awk '{sub(/[^ ]+ /, ""); print $0}'
解决方案 11:
echo "1 2 3 4 5 6" | awk '{ $NF = ""; print $0}'
这个使用 awk 打印除最后一个字段之外的所有内容
解决方案 12:
在所有的建议中,我最喜欢以下这条:
从第 6 列到最后一列打印。
ls -lthr | awk '{out=$6; for(i=7;i<=NF;i++){out=out" "$i}; print out}'
或者
ls -lthr | awk '{ORS=" "; for(i=6;i<=NF;i++) print $i;print "
"}'
解决方案 13:
此处和链接问题中给出的所有其他答案在给定各种可能的 FS 值的情况下都会以各种方式失败。有些会留下前导和/或尾随空格,有些会将每个 FS 转换为 OFS,有些依赖于仅在 FS 为默认值时适用的语义,有些依赖于括号表达式中的否定 FS,这在给定多字符 FS 时会失败,等等。
为了对任何 FS 稳健地执行此操作,请使用 GNU awk 作为 split() 的第 4 个参数:
$ cat tst.awk
{
split($0,flds,FS,seps)
for ( i=n; i<=NF; i++ ) {
printf "%s%s", flds[i], seps[i]
}
print ""
}
$ printf 'a b c d
' | awk -v n=3 -f tst.awk
c d
$ printf ' a b c d
' | awk -v n=3 -f tst.awk
c d
$ printf ' a b c d
' | awk -v n=3 -F'[ ]' -f tst.awk
b c d
$ printf ' a b c d
' | awk -v n=3 -F'[ ]+' -f tst.awk
b c d
$ printf 'a###b###c###d
' | awk -v n=3 -F'###' -f tst.awk
c###d
$ printf '###a###b###c###d
' | awk -v n=3 -F'###' -f tst.awk
b###c###d
请注意,我在上面使用了 split(),因为它的第三个参数是字段分隔符,而不仅仅是像 match() 的第二个参数那样的正则表达式。不同之处在于,字段分隔符比正则表达式具有额外的语义,例如,当分隔符是单个空白字符时,跳过前导和/或尾随空白 - 如果您想使用 while(match()) 循环或任何形式的 *sub() 来模拟上述内容,那么您需要编写代码来实现这些语义,而 split() 已经为您实现了它们。
解决方案 14:
如果需要使用任意分隔符打印特定列:
awk '{print $3 " " $4}'
列#3 列#4
awk '{print $3 "anything" $4}'
col#3任何东西col#4
因此,如果某一列中有空格,则它将分为两列,但您可以使用任何分隔符将其连接起来,也可以不使用分隔符。
解决方案 15:
Perl 解决方案:
perl -lane 'splice @F,0,1; print join " ",@F' file
这些命令行选项用于:
-n
循环输入文件的每一行,不自动打印每一行-l
在处理之前删除换行符,然后将其添加回来-a
自动分割模式 – 将输入行分割到 @F 数组中。默认按空格分割-e
执行 perl 代码
splice @F,0,1
从 @F 数组中彻底删除第 0 列
join " ",@F
连接 @F 数组的元素,每个元素之间使用空格
Python解决方案:
`python -c "import sys;[sys.stdout.write(' '.join(line.split()[1:]) + '
') for line in sys.stdin]" < file`
解决方案 16:
我想将提议的答案扩展到字段可能由几个空格分隔的情况——我想这就是 OP 不使用它的原因cut
。
我知道 OP 询问的是awk
,但sed
这里有一种方法可行(例如打印从第 5 列到最后一列):
纯 sed 方法
sed -r 's/^s*(S+s+){4}//' somefile
解释:
+ `s///`是执行替换的标准命令
+ `^s*`匹配行首的任何连续空格
+ `S+s+`表示一列数据(非空白字符后跟空白字符)
+ `(){4}`表示该模式重复 4 次。
sed 和 cut
sed -r 's/^s+//; s/s+/ /g' somefile | cut -f5-
只需用一个制表符替换连续的空格;
tr 和 cut:
tr
也可以用该选项来挤压连续的-s
字符。
tr -s [:blank:] <somefile | cut -d' ' -f5-
解决方案 17:
如果您不想重新格式化未截断的行部分,我能想到的最好的解决方案写在我的答案中:
如何使用 awk 打印特定数字后的所有列?
它会截断给定字段号 N 之前的内容,并打印行的其余部分,包括字段号 N 并保持原始间距(它不会重新格式化)。如果字段的字符串也出现在行中的其他地方,则无关紧要。
定义一个函数:
fromField () {
awk -v m="" -v N="$1" '{$N=m$N; print substr($0,index($0,m)+1)}'
}
像这样使用它:
$ echo " bat bi iru lau bost " | fromField 3
iru lau bost
$ echo " bat bi iru lau bost " | fromField 2
bi iru lau bost
输出保留所有内容,包括尾随空格
在您的特定情况下:
svn status | grep '!' | fromField 2 > removedProjs
如果您的文件/流在行中间不包含换行符(您可能使用了不同的记录分隔符),那么您可以使用:
awk -v m="
" -v N="3" '{$N=m$N ;print substr($0, index($0,m)+1)}'
第一种情况仅在包含罕见的十六进制字符数字 1 的文件/流中失败
解决方案 18:
此awk
函数返回$0
包含从begin
到字段的子字符串end
:
function fields(begin, end, b, e, p, i) {
b = 0; e = 0; p = 0;
for (i = 1; i <= NF; ++i) {
if (begin == i) { b = p; }
p += length($i);
e = p;
if (end == i) { break; }
p += length(FS);
}
return substr($0, b + 1, e - b);
}
要获取从字段 3 开始的所有内容:
tail = fields(3);
要获取$0
涵盖字段 3 至 5 的部分:
middle = fields(3, 5);
b, e, p, i
函数参数列表中的废话只是awk
声明局部变量的一种方式。
解决方案 19:
这里的 Awk 示例看起来很复杂,这里是简单的 Bash shell 语法:
command | while read -a cols; do echo ${cols[@]:1}; done
1
你的第n列从 0 开始数在哪里?
例子
鉴于文件()的内容in.txt
:
c1
c1 c2
c1 c2 c3
c1 c2 c3 c4
c1 c2 c3 c4 c5
输出如下:
$ while read -a cols; do echo ${cols[@]:1}; done < in.txt
c2
c2 c3
c2 c3 c4
c2 c3 c4 c5
解决方案 20:
如果您使用 Bash,那么这将有效,并且您可以使用任意数量的“x”作为您想要丢弃的元素,并且如果未转义,它会忽略多个空格。
while read x b; do echo "$b"; done < filename
解决方案 21:
Perl的:
@m=`ls -ltr dir | grep ^d | awk '{print $6,$7,$8,$9}'`;
foreach $i (@m)
{
print "$i
";
}
解决方案 22:
更新 :
如果您不想使用任何函数调用,同时保留剩余字段之间的空格和制表符,那么请执行以下操作:
echo " 1 2 33 4444 555555 6666666 " |
{m,g}awk ++NF FS='^[ ]*[^ ]*[ ]+|[ ]+$' OFS=
=
2 33 4444 555555 6666666
===================
你可以让它变得更加直接:
svn status | [m/g]awk '/!/*sub("^[^ ]*[ ]+",_)'
svn status | [n]awk '(/!/)*sub("^[^ ]*[ ]+",_)'
自动处理grep
管道中的早期内容,以及FS
在消隐后修剪掉多余的$1
内容,另外还有一个好处是保留原始输入的其余部分不变,而不是用空格覆盖制表符(除非这是所需的效果)
如果您非常确定$1
不包含需要正则表达式转义的特殊字符,那么就更容易了:
mawk '/!/*sub($!_"[ ]+",_)'
gawk -c/P/e '/!/*sub($!_"""[ ]+",_)'
或者如果您喜欢定制FS+OFS
来处理所有事情:
mawk 'NF*=/!/' FS='^[^ ]*[ ]+' OFS='' # this version uses OFS
解决方案 23:
这应该是一个相当全面的awk
字段子字符串提取函数,
$0
根据输入范围返回子字符串(包括)限制超出范围的值,
处理可变长度字段
SEPs
有加速治疗::
完全不需要输入,
$0
直接返回输入值保证为空字符串
("")
FROM-field == 1
FS = ""
已$0
按单个字符分离出来
(因此FROM <(_)>
和TO <(__)>
字段的行为类似于cut -c
而不是cut -f
)
恢复原始版本
$0
,无需覆盖FS
sepsOFS
|
{m,g}awk '{
2 print "
|---BEFORE-------------------------
"
3 ($0) "
|----------------------------
["
4 fld2(2, 5) "]
[" fld2(3) "]
[" fld2(4, 2)
5 "]<----------------------------------------------should be
6 empty
[" fld2(3, 11) "]<------------------------should be
7 capped by NF
[" fld2() "]
[" fld2((OFS=FS="")*($0=$0)+11,
8 23) "]<-------------------FS=\"\", split by chars
9
|---AFTER-------------------------
" ($0)
10 "
|----------------------------"
11 }
12 function fld2(_,__,___,____,_____)
13 {
if (+__==(_=-_<+_ ?+_:_<_) || (___=____="")==__ || !NF) {
return $_
16 } else if (NF<_ || (__=NF<+__?NF:+__)<(_=+_?_:!_)) {
return ___
18 } else if (___==FS || _==!___) {
19 return ___<FS \n ? substr("",$!_=$!_ substr("",__=$!(NF=__)))__
20 : substr($(_<_),_,__)
21 }
22 _____=$+(____=___=""\n "")
23 NF=__
24 if ($(!_)~("["(___)"]")) {
25 gsub("..","\&&",___) + gsub(".",___,____)
27 ___=____
28 }
29 __=(_) substr("",_+=_^=_<_)
30 while(___!="") {
31 if ($(!_)!~(____=substr(___,--_,++_))) {
32 ___=____
33 break }
35 ___=substr(___,_+_^(!_))
36 }
37 return \n substr("",($__=___ $__)==(__=substr($!_,
_+index($!_,___))),_*($!_=_____))(__)
}'
这些 都是真实的,但为了显示清晰而重新贴了标签<TAB>
**
|---BEFORE-------------------------
1 2 33 4444 555555 <TAB>6666666
|----------------------------
[2 33 4444 555555]
[33]
[]<---------------------------------------------- should be empty
[33 4444 555555 6666666]<------------------------ should be capped by NF
[ 1 2 33 4444 555555 <TAB>6666666 ]
[ 2 33 4444 555555 <TAB>66]<------------------- FS="", split by chars
|---AFTER-------------------------
1 2 33 4444 555555 <TAB>6666666
|----------------------------
解决方案 24:
我对这里介绍的任何解决方案都不满意awk
,因为我想提取前几列,然后打印其余部分,所以我转而使用perl
。以下代码提取前两列,并按原样显示其余部分:
echo -e "a b c d e f g" | \n perl -ne 'my @f = split /s+/, $_, 3; printf "first: %s second: %s rest: %s", @f;'
与Chris Koknatperl
的解决方案相比,其优势在于实际上只有前 n 个元素从输入字符串中分离出来;其余字符串根本没有被分离,因此保持完整。我的示例通过混合使用空格和制表符来演示这一点。
要更改应提取的列数,请将3
示例中的替换为 n+1。
解决方案 25:
ls -la | awk '{o=$1" "$3; for (i=5; i<=NF; i++) o=o" "$i; print o }'
这个答案还不错,但是自然间距消失了。
请将其与以下答案进行比较:
ls -la | cut -d -f4-
然后你就会看到差异。
即使ls -la | awk '{$1=$2=""; print}'
是基于迄今为止投票最佳的答案,也无法保留格式。
因此我将使用以下内容,并且它还允许在开始时明确选择列:
ls -la | cut -d -f1,4-
请注意,每个空格也算作列,例如在下面的示例中,第 1 列和第 3 列为空,第 2 列为信息,第 4 列为:
$ echo " INFO 2014-10-11 10:16:19 main " | cut -d -f1,3
$ echo " INFO 2014-10-11 10:16:19 main " | cut -d -f2,4
INFO 2014-10-11
$
解决方案 26:
如果您想要格式化的文本,请使用 echo 链接您的命令并使用 $0 打印最后一个字段。
例子:
for i in {8..11}; do
s1="$i"
s2="str$i"
s3="str with spaces $i"
echo -n "$s1 $s2" | awk '{printf "|%3d|%6s",$1,$2}'
echo -en "$s3" | awk '{printf "|%-19s|
", $0}'
done
印刷:
| 8| str8|str with spaces 8 |
| 9| str9|str with spaces 9 |
| 10| str10|str with spaces 10 |
| 11| str11|str with spaces 11 |
解决方案 27:
__=' 1 2 3 4 5 6 7 8 '
printf '%s' "$__" | od
0000000 538976288 538976305 538980896 538976307
1 2 3
040 040 040 040 061 040 040 040 040 062 040 040 063 040 040 040
sp sp sp sp 1 sp sp sp sp 2 sp sp 3 sp sp sp
32 32 32 32 49 32 32 32 32 50 32 32 51 32 32 32
20 20 20 20 31 20 20 20 20 32 20 20 33 20 20 20
0000020 540287008 540352544 540418080 538976311
4 5 6 7
040 040 064 040 040 040 065 040 040 040 066 040 067 040 040 040
sp sp 4 sp sp sp 5 sp sp sp 6 sp 7 sp sp sp
32 32 52 32 32 32 53 32 32 32 54 32 55 32 32 32
20 20 34 20 20 20 35 20 20 20 36 20 37 20 20 20
0000040 540549152 32
8
040 040 070 040 040
sp sp 8 sp sp
32 32 56 32 32
20 20 38 20 20
printf '"%s"' "$__"
" 1 2 3 4 5 6 7 8 "
mawk ++NF FS='^[ ]*[^ ]+[ ]+' OFS='"'
"2 3 4 5 6 7 8 "
这种方法通过专门针对头部来保留字段之间的所有多空白分隔符。
解决方案 28:
zed_0xff 给出的最高投票答案对我来说不起作用。
我有一个日志,其中带有 IP 地址的 $5 后面可以是更多文本或没有文本。如果 $5 后面有任何内容,我需要从 IP 地址到行尾的所有内容。在我的例子中,这实际上是在 awk 程序中,而不是 awk 单行程序中,因此 awk 必须解决问题。当我尝试使用 zed_0xff 提出的解决方案删除前 4 个字段时:
echo " 7 27.10.16. Thu 11:57:18 37.244.182.218" | awk '{$1=$2=$3=$4=""; printf "[%s]
", $0}'
它输出错误且无用的响应(我添加了 [...] 来演示):
[ 37.244.182.218 one two three]
甚至有人建议将 substr 与这个错误答案结合起来,但这只会让事情变得更加复杂。它没有任何改进作用。
相反,如果列宽直到截止点都是固定的并且需要 awk,那么正确的答案是:
echo " 7 27.10.16. Thu 11:57:18 37.244.182.218" | awk '{printf "[%s]
", substr($0,28)}'
产生所需的输出:
[37.244.182.218 one two three]
- 2024年20款好用的项目管理软件推荐,项目管理提效的20个工具和技巧
- 2024年开源项目管理软件有哪些?推荐5款好用的项目管理工具
- 项目管理软件有哪些?推荐7款超好用的项目管理工具
- 项目管理软件哪个最好用?盘点推荐5款好用的项目管理工具
- 项目管理软件有哪些最好用?推荐6款好用的项目管理工具
- 项目管理软件有哪些,盘点推荐国内外超好用的7款项目管理工具
- 2024项目管理软件排行榜(10类常用的项目管理工具全推荐)
- 项目管理软件排行榜:2024年项目经理必备5款开源项目管理软件汇总
- 2024年常用的项目管理软件有哪些?推荐这10款国内外好用的项目管理工具
- 项目管理必备:盘点2024年13款好用的项目管理软件