使用awk
处理输入时,有时我想编辑其中一个字段,而不触及任何其他字段。考虑一下:
$ ls -l | awk 1
total 88
-rw-r--r-- 1 jack jack 8 Jun 19 2013 qunit-1.11.0.css
-rw-r--r-- 1 jack jack 56908 Jun 19 2013 qunit-1.11.0.js
-rw-r--r-- 1 jack jack 4306 Dec 29 09:16 test1.html
-rw-r--r-- 1 jack jack 5476 Dec 7 08:09 test1.js
如果我不编辑任何字段($1
,$2
,...),则所有内容都会保留原样。但是如果让我说我只想保留第一个字段的前3个字符:
$ ls -l | awk '{$1 = substr($1, 1, 3) } 1'
tot 88
-rw 1 jack jack 8 Jun 19 2013 qunit-1.11.0.css
-rw 1 jack jack 56908 Jun 19 2013 qunit-1.11.0.js
-rw 1 jack jack 4306 Dec 29 09:16 test1.html
-rw 1 jack jack 5476 Dec 7 08:09 test1.js
所有字段之间的原始空格将替换为一个简单的空格。
有没有办法保留字段之间的原始空格?
更新
在此示例中,编辑前4个字段相对容易。但是,如果我只想保留$5
的第一个字母以获得此输出,那该怎么办:
-rw-r--r-- 1 jack jack 8 J 19 2013 qunit-1.11.0.css
-rw-r--r-- 1 jack jack 56908 J 19 2013 qunit-1.11.0.js
-rw-r--r-- 1 jack jack 4306 D 29 09:16 test1.html
-rw-r--r-- 1 jack jack 5476 D 7 08:09 test1.js
答案 0 :(得分:9)
如果您想保留空白,还可以尝试使用split
功能。
在Gnu Awk版本4中,split
函数接受4个参数,后者是字段之间的分隔符。例如,
echo "a 2 4 6" | gawk ' {
n=split($0,a," ",b)
a[3]=7
line=b[0]
for (i=1;i<=n; i++)
line=(line a[i] b[i])
print line
}'
给出输出
a 2 7 6
答案 1 :(得分:5)
我知道这是一个老问题,但我认为必须有更好的东西。这个答案适合那些在搜索时偶然发现这个问题的人。在网上浏览时,我不得不说@Håkon Hægland有最好的答案,这就是我最初使用的。
但这是我的解决方案。使用FPAT。它可以设置正则表达式来表示字段应该是什么。
FPAT = "([[:space:]]*[[:alnum:][:punct:][:digit:]]+)";
在这种情况下,我说该字段应该以零个或多个空白字符开头,并以除空白字符之外的任何其他字符结束。如果您无法理解POSIX
括号表达式,那么Here就是一个链接。
此外,将输出字段更改为OFS = "";
分隔符,因为一旦操作了该行,如果不更改OFS的默认值,输出将添加一个额外的空格作为分隔符。
我使用相同的例子进行测试。
$ cat example-output.txt
-rw-r--r-- 1 jack jack 8 Jun 19 2013 qunit-1.11.0.css
-rw-r--r-- 1 jack jack 56908 Jun 19 2013 qunit-1.11.0.js
-rw-r--r-- 1 jack jack 4306 Dec 29 09:16 test1.html
-rw-r--r-- 1 jack jack 5476 Dec 7 08:09 test1.js
$ awk 'BEGIN { FPAT = "([[:space:]]*[[:alnum:][:punct:][:digit:]]+)"; OFS = ""; } { $6 = substr( $6, 1, 2); print $0; }' example-output.txt
-rw-r--r-- 1 jack jack 8 J 19 2013 qunit-1.11.0.css
-rw-r--r-- 1 jack jack 56908 J 19 2013 qunit-1.11.0.js
-rw-r--r-- 1 jack jack 4306 D 29 09:16 test1.html
-rw-r--r-- 1 jack jack 5476 D 7 08:09 test1.js
请记住。这些字段现在有前导空格。因此,如果该字段需要被其他内容替换,则可以执行
len = length($1);
$1 = sprintf("%"(len)"s", "-42-");
$ awk 'BEGIN { FPAT = "([[:space:]]*[[:alnum:][:punct:][:digit:]]+)"; OFS = ""; } { if(NR==1){ len = length($1); $1 = sprintf("%"(len)"s", "-42-"); } print $0; }' example-output.txt
-42- 1 jack jack 8 Jun 19 2013 qunit-1.11.0.css
-rw-r--r-- 1 jack jack 56908 Jun 19 2013 qunit-1.11.0.js
-rw-r--r-- 1 jack jack 4306 Dec 29 09:16 test1.html
-rw-r--r-- 1 jack jack 5476 Dec 7 08:09 test1.js
答案 2 :(得分:4)
可以通过修改$0
代替单个字段($1
,$2
,...)来保留原始空白,例如:
$ ls -l | awk '{$0 = substr($1, 1, 3) substr($0, length($1) + 1)} 1'
tot 88
-rw 1 jack jack 8 Jun 19 2013 qunit-1.11.0.css
-rw 1 jack jack 56908 Jun 19 2013 qunit-1.11.0.js
-rw 1 jack jack 4306 Dec 29 09:16 test1.html
-rw 1 jack jack 5476 Dec 7 08:09 test1.js
编辑第一列时相对容易,但在编辑其他列时遇到麻烦($2
,...,$4
),并在空格宽度的字段后分解中间不固定(在此示例中为$5
及以上)。
<强>更新强>
根据@Håkon Hægland的答案,这里有一种方法可以保留第6个字段(月份)的前2个字符:
{
n = split($0, f, " ", sep)
f[6] = substr(f[6], 1, 2)
line = sep[0]
for (i = 1; i <= n; ++i) line = line f[i] sep[i]
print line
}
答案 3 :(得分:2)
最简单的解决方案是确保在每个空间上完成字段分割。这是通过制作字段分隔符[ ]
:
$ awk -F '[ ]' '{$1=substr($1,1,3)}1' infile
-rw 1 jack jack 8 Jun 19 2013 qunit-1.11.0.css
-rw 1 jack jack 56908 Jun 19 2013 qunit-1.11.0.js
-rw 1 jack jack 4306 Dec 29 09:16 test1.html
-rw 1 jack jack 5476 Dec 7 08:09 test1.js
默认情况下,awk会拆分任何重复的空白区域(标签和空格,类似于[ \t]+
。手册说明:
在FS是单个空格的特殊情况下,字段由空格和/或制表符和/或换行符分隔。
这将在输出中将空格,制表符和换行符的运行仅折叠为OFS的一个值。如果OFS也是一个空格(也是默认值),结果是每次运行空白区域只会打印一个空格。
但是awk可以被告知只使用一个只匹配一个字符的正则表达式选择一个空格作为字段分隔符:[ ]
。
请注意,这将更改字段的字段数。每个空间都将开始一个新的领域。因此,请注意您提供的数据的结果:
$ awk -F '[ ]' '{print($4,$5,$6)}' infile
jack
jack 56908 Jun
jack 4306
jack 5476
在这种特殊情况下,第一个字段之前没有空格,之后只有一个空格,这就是它正常工作的原因。