109

If inner.sh is

#...
echo first
echo second
echo third

And outer.sh is

var=`./inner.sh`
# only wants to use "first"...  

How can var be split by whitespace?

3
  • 1
    Why not to use for word in $var; do ? Commented Dec 11, 2012 at 16:01
  • $EarlGray I would guess bash has a more direct way. Commented Dec 11, 2012 at 16:19
  • this reminded me of bash "emulation" of string(3) here. I'm sure you'll find a strtok() there :) Commented Dec 11, 2012 at 16:21

3 Answers 3

155

Try this:

var=($(./inner.sh))

# And then test the array with:

echo ${var[0]}
echo ${var[1]}
echo ${var[2]}

Output:

first
second
third

Explanation:

  • You can make an array in bash by doing var=(first second third), for example.
  • $(./inner.sh) runs the inner.sh script, which prints out first, second, and third on separate lines. Since we don't didn't put double quotes around $(...), they get lumped onto the same line, but separated by spaces, so you end up with what it looks like in the previous bullet point.
Sign up to request clarification or add additional context in comments.

6 Comments

check out the last answer, this method was good for the question, but if you need something more (like keeping the separators in tact, calling a function automatically after each line is read, etc) then mapfile by far is more powerful + it's super efficient, ive tested this with many programs and i've never had to wait long even for files that get to be in 100,000's of lines. Usually it's instant assuming you are using local storage :)
This answer actually split words not lines into array elements. Technically it answer the question asked, but not the more general case. Test case : var=( $( echo "1 2 3" ; echo "4 5 6" ) ) ; echo ${var[0]} I expected "1 2 3", it yields "1". Depends on your use case. mapfile does what I need.
@sampson-chen how do I reference a range subset of elements? var[0:2] doesn't work
as @StéphaneGourichon pointed out, this splits words not lines, so this does not work with empty lines, they are simply ignored which effectively screws up the order of lines in the array
Didn't work for me. I had to prefix the var=( ... command with IFS=$'\n' and in the next line unset the internal field separator of bash with unset IFS.
|
67

Don't forget the builtin mapfile. It's definitely the most efficient in your case: If you want to slurp the whole file in an array, the fields of which will be the lines output by ./inner.sh, do

mapfile -t array < <(./inner.sh)

Then you'll have the first row in ${array[0]}, etc...

For more info on this builtin and all the possible options:

help mapfile

If you just need the first line in a variable called firstline, do

read -r firstline < <(./inner.sh)

These are definitely the most efficient ways!

This small benchmark will prove it:

$ time mapfile -t array < <(for i in {0..100000}; do echo "fdjsakfjds$i"; done)

real    0m1.514s
user    0m1.492s
sys 0m0.560s
$ time array=( $(for i in {0..100000}; do echo "fdjsakfjds$i"; done) )

real    0m2.045s
user    0m1.844s
sys 0m0.308s

If you only want the first word (with space delimiter) of the first line output by ./inner.sh, the most efficient way is

read firstword _ < <(./inner.sh)

15 Comments

imo: the best answer here, mapfile by far is faster than the other methods, i was working with output lines > 14,000 entries, mapfile worked almost instantaneously rather than using an external program, i use the rule of thumb to never involve other programs to do the work that the language is supposed to do for itself, and bash is no exception, that rule goes double when working with variables.
WTF is mapfile?_ brew info mapfile || echo "found $(brew desc -s mapfile | wc -l) references to mapfile" -> Error: No available formula for mapfile found 0 references to mapfile let me go on... sh-3.2$ mapfile -> sh: mapfile: command not found
@alexgray mapfile is a Bash≥4 builtin. Not available in POSIX shells or Bash<4. Seems you're running 3.2; you're screwed. Unless you install a more recent version of Bash.
This answer indeed splits lines into an array. Test case: mapfile -t var < <( echo "1 2 3" ; echo "4 5 6" ) ; echo ${var[1]} correctly yields 4 5 6.
@ssc That's to be sure that you're using the correct version of Bash when calling mapfile — just because it's a common source of error on OS X. And I have no idea why your error is prefixed with -bash. Sorry, I don't have OS X available. Moreover, there are lots of people using mapfile on OS X around here, so I suspect that you're the one doing something wrong. I was just trying to help you, starting with the basic, suggesting that you double check the version number. Now I'm sorry I can't be of any further help.
|
11

You're not splitting on whitespaces but rather on newlines. Give this a whirl:

IFS=$'
'

var=$(./echo.sh)
echo $var | awk '{ print $1 }'
unset IFS

3 Comments

how do I then access the elements of var?
newvar = $(echo $var | awk '{ print $1 }') and access $newvar
thanks, I looked for this and found your nice snippet. You can also write IFS=$'\n' for better readability, and put the two commands on one line without ; between them like: "IFS=$'\n' var=$(./echo.sh)" so you do not need to unset IFS afterwards

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.