Как сохранить исходные пробелы между полями в 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

Если я не редактирую ни одно из полей ($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

Исходное пробел между всеми полями заменяется простым пространством.

Есть ли способ сохранить исходные пробелы между полями?

UPDATE

В этом примере относительно легко редактировать первые 4 поля. Но что, если я хочу сохранить только 1-ю букву $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

Ответ 1

Я знаю, что это старый вопрос, но я думал, что должно быть что-то лучше. Этот ответ для тех, кто наткнулся на этот вопрос во время поиска. Если посмотреть в Интернете, я должен сказать, что @Håkon Hægland имеет лучший ответ, и именно это я использовал вначале.

Но вот мое решение. Используйте FPAT. Он может установить регулярное выражение, чтобы сказать, какое должно быть поле.

 FPAT = "([[:space:]]*[[:alnum:][:punct:][:digit:]]+)";
В этом случае я говорю, что поле должно начинаться с нуля или более пустых символов и заканчивается в основном любым другим символом, кроме пустых символов. Здесь - ссылка, если у вас возникли проблемы с пониманием выражений POSIX.

Кроме того, измените поле вывода на OFS = ""; separator, потому что, как только линия была обработана, на выходе будет добавлено дополнительное пустое пространство в качестве разделителя, если вы не измените 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

Если вы хотите сохранить пробел, вы также можете попробовать функцию 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

Ответ 3

Можно сохранить исходные пробелы, отредактировав $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 и выше в этом примере).

UPDATE

На основе @Håkon Hægland ответьте здесь, чтобы сохранить первые 2 символа 6-го поля (месяца):

{
    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
}