awk打印某列之后的所有列
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了awk打印某列之后的所有列相关的知识,希望对你有一定的参考价值。
参考技术A 这里以ps -aux的结果为例,打印运行程序的命令全路径。命令全路径在ps -aux结果的第10行以后,从第11行开始。
如下是ps -aux 的输出结果
我的目的是截取后面的命令全路径
也就是需要打印第11列到最后一行。
ps -aux |sort -rn -k4 |head -10 : 输出内存占用排名前十的进程
for(i=11;i<=NF;i++)printf("%s ",$i) :打印11列以后的结果
print “” 打印组合
使用 awk 打印从第 n 到最后的所有列
【中文标题】使用 awk 打印从第 n 到最后的所有列【英文标题】:Using awk to print all columns from the nth to the last 【发布时间】:2011-02-27 01:05:46 【问题描述】:这条线一直有效,直到我在第二个字段中有空格。
svn status | grep '\!' | gawk 'print $2;' > removedProjs
有没有办法让 awk 以 2 美元或更高的价格打印所有内容? ($3, $4.. 直到我们不再有列?)
我想我应该补充一点,我是在带有 Cygwin 的 Windows 环境中执行此操作的。
【问题讨论】:
顺便说一句,grep | awk
is an antipattern -- 你想要awk '/!/ print $2 '
Unix“剪切”更容易...svn status | grep '\!' | cut -d' ' -f2- > removedProjs
print rest of the fields in awk的可能重复
@tripleee:我很高兴你提到了这一点 - 我很沮丧地看到它无处不在!
【参考方案1】:
投票最多的answer by zed_0xff 对我不起作用。
我有一个日志,其中 5 美元之后的 IP 地址可以是更多文本或没有文本。如果 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]\n", $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]\n", substr($0,28)'
产生所需的输出:
[37.244.182.218 one two three]
【讨论】:
【参考方案2】:我想将建议的答案扩展到字段可能由 几个空格 分隔的情况——我想这就是 OP 不使用 cut
的原因。
我知道 OP 询问了 awk
,但 sed
方法可以在这里工作(例如打印从第 5 列到最后的列):
纯 sed 方法
sed -r 's/^\s*(\S+\s+)4//' somefile
解释:
s///
是执行替换的标准命令
^\s*
匹配行首的任何连续空格
\S+\s+
表示一列数据(非空白字符后跟空白字符)
()4
表示模式重复 4 次。
sed 和剪切
sed -r 's/^\s+//; s/\s+/\t/g' somefile | cut -f5-
只需用一个制表符替换连续的空格;
tr 和剪切:
tr
也可以通过-s
选项用于squeeze consecutive 字符。
tr -s [:blank:] <somefile | cut -d' ' -f5-
【讨论】:
我同意sed
最适合这个问题。注意:您提供的cut
示例不会在您尝试提取的部分中保留连续的空格。考虑这个输入:a b c d The rest
。如果您只保留纯 sed 方法,您的答案会更好。也可以使用-E
而不是-r
以实现可移植性。此外,由于 \s
是 GNU 扩展,请将 \s
替换为 [ \t]
并将 \S
替换为 [^ \t]
。【参考方案3】:
打印所有列:
awk 'print $0' somefile
打印除第一列以外的所有内容:
awk '$1=""; print $0' somefile
打印除前两列之外的所有列:
awk '$1=$2=""; print $0' somefile
【讨论】:
陷阱:留下一个前导空格悬空:( @raphinesse 你可以用awk '$1=""; print substr($0,2)' input_filename > output_filename
修复它
这不适用于非空白分隔符,用空格替换它们。
对于非空白分隔符,您可以指定输出字段分隔符 (OFS),例如到逗号:awk -F, -vOFS=, '$1=""; print $0'
你最终会得到一个初始分隔符($1
仍然包括在内,就像一个空字符串一样)。你可以用sed
去掉它:awk -F, -vOFS=, '$1=""; print $0' | sed 's/^,//'
AWK 就像是实现三个愿望的过于文字的精灵【参考方案4】:
awk ' for(i=3; i<=NF; ++i) printf $i""FS; print "" '
lauhub提出了这个正确、简单、快速的解决方案here
【讨论】:
【参考方案5】:此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);
要获取覆盖字段 3 到 5 的 $0
部分:
middle = fields(3, 5);
函数参数列表中的b, e, p, i
废话只是awk
声明局部变量的方式。
【讨论】:
【参考方案6】:Perl:
@m=`ls -ltr dir | grep ^d | awk 'print \$6,\$7,\$8,\$9'`;
foreach $i (@m)
print "$i\n";
【讨论】:
这没有回答问题,它概括了从第 N 列打印到末尾的要求。【参考方案7】:如果您不想重新格式化您不切断的那部分行,我能想到的最佳解决方案写在我的答案中:
How to print all the columns after a particular number using awk?
它将给定字段编号 N 之前的内容切掉,并打印该行的所有其余部分,包括字段编号 N 并保持原始间距(它不会重新格式化)。字段的字符串是否也出现在该行的其他位置并不重要。
定义一个函数:
fromField ()
awk -v m="\x01" -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="\x0a" -v N="3" '$N=m$N ;print substr($0, index($0,m)+1)'
第一种情况只会在包含罕见的十六进制字符数 1 的文件/流中失败
【讨论】:
【参考方案8】:如果您使用 Bash 并且您可以使用尽可能多的 'x ' 作为您希望丢弃的元素并且如果它们没有被转义,它会忽略多个空格。
while read x b; do echo "$b"; done < filename
【讨论】:
【参考方案9】:如果您想要格式化文本,请使用 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|\n", $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 |
【讨论】:
【参考方案10】:ls -la | awk 'o=$1" "$3; for (i=5; i<=NF; i++) o=o" "$i; print o '
来自this answer 还不错,但自然间距消失了。 然后请与此比较:
ls -la | cut -d\ -f4-
然后你会看到不同之处。
即使基于the answer 的ls -la | awk '$1=$2=""; print'
迄今为止被评为最佳,也不会保留格式。
因此我将使用以下内容,并且它还允许在开头显式选择列:
ls -la | cut -d\ -f1,4-
请注意,每个空格也算作列,因此例如在下面,第 1 列和第 3 列为空,第 2 列为 INFO,第 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
$
【讨论】:
【参考方案11】:这让我非常恼火,我坐下来写了一个类似cut
的字段规范解析器,用 GNU Awk 3.1.7 进行了测试。
首先,创建一个名为 pfcut
的新 Awk 库脚本,例如
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
为了避免输入所有内容,我想最好的方法是(另见Automatically load a user function at startup with 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);
【讨论】:
好像你想使用cut
,而不是awk
@roblogic : unix cut 非常适合像几兆这样的小任务。也许低数百 MB 可能是切入点对于卷来说确实太慢了,而 awk 真正闪耀的地方。【参考方案12】:
我对这里提供的任何awk
解决方案都不满意,因为我想提取前几列然后打印其余的列,所以我转而使用perl
。以下代码提取前两列,并按原样显示其余列:
echo -e "a b c d\te\t\tf g" | \
perl -ne 'my @f = split /\s+/, $_, 3; printf "first: %s second: %s rest: %s", @f;'
与Chris Koknat 的perl
解决方案相比,优势在于实际上只有前n 个元素从输入字符串中分离出来;字符串的其余部分根本没有分开,因此保持完整。我的示例通过混合使用空格和制表符来演示这一点。
要更改应提取的列数,请将示例中的 3
替换为 n+1。
【讨论】:
【参考方案13】:这里的 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
【讨论】:
【参考方案14】:大多数带有 awk 的解决方案都会留下空格。这里的选项避免了这个问题。
选项 1
一个简单的剪切解决方案(仅适用于单个分隔符):
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(推荐)
带有 sub 的循环用于删除前面的字段和分隔符。
并使用 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
很可能构建一个不添加额外(前导或尾随)空格的解决方案,并使用来自 GNU awk 的函数 gensub
保留现有空格,如下所示:
$ 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]*
用于在输入行中允许前导空格。
【讨论】:
【参考方案15】:有一个重复的问题,simpler answer 使用 cut:
svn status | grep '\!' | cut -d\ -f2-
-d
指定分隔符(空格),-f
指定列列表(都从第2个开始)
【讨论】:
也可以使用“-b”指定位置(从第N个字符开始)。 请注意,虽然它执行与awk
版本相同的任务,但cut
存在行缓冲问题,awk
没有:***.com/questions/14360640/…
很好很简单,但有一个警告:awk
处理多个相邻的空格字符。作为 single 分隔符,而 cut
没有;另外——尽管这在手头的情况下不是问题——cut
只接受一个单一的文字字符。作为分隔符,而 awk
允许使用正则表达式。
基于此:***.com/a/39217130/8852408,很可能这个解决方案效率不高。
@Joaquin 我赞成您的评论,但随后在 120MB 的日志文件上运行了一些快速、非科学的基准测试:(time cut -d\ -f2- logfile.txt > /dev/null
与 time awk '$1=""; print $0' logfile.txt > /dev/null
)。 cut
命令(没有任何 grep
)始终比 awk
等效命令快(cut
的平均时间是 awk
命令的 70%)。看起来cut
在“寻找”文件以到达某一行时速度较慢——但一次处理每一行的效率很高。【参考方案16】:
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:]) + '\n') for line in sys.stdin]" < file
【讨论】:
【参考方案17】:您可以使用 for 循环循环打印字段 $2 到 $NF(表示行中字段数的内置变量)。
编辑: 由于“打印”附加了一个换行符,因此您需要缓冲结果:
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 "\n"'
【讨论】:
所以我尝试了这个,但认为我遗漏了一些东西.. 这是我所做的 svn status | grep '\!' | gawk 'for (i=1; i removedProjs 由于 print 附加了一个换行符,因此您需要缓冲结果。查看我的编辑。 我更喜欢这个答案,因为它展示了如何遍历字段。 如果要打印使用空格,更改输出记录分隔符: awk 'ORS=" "; for(i=2;i'for(i=11;i<=NF-1;i++)printf "%s ", $i; print $NF;'
没有前导或尾随空格。【参考方案18】:
如果您需要使用任意分隔符打印特定列:
awk 'print $3 " " $4'
col#3 col#4
awk 'print $3 "anything" $4'
col#3anythingcol#4
因此,如果您在一列中有空格,它将是两列,但您可以使用任何分隔符连接它,也可以不使用它。
【讨论】:
【参考方案19】:这行得通吗?
awk 'print substr($0,length($1)+1);' < file
它在前面留下了一些空白。
【讨论】:
【参考方案20】:我亲自尝试了上面提到的所有答案,但其中大多数都有些复杂或不正确。从我的角度来看,最简单的方法是:
awk -F" " ' for (i=4; i<=NF; i++) print $i '
其中 -F" " 定义 awk 使用的分隔符。在我的例子中是空格,它也是 awk 的默认分隔符。这意味着 -F" " 可以忽略。
NF 定义字段/列的总数。因此循环将从第 4 个字段开始直到最后一个字段/列。
其中 $N 检索第 N 个字段的值。因此 print $i 将根据循环计数打印当前字段/列。
【讨论】:
问题,将每个字段打印在不同的行上。 没有什么能阻止你在最后添加这个:-) ` | tr '\n' ' ' ` 有点晚了但是 awk ' for (i = 5; i 【参考方案21】:打印从 #2 开始的列(输出开头没有尾随空格):
ls -l | awk 'sub(/[^ ]+ /, ""); print $0'
【讨论】:
很好,尽管您应该在空格后添加+
,因为字段可能被超过 1 个空格分隔(awk
将多个相邻空格视为单个分隔符)。此外,awk
将忽略前导空格,因此您应该以^[ ]*
开始正则表达式。使用空间作为分隔符,您甚至可以概括解决方案;例如,以下内容从第三个字段返回所有内容:awk 'sub(/^[ ]*([^ ]+ +)2/, ""); print $0'
不过,使用任意字段分隔符会变得更加棘手。【参考方案22】:
这是我在所有建议中的首选:
从第 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 "\n"'
【讨论】:
【参考方案23】:echo "1 2 3 4 5 6" | awk ' $NF = ""; print $0'
这个使用 awk 打印除最后一个字段之外的所有字段
【讨论】:
【参考方案24】:awk 'out=$2; for(i=3;i<=NF;i++)out=out" "$i; print out'
我的答案是基于the one of VeeArr,但我注意到它在打印第二列(以及其余列)之前以空格开头。由于我只有 1 个声望点,我无法对此发表评论,所以这里作为一个新答案:
以“out”作为第二列,然后添加所有其他列(如果存在)。只要有第二列,这就很好。
【讨论】:
太好了,您还删除了 out 变量前面的 $,这也很重要。以上是关于awk打印某列之后的所有列的主要内容,如果未能解决你的问题,请参考以下文章