I am trying to construct an array and write to a file in this format, i.e. the file's content should be something like this

hero_pairscore=( askdjfh sdf,sdlkfj lksf,dfgdf,dsflkgj,asdlkf ....)

Where the elements are separated by comma. I wrote the following code for this.

#set -x

while read line

$( IFS=",";hero_pairscore+=( "$line" )
done < true_pairscore.txt

echo "hero_pairscore=( ${hero_pairscore[@]} )" > embed.txt

But the resultant file contains only this hero_pairscore=( ). What is wrong with my code and how can I correct it to give the desired output?

  • Does the true_pairscore.txt file contain the "hero_pairscore=(" text?? – glenn jackman Apr 21 '14 at 20:38

All you need to do here is this:

  1. Set the variable. We'll use this:

    var="kijhg, fbjhku,,,ioy  fbjfr, kjmyhg"
  2. Set the $IFS and prevent filename expansion:

    IFS=, ; set -f
  3. set shell $@array to output.

    set -- $var
  4. Reset parent shell $IFS and shell params:

    unset IFS ; set +f
  5. Hooray! It even preserves multiple repeats and spaces and all!

    printf %s\\n "$@"


    ioy  fbjfr

Anyway, take my word for it. Every character that is not a , is preserved.


I especially recommend using the real shell array as opposed to the bash array because you can instantly transform its splitter.

printf %s "$*"

kijhg  fbjhku   ioy  fbjfr  kjmyhg %

IFS=, ; printf %s "$*"

kijhg, fbjhku,,,ioy  fbjfr, kjmyhg

Because $* has the special POSIX-specified quality of splitting the shell's positional parameter argument array on the first character of $IFS you can easily convert swathes of data as simply as that. You can "quote"-protect the argument array and still split it on any single character you like. As long as you get it right the first time, that is.


POSIX also specifies special qualities to "$@". Aside from the "$1"... eval "\${$#}" addressing forms for each argument, the "$@" parameter - when quoted - safely expands to all of the shell's arguments as they were last set regardless of the current value of $IFS. So if you want to add a list to the beginning of your array you do:

set -- $list "$@"

To the head/tail:

set -- $head "$@" $tail

To triple your array:

set -- "$@" "$@" "$@"


Unless your datastream is very large, the following might be a little quicker than your current operation (though, if you're working with files, you'd be far better off to eschew shell splitting altogether):

( set -f -- ; IFS=, ; while read -r line ; do set -- "$@" $line ; done
printf %s "$*" ) | cat >out


What I don't understand though is this - just what are you doing? Are you trying to replace newlines with commas? I mean - is the data already comma separated excepting only newlines? If so:

tr '\n' ',' <in >out

To do what I think you are trying to do, which is read every comma separated field in true_pairscore.txt into an array variable there is not much wrong with your code. The $( at the start of the IFS line is obviously wrong and would cause the script to have an error (maybe this is the reason you get no output). Other than that, the main fix for your code would be just to remove the quotes around $line since you want bash to do word splitting around IFS (usually this is what you use them to avoid):


set -o noglob

while IFS= read -r line
  hero_pairscore+=( $line )
done < true_pairscore.txt

echo "hero_pairscore=( ${hero_pairscore[@]} )" > embed.txt

set +o noglob

However, here is a trick to do the above in only two lines:

IFS=$',\n' read -ra hero_pairscore -d '' <true_pairscore.txt
echo "hero_pairscore=( ${hero_pairscore[@]} )" > embed.txt


If the embed.txt file is to be sourced in another bash shell, you probably want to quote each element of the written hero_pairscore array. To do this, replace the echo "hero_pairscore=( ${hero_pairscore[@]} )" with:

echo "hero_pairscore=( $(printf '"%s" ' "${hero_pairscore[@]}") )"

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.