83

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?

Zombo
  • 1
ACyclic
  • 1,003

10 Answers10

105

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
chepner
  • 7,041
37

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
Daniel Beck
  • 111,893
rush
  • 1,299
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.

Meow
  • 297
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[@]}"
Zombo
  • 1
1

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
codeforester
  • 164
  • 1
  • 1
  • 8
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
jcayzac
  • 245
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}"
}
TacB0sS
  • 111
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!

0

The shortest and probably one of the most efficient solutions I can think of, is just redirecting the output from printf into a file:

printf '%s\n' "${array[@]}" > testfile