78

I want to write out a bash array variable to a file, with each element on a new line. I could do this with a for loop, but is there another (cleaner) way to join the elements with \n?

9 Answers 9

101

Here's a way that utilizes bash parameter expansion and its IFS special variable.

$ System=('s1' 's2' 's3' 's4 4 4')
$ ( IFS=$'\n'; echo "${System[*]}" )

We use a subshell to avoid overwriting the value of IFS in the current environment. In that subshell, we then modify the value of IFS so that the first character is a newline (using $'...' quoting). Finally, we use parameter expansion to print the contents of the array as a single word; each element is separated by the first charater of IFS.

To capture to a variable:

$ var=$( IFS=$'\n'; echo "${System[*]}" )

If your bash is new enough (4.2 or later), you can (and should) still use printf with the -v option:

$ printf -v var "%s\n" "${System[@]}"

In either case, you may not want the final newline in var. To remove it:

$ var=${var%?}    # Remove the final character of var
5
  • Thanks, is there a way to output this to a variable?
    – ACyclic
    Aug 16, 2012 at 16:26
  • Updated to show how to capture to a variable.
    – chepner
    Aug 16, 2012 at 16:34
  • 2
    Shouldn't the last example be var=${var%?} instead? This is not a regular expression, so . will match only a period character.
    – musiphil
    Sep 13, 2013 at 17:49
  • Need quotes the array variable name: $ IFS=', ' $ echo ${VAR[*]} first second third $ echo "${VAR[*]}" first,second,third
    – Seff
    Jun 16, 2019 at 4:23
  • 2
    var=$( .. ) trims the trailing newline automatically; printf -v var doesn't Apr 23, 2020 at 6:09
36

You can use printf to print each array item on its own line:

 $ System=('s1' 's2' 's3' 's4 4 4')
 $ printf "%s\n"  "${System[@]}"
s1
s2
s3
s4 4 4
0
11
awk -v sep='\n' 'BEGIN{ORS=OFS="";for(i=1;i<ARGC;i++){print ARGV[i],ARGC-i-1?sep:""}}' "${arr[@]}"

or

perl -le 'print join "\n",@ARGV' "${arr[@]}"

or

python -c 'import sys;print "\n".join(sys.argv[1:])' "${arr[@]}"

or

sh -c 'IFS=$'\''\n'\'';echo "$*"' '' "${arr[@]}"

or

lua <(echo 'print(table.concat(arg,"\n"))') "${arr[@]}"

or

tclsh <(echo 'puts [join $argv "\n"]') "${arr[@]}"

or

php -r 'echo implode("\n",array_slice($argv,1));' -- "${arr[@]}"

or

ruby -e 'puts ARGV.join("\n")' "${arr[@]}"

that's all I can remind so far.

0
2

Above solutions are pretty much it, but the original question asks for output to file:

$ a=(a b c d e)
$ ( IFS=$'\n'; echo "${a[*]}" ) > /tmp/file
$ cat /tmp/file
a
b
c
d
e
$

Notes: 1) 'echo' provides the final newline 2) If this file will just be read in by bash again, then declare -p may be the serialization wanted.

2

Using for:

for each in "${alpha[@]}"
do
  echo "$each"
done

Using history; note this will fail if your values contain !:

history -p "${alpha[@]}"

Using basename; note this will fail if your values contain /:

basename -a "${alpha[@]}"

Using shuf; note that results might not come out in order:

shuf -e "${alpha[@]}"
1

My take, using only Bash builtins, without mutating IFS:

# $1  separator
# $2… strings
join_strings () {
    declare separator="$1";
    declare -a args=("${@:2}");
    declare result;
    printf -v result '%s' "${args[@]/#/$separator}";
    printf '%s' "${result:${#separator}}"
}

Example

$ join_strings $'\n' "a b c" "d e f" "g h i"
a b c
d e f
g h i

You can also use any separator:

$ join_strings '===' "a b c" "d e f" "g h i"
a b c===d e f===g h i
1

here is my impression of joinArray:

function joinArray() {
  local delimiter="${1}"
  local output="${2}"
  for param in ${@:3}; do
    output="${output}${delimiter}${param}"
  done

  echo "${output}"
}
0

printf seems to be the most efficient approach for creating a delimited string from an array:

# create a delimited string; note that printf doesn't put the trailing delimiter
# need to save and restore IFS
# it is prudent to put the whole logic on a single line so as to minimize the risk of future code changes breaking the sequence of saving/restoring of IFS
oldIFS=$IFS; IFS=$'\n'; printf -v var "${arr[*]}"; IFS=$oldIFS

# print string to file; note that the newline is required in the format string because printf wouldn't put a trailing delimiter (which is a good thing)
printf '%s\n' "$var" > file

An even simpler way to do this is:

delim=$'\n'
printf -v var "%s$delim" "${arr[@]}" # create a delimited string
var="${var%$delim}"                  # remove the trailing delimiter

Example

delim=:
arr=(one two three)
printf -v var "%s$delim" "${arr[@]}" # yields one:two:three:
var="${var%$delim}"                  # yields one:two_three
1
  • 2
    Downvoter, please comment what is wrong here. Jan 26, 2018 at 13:55
0

As said before, the following:

$ printf -v var "%s\n" "${System[@]}"

works, if you're converting to a string, but you have to deal with the single blank line at the end. I find you can avoid this by using command substitution instead, so

$ var=$(printf '%\n' "${System[@]}")

should work just fine. Just remember to quote var to get the required effect!

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .