How to split break lines with Bash scripts?
Asked Answered
C

3

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?

Chelicera answered 11/5, 2019 at 23:48 Comment(3)
What is your expected output?Interference
Hopefully this was just an academic example but make sure to read mywiki.wooledge.org/ParsingLs and 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.Lucilelucilia
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.Subeditor
P
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.

Prissy answered 12/5, 2019 at 0:18 Comment(2)
Wouldn't ls -la | while read line; do echo "$line"; done do it, too?Clubbable
@U.Windl possibly, but the OP was asking what was wrong with his code.Prissy
Y
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)
Yoshikoyoshio answered 12/5, 2019 at 0:27 Comment(0)
I
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
Interference answered 12/5, 2019 at 0:35 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.