- EDIT 2019-Oct-11 - Simple example
- removed previous example
I want awk to resepect a TAB character embedded in $0 as a content when it reprocesses the $0 input record after a field value change ($1, $2, ..).
Here a short example. In the output below, "t @ 48" for example, means there is a TAB at position 48 in the $0 data record. Please note that "\t" is expanded to: TAB chr(9) as initial processing of the input (labeled raw).
Example output:
$ ./tmp.awk tmp.input
raw $0: ' line with spaces here a tab between AAA\tBBB', t @ 0, NF = 8, len = 52.
$1: 'line', len = 4.
unescape $0: ' line with spaces here a tab between AAA BBB', t @ 48, NF = 9, len = 51.
$1 = $1, $0: 'line with spaces here a tab between AAA BBB', t @ 0, NF = 9, len = 43.
unescape $0: ' line with spaces here a tab between AAA BBB', t @ 48, NF = 9, len = 51.
$1 = "", $0: ' with spaces here a tab between AAA BBB', t @ 0, NF = 9, len = 39.
final $0: ' with spaces here a tab between AAA BBB', t @ 0, NF = 9, len = 39.
When "\t" is expanded and $0 updated, awk correctly rebuilds and gives 9 fields (no longer 8). (tick)
Input record is:
line with spaces here a tab between AAA\tBBB
Desired result:
The end goal, is to be able to remove the content of field $1 while preserving all the formatting and spacing would be as shown.
$0: ' with spaces here a tab between AAA BBB', t @ 44, NF = 8, len = 47.
With only the specified $1-characters removed, which is "line". Including the TAB between "AAA" and "BBB". I have shown one less field (NF = 8). Awk itself appears to retain the empty $1 cell so NF = 9 would also be acceptable.
Following the line labeled: $1 = $1', when we change the value of $12.
{
:
print " unescape $0: '" $0 "', t @ " index( $0, "\t" ) ", NF = " NF ", len = " length( $0 ) ".";
$1 = $1; # force record to be reconstituted
print " $1 = $1, $0: '" $0 "', t @ " index( $0, "\t" ) ", NF = " NF ", len = " length( $0 ) ".";
}
output ...
unescape $0: ' line with spaces here a tab between AAA BBB', t @ 48, NF = 9, len = 51.
$1 = $1, $0: 'line with spaces here a tab between AAA BBB', t @ 0, NF = 9, len = 43.
Please note that while I still have 7 fields on this line. There is NO LONGER a TAB character and the multiple-spaces after "here" have been removed. These formatting changes are undesirable for this use-case.
I get this result consistiently NO matter what values I enter for field separator, FS (even a line-feed) and OFS. Actually changing OFS make things much worse.
The behaviour was not anticipated. However after some comments, it migh be that this is prescribed no matter what.
Sample awk script:
{
print "";
print "raw $0: '" $0 "', t @ " index( $0, "\t" ) ", NF = " NF ", len = " length( $0 ) ".";
print "$1: '" $1 "', len = " length( $1 ) ".";
gsub(/\\t/, "\t", $0); # expand any embedded TAB-s
print "unescape $0: '" $0 "', t @ " index( $0, "\t" ) ", NF = " NF ", len = " length( $0 ) ".";
preserve = $0;
print "";
$1 = $1; # force record to be reconstituted
print "$1 = $1, $0: '" $0 "', t @ " index( $0, "\t" ) ", NF = " NF ", len = " length( $0 ) ".";
print "";
$0 = preserve;
print "unescape $0: '" $0 "', t @ " index( $0, "\t" ) ", NF = " NF ", len = " length( $0 ) ".";
$1 = "";
print "$1 = \"\", $0: '" $0 "', t @ " index( $0, "\t" ) ", NF = " NF ", len = " length( $0 ) ".";
print "";
print "final $0: '" $0 "', t @ " index( $0, "\t" ) ", NF = " NF ", len = " length( $0 ) ".";
print "";
}
Questions ...
- How can I get the desired behaviour? Meaning, no edit of the record when a field is removed?
- If that is not possible -- Is there a method which preserves the integrity and spacing of the 'current' $0 record?
- For example I was looking for an array that maps all the fields to the $0 record, but didn't find it.
- How is it possible to preserve the
TABin the example. - Can this editing of the
$0record be prevented?
Characters have been deleted. Examination shows that awk has edited-out repeated spaces (deleted them) and TAB.
The single space is not the culprit, it would appear to be the reconstitution or manufacture of the $0 record.
Reference:
The areas from the UG ... Gnu Awk User Guide:
Fields are normally separated by whitespace sequences (spaces, TABs, and newlines), not by single spaces. Two spaces in a row do not delimit an empty field. The default value of the field separator FS is a string containing a single space, " ".
I get that FS space is special. However even when I put a strange FS like "W" and "\n" characers are still deleted from $0 following the $1 = $1 rebuild step.
- Conclusion the FS is not used when reprocessing
$0
A change in the value of $0 = new string, has worked as expected. The number of fields goes up because awk recognised the Tab character. I must point-out that awk did not delete the Tab in this case (as desired).
Finally, there are times when it is convenient to force awk to rebuild the entire record, using the current values of the fields and OFS. To do this, use the seemingly innocuous assignment:
$1 = $1 # force record to be reconstituted
print $0 # or whatever else with $0
This forces awk to rebuild the record. It does help to add a comment, as we’ve shown here.
The version used:
gawk -V
GNU Awk 4.2.1, API: 2.0 (GNU MPFR 4.0.2, GNU MP 6.1.2)
Copyright (C) 1989, 1991-2018 Free Software FoundationUbuntu 19.04
This instruction doesn't warn that the $0 can have 9 characters removed, or even hint that $0 will be affected.
Other unexplained aspects:
- I there an explaination for this?
- Is this
gawkonly or is it common across alternativeawk-s?
Personally, I was very happy with $0 does not change. There are many times when I want awk for its ability to structure data and preserve unstructured source for output.
Looking forward to your thoughts.