3

Here's my bash function to get a command's output as parameter and then return an array of output lines.

function get_lines { 
    while read -r line; do 
        echo $line
    done <<< $1
}

SESSIONS=`loginctl list-sessions`
get_lines "$SESSIONS"

Actual output of loginctl list-sessions is:

   SESSION        UID USER             SEAT            
        c2       1000 asif             seat0           
        c7       1002 sadia            seat0           

But the while loop only runs once printing all output in a single line. How can I get an array of lines and return it?

3 Answers 3

3

You could use readarray and avoid the get_lines function:

readarray SESSIONS < <(loginctl --no-legend list-sessions)

this create the array SESSIONS with each line of the output of the command mapped to an element of the array.

Sign up to request clarification or add additional context in comments.

2 Comments

++, but you should add -t so as not to capture the trailing \n along with each input line; readarray requires Bash 4.x.
Looks great. I don't get < <( part. What does it do?
1

The value of this answer is in explaining the problem with the OP's code.
- The other answers show the use of Bash v4+ builtin mapfile (or its effective alias, readarray) for directly reading input line by line into the elements of an array, without the need for a custom shell function.
- In Bash v3.x, you can use IFS=$'\n' read -r -d '' -a lines < <(...),, but note that empty lines will be ignored.


Your primary problem is that unquoted (non-double-quoted) use of $1 makes the shell apply word-splitting to its contents, which effectively normalizes all runs of whitespace - including newlines - to a single space each, resulting in a single input line to the while loop.

Secondarily, using $input unquoted applies this word-splitting again on output with echo.

Finally, by using read without setting $IFS, the internal field separator, to the empty string - via IFS= read -r line - leading and trailing whitespace is trimmed from each input line.

That said, you can simplify your function to read directly from stdin rather than taking arguments:

function get_lines { 
    while IFS= read -r line; do 
        printf '%s\n' "$line"
    done
}

which you can then invoke as follows, using a process substitution:

get_lines < <(loginctl list-sessions)

Using a pipeline would work too, but get_lines would then run in a subshell, which means that it can't set variables visible to the current shell:

loginctl list-sessions | get_lines

Comments

1

here's a way in bash v4+:

SESSIONS=`loginctl list-sessions`
mapfile -t myArray <<< "$SESSIONS"

ref:

Creating an array from a text file in BASH with mapfile

1 Comment

++; worth noting that mapfile (and its effective alias, readarray) require Bash 4.x.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.