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
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 5You 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 0awk -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.
0Above 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.
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[@]}" 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 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 1here 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}" } 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!