How to preserve the original whitespace between fields in awk?

Viewed 10132

When processing input with awk, sometimes I want to edit one of the fields, without touching anything else. Consider this:

$ 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

If I don't edit any of the fields ($1, $2, ...), everything is preserved as it was. But if let's say I want to keep only the first 3 characters of the first field:

$ 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

The original whitespace between all fields is replaced with a simple space.

Is there a way to preserve the original whitespace between the fields?

UPDATE

In this sample, it's relatively easy to edit the first 4 fields. But what if I want to keep only the 1st letter of $5 in order to get this output:

-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
4 Answers

The simplest solution is to make sure that the field spliting is done on every single space. That is done by making the field separator [ ]:

$ 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

By default, awk will split on any repetition of white spaces (tabs and spaces, something similar to [ \t]+. The manual states:

In the special case that FS is a single space, fields are separated by runs of spaces and/or tabs and/or newlines.

That will collapse runs of spaces, tabs and newlines to only one value of OFS in the output. If OFS is also an space (also the default), the result is that only one space will be printed for each run of white space.

But awk could be told to select only one space as a field delimiter using a regular expression that will match only one character: [ ].

Note that that will change the field numbers of fields. Each space will start a new field. So, note this result from the data you presented:

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

In this specific case, there are no spaces before the first field, and only one space after, that's why it works correctly.

Related