S. M. S. M. - 4 months ago 18
Linux Question

xargs does not preserve leading whitespace in format

In Linux C Shell, I need to replace a line in a file with a variable which contains a set of numbers and a string. I am using a combination of printf, xargs and sed to preserve the formatting as suggested by this answer.
This is my code up to now (I couldn't find an easier approach in csh):

> echo " 2 2 2 2 2 2 2 2 Number of gradient directions" > myfile.txt
> set ndir = `repeat 8 printf ' 2'`
> set ndir[1] = 16
> set afmt1 = `repeat 8 printf '%%3d'`
> set afmt = "$afmt1%s"
> printf "$afmt" $ndir " Number of gradient directions" | xargs -I '{}' sed -i '/Number of gradient directions/c\{}' myfile.txt


The result is:

> cat myfile.txt
16 2 2 2 2 2 2 2 Number of gradient directions


But I need:

> cat myfile.txt
16 2 2 2 2 2 2 2 Number of gradient directions


The only issue is that the line loses the %3d format only for the first index of the variable $ndir. I need to preserve this format for the whole line. Is there a more subtle way or a fix to this way?

Answer

Add -d'\n' to your xargs command:

printf "$afmt" $ndir "     Number of gradient directions" | xargs -d'\n' -I '{}' sed -i '/Number of gradient directions/c\{}' myfile.txt

The problem is that xargs trims leading whitespace from each input line when using -I , which you can verify as follows:

% echo '  A   B  ' | xargs -I {} echo "[{}]"
[A  B  ]

Adding option -d'\n' deactivates this behavior and causes the entire line to be passed as-is:

% echo '  A   B  ' | xargs -d'\n' -I {} echo "[{}]"
[  A  B  ]

As an aside (not relevant on Linux):

BSD/OSX xargs not only trims both leading and trailing whitespace with -I, it also normalizes line-interior whitespace; Since BSD/OSX xargs doesn't support the -d option, the following workaround is needed to pass lines as-is (would also work with GNU xargs):

% echo '  A   B  ' | tr '\n' '\0' | xargs -0 -I {} echo "[{}]"
[  A  B  ]