57

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?

74

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
| improve this answer | |
  • Thanks, is there a way to output this to a variable? – ACyclic Aug 16 '12 at 16:26
  • Updated to show how to capture to a variable. – chepner Aug 16 '12 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 '13 at 17:49
  • Need quotes the array variable name: $ IFS=', ' $ echo ${VAR[*]} first second third $ echo "${VAR[*]}" first,second,third – Seff Jun 16 '19 at 4:23
  • var=$( .. ) trims the trailing newline automatically; printf -v var doesn't – dave_thompson_085 Apr 23 at 6:09
31

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
| improve this answer | |
8
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.

| improve this answer | |
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.

| improve this answer | |
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[@]}"
| improve this answer | |
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
| improve this answer | |
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
| improve this answer | |
  • 2
    Downvoter, please comment what is wrong here. – codeforester Jan 26 '18 at 13:55
0

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}"
}
| improve this answer | |
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!

| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.