Shell – Write variable containing large text with \n to a file with common shell interpreters. How to do it

filesshellstring

Environment: linux shell or better, ash shell in busybox (example system: Openwrt).

Given a string variable with large text "${a}" constructed in this way:

for index in $(seq 1 40000); do #it is 40000 for a reason, it helps if you want to reproduce the error on your system.
   a="${a}""\n""word ${index}"
done

and given the fact that trying to use this variable as argument for common command produce an error, for example:

echo "${a}" #fails
awk -v VAR_A="${a}" '<program>' #fails

(the failures are due: http://www.in-ulm.de/~mascheck/various/argmax/ )

How do you write such a variable to a text file, possibly using only simple commands and not sed/awk/perl/other powerful interpreter.

Ideally the text file should look like:

word 1
word 2
word 3
...

and not like:

word 1\nword 2\nword 3\n...

Update1: Someone asks "Why you cannot change the script that produce the variable $a?". Because i cannot due to, let's say, lack of authorization ("the script was working until today, so you can fix only the printing part"). So $a and its format is given, i can only find a way to print it to a file.

Update2: Using the suggested "here docs" solve most of the problems but still i have the content printed in one line. Maybe is my config?

Best Answer

You can make a here document and use cat:

$ cat <<EOT >output
$a
EOT
$ wc -l output
40000

I don't know exactly how powerful your Busybox's ash is (it's configurable), but that should work anywhere, even with no builtins at all. It expands the variable value into a quasi-file, which is then given to cat, rather than putting the value itself into the command line.


Since you seem to have literal "\n" in your string that you want to get rid of, you can also use string replacement during parameter subsitution. This can be configured out of Busybox's ash, in which case you'll have to go to sed. If you do have this (non-POSIX) extension, you can use ${var//\\\n/$NEWLINE}:

$ busybox ash
$ foo="x\ny\nz"
$ echo "$foo"
x\ny\nz
$ NEWLINE="
"
$ echo "${foo//\\\n/$NEWLINE}"
x
y
z

This is the same syntax as Bash; ${var/pattern/replacement}, where pattern is global if it starts with /. It seems your ash has a lot configured out of it, so this might not be there at all. In that case you're probably going to have to go with sed, although I guess judicious use of IFS and read would let you work around that and reconstruct a correct string.

Related Question