这行一直有效,直到我在第二个字段中留有空格。
svn status | grep '\!' | gawk '{print $2;}' > removedProjs
有没有办法让awk打印$ 2或以上的所有内容?($ 3,$ 4 ..直到我们没有更多的列了?)
我想我应该补充一点,就是我在Windows环境中使用Cygwin进行此操作。
svn status | grep '\!' | cut -d' ' -f2- > removedProjs
这行一直有效,直到我在第二个字段中留有空格。
svn status | grep '\!' | gawk '{print $2;}' > removedProjs
有没有办法让awk打印$ 2或以上的所有内容?($ 3,$ 4 ..直到我们没有更多的列了?)
我想我应该补充一点,就是我在Windows环境中使用Cygwin进行此操作。
svn status | grep '\!' | cut -d' ' -f2- > removedProjs
Answers:
将打印除第一列以外的所有内容:
awk '{$1=""; print $0}' somefile
将打印除第一列以外的所有列:
awk '{$1=$2=""; print $0}' somefile
awk '{$1=""; print substr($0,2)}' input_filename > output_filename
awk -F, -vOFS=, '{$1=""; print $0}'
您将得到一个初始定界符($1
仍包括在内,就像一个空字符串一样)。您可以通过以下方式删除它sed
:awk -F, -vOFS=, '{$1=""; print $0}' | sed 's/^,//'
awk
对待多个相邻的空格字符。作为单个分隔符,而cut
没有;-尽管这在当前情况下不是问题,但也cut
只接受一个文字字符。作为分隔符,而awk
允许使用正则表达式。
您可以使用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"}'
'{for(i=11;i<=NF-1;i++){printf "%s ", $i}; print $NF;}'
没有前导或尾随空格。
awk '{out=$2; for(i=3;i<=NF;i++){out=out" "$i}; print out}'
我的答案基于VeeArr之一,但我注意到它以空格开头,然后才打印第二列(其余部分)。由于我只有1个声誉点,因此我无法对其进行评论,因此这里提供了新的答案:
以“ out”作为第二列,然后添加所有其他列(如果存在)。只要有第二列,就可以顺利进行。
大多数使用awk的解决方案都留有空格。这里的选项可以避免该问题。
一个简单的剪切解决方案(仅适用于单个定界符):
command | cut -d' ' -f3-
强制awk重新计算有时会通过删除第一个字段来删除剩余的前导空格(OFS)(适用于某些版本的awk):
command | awk '{ $1=$2="";$0=$0;} NF=NF'
打印每个用格式化的字段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。让我们构建一些不这样做的选项。
带有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
使用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
它也可以用来交换给定count的一组字段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]*
用于在输入行中保留前导空格。
我亲自尝试了上面提到的所有答案,但是其中大多数都有些复杂或不正确。从我的角度来看,最简单的方法是:
awk -F" " '{ for (i=4; i<=NF; i++) print $i }'
-F“”定义了awk要使用的分隔符。就我而言,是空白,它也是awk的默认定界符。这意味着-F“”可以忽略。
其中NF定义字段/列的总数。因此,循环将从第四个字段开始直到最后一个字段/列。
$ N检索第N个字段的值。因此,print $ i将基于循环计数打印当前字段/列。
这让我非常恼火,我坐下来写了一个类似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);
}
cut
,而不是awk
打印出从#2开始的列(输出开头将没有尾随空格):
ls -l | awk '{sub(/[^ ]+ /, ""); print $0}'
+
在空格后面加上空格,因为字段可能被awk
多个空格分隔(将多个相邻空格视为一个分隔符)。另外,awk
将忽略前导空格,因此您应使用来启动正则表达式^[ ]*
。使用空格作为分隔符,您甚至可以泛化解决方案。例如,以下代码从第三字段返回所有内容:awk '{sub(/^[ ]*([^ ]+ +){2}/, ""); print $0}'
但是,使用任意字段分隔符会变得更加棘手。
echo "1 2 3 4 5 6" | awk '{ $NF = ""; print $0}'
这个使用awk打印除最后一个字段以外的所有内容
这是我从所有建议中首选的:
从第六列到最后一列打印。
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"}'
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
如果您不想重新格式化您不想砍掉的那一部分,那么我能想到的最佳解决方案写在我的答案中:
它会截取给定字段号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的文件/流中失败
Perl:
@m=`ls -ltr dir | grep ^d | awk '{print \$6,\$7,\$8,\$9}'`;
foreach $i (@m)
{
print "$i\n";
}
此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
声明局部变量的一种方式。
我想将提议的答案扩展到字段可能由多个空格分隔的情况,cut
我想是OP不使用的原因。
我知道OP询问过awk
,但是sed
这里可以使用一种方法(例如,从第5列到最后一个列的打印):
纯sed方法
sed -r 's/^\s*(\S+\s+){4}//' somefile
说明:
s///
用于执行替换的标准方法^\s*
匹配行首的任何连续空格\S+\s+
表示一列数据(非空白字符,后跟空白字符)(){4}
表示图案重复了4次。切割
sed -r 's/^\s+//; s/\s+/\t/g' somefile | cut -f5-
通过仅用单个选项卡替换连续的空格;
tr and cut:
tr
也可以使用该选项挤压连续字符-s
。
tr -s [:blank:] <somefile | cut -d' ' -f5-
我对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。
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列为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
$
如果要格式化文本,请用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 |
由于错误的最投票赞成的投票结果为340票,我只输了5分钟!在赞成之前有人有人尝试过这个答案吗?显然不是。完全没用。
我有一个日志,其中带有IP地址的$ 5之后可以是更多文本,也可以是没有文本。我需要从IP地址到行尾的所有内容,$ 5之后应该有什么。就我而言,这实际上是一个awk程序,而不是一个awk oneliner,因此awk必须解决问题。当我尝试使用最受欢迎但完全错误的答案删除前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]
grep | awk
awk '/!/ { print $2 }'