6

If you have a string with a delimiter, let's say a , character, you could use IFS just like that:

text=some,comma,separated,text
IFS="," read -ra ADDR <<< "$text"

for i in ${ADDR[@]}
do
    echo $i
done

Each word will be printed in a new line. But if you grab the result of command like ls and then try to split it on the \n you don't get to the same result:

results=$(ls -la)
IFS="\n" read -ra ADDR <<< "$results"

for i in ${ADDR[@]}
do
    echo $i
done

It only prints 2 lines, and they are not even the file entries. It is

total
36

The first line of the ls command output.

Can someone give a little help? If it is not the correct way, how is that?

John Kugelman
  • 349,597
  • 67
  • 533
  • 578
Victor Ferreira
  • 6,151
  • 13
  • 64
  • 120
  • What is your expected output? – Hai Vu May 12 '19 at 00:24
  • 1
    Hopefully this was just an academic example but make sure to read https://mywiki.wooledge.org/ParsingLs and [why-is-using-a-shell-loop-to-process-text-considered-bad-practice](https://unix.stackexchange.com/questions/169716/why-is-using-a-shell-loop-to-process-text-considered-bad-practice) before doing any more parsing `ls` output or writing shell loops to manipulate text. – Ed Morton May 12 '19 at 05:49
  • `IFS="\n"` adds the two characters ``\`` and `n` to `IFS`, not a single newline character. You would want `IFS=$'\n'`, but this is not a good way to iterate over the files in a directory as Ed Morton points out. – chepner May 13 '19 at 13:27

3 Answers3

6

read usually reads until it reaches newline, unless you tell it otherwise using -d.

In this example, we use -d $'\0'. This has the shell read until it reaches a null character (which it won't in the output of ls). Then IFS=$'\n' causes the shell to split on newline and assign each line as an array element. Note the use of $'...' instead of "..." to interpret the escape sequences.

results=$(ls -la)

IFS=$'\n' read -ra ADDR -d $'\0' <<< "$results"

for i in "${ADDR[@]}"
do
    echo "$i"
done

Last but not least, we have to quote both the substitutions of the array and $i.

John Kugelman
  • 349,597
  • 67
  • 533
  • 578
Michael Jaros
  • 4,586
  • 1
  • 22
  • 39
2

Use readarray or mapfile to read many lines into an array. Much simpler. Make sure to quote the variable expansions in the loop as well.

results=$(ls -la)
readarray -t ADDR <<< "$results"

for i in "${ADDR[@]}"
do
    echo "$i"
done

Or skip the $results variable:

readarray -t ADDR < <(ls -la)
John Kugelman
  • 349,597
  • 67
  • 533
  • 578
0

I'm not sure what kind of output you are looking for, but guessed that you are looking for each line broken into tokens. Here is one way:

/bin/ls -la | while read -ra line
do
    # Whole line
    echo "Line: >${line[@]}<"

    # Break each line into tokens
    for i in ${line[@]}
    do
        echo "- $i"
    done
done

Notes

  • The first line takes the output of ls -la and feeds into the while loop, reading each line as an array
  • Inside the while loop, we can deal with line as a whole, or as individual tokens

Output Excerpt

Line: >-rw-r--r-- 1 haiv staff 142 May 8 10:56 star.md<
- -rw-r--r--
- 1
- haiv
- staff
- 142
- May
- 8
- 10:56
- star.md
Line: >drwxr-xr-x 7 haiv staff 224 May 2 10:26 tailstring<
- drwxr-xr-x
- 7
- haiv
- staff
- 224
- May
- 2
- 10:26
- tailstring
Hai Vu
  • 37,849
  • 11
  • 66
  • 93