Yotam
Yotam

Reputation: 10685

Bash, splitting string fail

Following what I found here, I tried to split the list of files of a certain pattern to an array. However, the string doesn't split (The empty line is just to ease on the reading).

bash-3.2$ files=jfn240_463.o*
bash-3.2$ echo $files
jfn240_463.o9017306 jfn240_463.o9075989 jfn240_463.o9281439 jfn240_463.o9287196

bash-3.2$ IFS=' ' read -ra ll <<< $files
bash-3.2$ echo $ll
jfn240_463.o9017306 jfn240_463.o9075989 jfn240_463.o9281439 jfn240_463.o9287196

bash-3.2$ IFS=' ' read -ra ll <<< "$files"
bash-3.2$ echo $ll
jfn240_463.o9017306 jfn240_463.o9075989 jfn240_463.o9281439 jfn240_463.o9287196

bash-3.2$ echo ${ll[@]}
jfn240_463.o9017306 jfn240_463.o9075989 jfn240_463.o9281439 jfn240_463.o9287196

bash-3.2$ echo ${ll[1]}

bash-3.2$ 

I was sure I would get an array, so why won't I?

Edit:

I tried to use find instead without much success.

bash-3.2$ files=$(find jfn240_463.o*)
bash-3.2$ echo ${files[0]}
jfn240_463.o9017306 jfn240_463.o9075989 jfn240_463.o9281439 jfn240_463.o9287196
bash-3.2$ 
bash-3.2$ IFS=' ' read -ra ll <<< $files
bash-3.2$ echo ${ll[@]}
jfn240_463.o9017306
bash-3.2$ 

Upvotes: 1

Views: 150

Answers (2)

rici
rici

Reputation: 241881

Ignacio Vazquez-Abrams has the problem precisely correct: when you define:

$ files=jfn240_463.o*

the shell does not expand the * ("pathname expansion"). When you write:

$ echo $files

the shell does expand the *. To see the real value of $files, you need to write:

$ echo "$files"
jfn240_463.o*

So $files does not have a space in it.

You might expect the same to apply to <<<, but it turns out that bash does not distinguish between <<<$files and <<<"$files", because bash does not do pathname expansion on the word following <<<. You can see this by typing, for example:

$ cat <<<jfn240_463.o*
jfn240_463.o*

(There is actually a small difference between <<<$files and <<<"$files". Bash performs parameter expansion, command expansion and arithmetic expansion on the word following <<<, in this case $files. If the word is not quoted, it also performs word-splitting. However, after the word-splitting is finished, the words are joined together again, separated by a single space. Consequently, in the unquoted case, all sequences of whitespace are reduced to a single space, whereas in the quoted case the white space is retained precisely. The difference is most visible when $IFS has been changed to something other than whitespace.)

There are various possible workarounds, of which the simplest is to simply set the array directly, as Ignacio Vasquez-Abrams suggests:

ll=(jfn240_463.o*)

(In this syntax, bash does perform pathname expansion.)

However, that will fail if any file has whitespace in its name. Another simple solution (with bash), if no file has a newline in its name, is to use mapfile:

mapfile -t ll < <(ls jfn240_463.o*)

or, with the bash builtin printf:

mapfile -t ll < <(printf "%s\n" jfn240_463.o*)

It's by no means simple to get this to work in the case where a filename might contain arbitrary characters (other than \0 and /, which cannot appear in filenames).

Upvotes: 0

Ignacio Vazquez-Abrams
Ignacio Vazquez-Abrams

Reputation: 799150

The problem is that $files contains "jfn240_463.o*" and not a bunch of filenames delimited by spaces. You can't split that by a space since there's no space even in it. Use an array in the first place:

ll=(jfn240_463.o*)

Upvotes: 2

Related Questions