如何在awk中保留字段之间的原始空白?

Sto*_*ica 13 awk

处理输入时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
Run Code Online (Sandbox Code Playgroud)

如果我不修改任何字段($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
Run Code Online (Sandbox Code Playgroud)

所有字段之间的原始空格将替换为一个简单的空格.

有没有办法保留字段之间的原始空白?

UPDATE

在此示例中,编辑前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
Run Code Online (Sandbox Code Playgroud)

Håk*_*and 11

如果你想保留空格,你也可以试试这个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
}' 
Run Code Online (Sandbox Code Playgroud)

给出输出

a  2   7  6
Run Code Online (Sandbox Code Playgroud)

  • 这是 **THE** 正确答案,也是引入 `split()` 的第四个参数的主要原因。在 FS 可以是任何正则表达式的一般情况下,其他任何事情都变得非常复杂,而不仅仅是默认的空格或其他任何可以简单地在括号表达式中取反的东西。 (2认同)

Sto*_*ica 5

可以通过编辑$0而不是单个字段($1$2、 ...)来保留原始空格,例如:

\n\n
$ ls -l | awk \'{$0 = substr($1, 1, 3) substr($0, length($1) + 1)} 1\'\ntot 88\n-rw 1 jack jack     8 Jun 19  2013 qunit-1.11.0.css\n-rw 1 jack jack 56908 Jun 19  2013 qunit-1.11.0.js\n-rw 1 jack jack  4306 Dec 29 09:16 test1.html\n-rw 1 jack jack  5476 Dec  7 08:09 test1.js\n
Run Code Online (Sandbox Code Playgroud)\n\n

在编辑第一列时,这相对容易做到,但在编辑其他列($2、...、$4)时会很麻烦,并且在中间空白宽度不固定的字段($5在本例中超出)之后会崩溃。

\n\n

更新

\n\n

根据@H\xc3\xa5kon H\xc3\xa6gland \的答案,这里有一种保留第6个字段(月份)的前2个字符的方法:

\n\n
{\n    n = split($0, f, " ", sep)\n    f[6] = substr(f[6], 1, 2)\n    line = sep[0]\n    for (i = 1; i <= n; ++i) line = line f[i] sep[i]\n    print line\n}\n
Run Code Online (Sandbox Code Playgroud)\n


小智 5

我知道这是一个老问题,但我认为必须有更好的选择。该答案适用于在搜索时偶然发现此问题的人。在网上浏览时,我不得不说@HåkonHægland是最好的答案,这是我最初使用的。

但是这是我的解决方案。使用FPAT。它可以设置一个正则表达式来说明字段应该是什么。

 FPAT = "([[:space:]]*[[:alnum:][:punct:][:digit:]]+)";
Run Code Online (Sandbox Code Playgroud)在这种情况下,我要说的是,该字段应以零个或多个空白字符开头,并以除空白字符外的任何其他字符结尾。如果您无法理解,这里是一个链接POSIX括号表达式时。

另外,将输出字段更改为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
Run Code Online (Sandbox Code Playgroud)
$ 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
Run Code Online (Sandbox Code Playgroud)

记住。字段现在有前导空格。因此,如果该字段需要替换为其他内容,则可以执行

len = length($1); 
$1 = sprintf("%"(len)"s", "-42-");
Run Code Online (Sandbox Code Playgroud)
$ 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
Run Code Online (Sandbox Code Playgroud)

  • 你可以用 `[^[:space:]]` 替换 `[[:alnum:][:punct:][:digit:]]`,除了更简洁之外,解决方案也会更健壮。不知道`-42-` 的内容是什么,但如果你只是想在字段宽度中显示一些东西,它会被写成`$1 = sprintf("%*s", len, "-42- ")`,而不是`$1 = sprintf("%"(len)"s", "-42-")`。显然,当使用默认 FS 之外的其他解决方案时,整个解决方案都会崩溃,因此 [@Hakon 的解决方案](http://stackoverflow.com/a/20836890/1745001) 是首选。 (2认同)

ImH*_*ere 5

最简单的解决方案是确保在每个空间上都进行了字段拆分。这是通过制作字段分隔符来完成的[ ]

$ 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
Run Code Online (Sandbox Code Playgroud)

默认情况下,awk 将拆分任何重复的空格(制表符和空格,类似于[ \t]+。手册指出:

在 FS 是单个空格的特殊情况下,字段由空格和/或制表符和/或换行符分隔。

这会将空格、制表符和换行符的运行折叠为输出中只有一个 OFS 值。如果 OFS 也是一个空格(也是默认值),则结果是每次运行空白时只会打印一个空格。

但是可以告诉 awk 使用仅匹配一个字符的正则表达式只选择一个空格作为字段分隔符:[ ]

请注意,这将更改字段的字段编号。每个空间都会开始一个新的领域。因此,请注意您提供的数据的结果:

$ awk -F '[ ]' '{print($4,$5,$6)}' infile
jack
jack 56908 Jun
jack  4306
jack  5476
Run Code Online (Sandbox Code Playgroud)

在这种特定情况下,第一个字段之前没有空格,后面只有一个空格,这就是它正常工作的原因。