Append the arguments to a script as lines in a file

How do you append $@ or $* , each as a new line to a file?
I think I already searched for that before.

Adding a new option -i --insert-newline with possible values 1, 2, 3 or Linux, Windows, Mac to echo command should be enough.

Asked By: Laurent Lyaudet

||

In zsh, you can do:

print -rC1 -- "$@"

Prints the arguments raw on 1 Column. Add >> file to append that output to a file.

print is ksh’s replacement for the highly unreliable and unportable echo command. -C is a zsh extension though. bash also has a print builtin but it’s one that you have to enable with the enable builtin┬╣ and doesn’t support -C anyway.

Standardly:

printf '%sn' "$@"

Would also do that but with one caveat: if the list of positional parameters is empty, that still prints an empty line, as if the list contained one empty element.

That’s why in bash or other Bourne-like shells, it’s common to define a:

println() {
  [ "$#" -eq 0 ] || printf '%sn' "$@"
}

function that calls printf only if the list is non empty.

Then you do:

println "$@" >> file

Or without the function:

{ [ "$#" -eq 0 ] || printf '%sn' "$@"; } >> file

Note that it’s not completely equivalent to:

[ "$#" -eq 0 ] || printf '%sn' "$@" >> file

As when $# is 0, the latter doesn’t attempt to open file for writing (and would therefore not create it empty if it didn’t exist beforehand).

printf is POSIX’s own answer to the echo mess. Not sure why they didn’t go for specifying ksh‘s print instead. There was some prior art of a printf utility in research Unix v8 or SysV, but with different APIs.

In the fish shell, you can actually use echo with:

echo -sn -- $argvn

Where -n disables the adding of a final newline, -s disables the adding of a space between each element and we append a newline manually to each element.

You can do the same in zsh with:

echo -En - ${(j[])argv/%/$'n'}

Here, replacing -s with the join parameter expansion flag. And appending a newline using the ksh-style ${var/%pattern/replacement}.

In bash and as long as the xpg_echo and posix options are not both enabled (which would prevent echo from supporting options), that can also be done with:

IFS=; echo -nE "${*/%/$'n'}"

Same except that instead of using j[] to join the elements with nothing, we rely on the fact that "$*" is joined with the first character of $IFS or nothing if $IFS is empty.

bash‘s echo doesn’t support an option delimiter like the -- of fish‘s or the - of zsh‘s, but the adding of at least one newline means that arguments should not be taken as options to echo even if they start with -. That might not be future-proof though (like if in a future version of bash, echo supports a --separator=<sep> option; unlikely).


┬╣ Something like enable -f /usr/lib/bash/print print, where the path may vary from one system to the next and not all systems provide those loadable builtins by default if they ship bash at all.

Answered By: Stéphane Chazelas
Categories: Answers Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.