Capturing multiple line output into a Bash variable

后端 未结 7 2152
忘掉有多难
忘掉有多难 2020-11-22 03:17

I\'ve got a script \'myscript\' that outputs the following:

abc
def
ghi

in another script, I call:

declare RESULT=$(./myscr         


        
相关标签:
7条回答
  • 2020-11-22 03:35

    After trying most of the solutions here, the easiest thing I found was the obvious - using a temp file. I'm not sure what you want to do with your multiple line output, but you can then deal with it line by line using read. About the only thing you can't really do is easily stick it all in the same variable, but for most practical purposes this is way easier to deal with.

    ./myscript.sh > /tmp/foo
    while read line ; do 
        echo 'whatever you want to do with $line'
    done < /tmp/foo
    

    Quick hack to make it do the requested action:

    result=""
    ./myscript.sh > /tmp/foo
    while read line ; do
      result="$result$line\n"
    done < /tmp/foo
    echo -e $result
    

    Note this adds an extra line. If you work on it you can code around it, I'm just too lazy.


    EDIT: While this case works perfectly well, people reading this should be aware that you can easily squash your stdin inside the while loop, thus giving you a script that will run one line, clear stdin, and exit. Like ssh will do that I think? I just saw it recently, other code examples here: https://unix.stackexchange.com/questions/24260/reading-lines-from-a-file-with-bash-for-vs-while

    One more time! This time with a different filehandle (stdin, stdout, stderr are 0-2, so we can use &3 or higher in bash).

    result=""
    ./test>/tmp/foo
    while read line  <&3; do
        result="$result$line\n"
    done 3</tmp/foo
    echo -e $result
    

    you can also use mktemp, but this is just a quick code example. Usage for mktemp looks like:

    filenamevar=`mktemp /tmp/tempXXXXXX`
    ./test > $filenamevar
    

    Then use $filenamevar like you would the actual name of a file. Probably doesn't need to be explained here but someone complained in the comments.

    0 讨论(0)
  • 2020-11-22 03:41

    Actually, RESULT contains what you want — to demonstrate:

    echo "$RESULT"
    

    What you show is what you get from:

    echo $RESULT
    

    As noted in the comments, the difference is that (1) the double-quoted version of the variable (echo "$RESULT") preserves internal spacing of the value exactly as it is represented in the variable — newlines, tabs, multiple blanks and all — whereas (2) the unquoted version (echo $RESULT) replaces each sequence of one or more blanks, tabs and newlines with a single space. Thus (1) preserves the shape of the input variable, whereas (2) creates a potentially very long single line of output with 'words' separated by single spaces (where a 'word' is a sequence of non-whitespace characters; there needn't be any alphanumerics in any of the words).

    0 讨论(0)
  • 2020-11-22 03:44

    Another pitfall with this is that command substitution — $() — strips trailing newlines. Probably not always important, but if you really want to preserve exactly what was output, you'll have to use another line and some quoting:

    RESULTX="$(./myscript; echo x)"
    RESULT="${RESULTX%x}"
    

    This is especially important if you want to handle all possible filenames (to avoid undefined behavior like operating on the wrong file).

    0 讨论(0)
  • 2020-11-22 03:45

    Parsing multiple output

    Introduction

    So your myscript output 3 lines, could look like:

    myscript() { echo $'abc\ndef\nghi'; }
    

    or

    myscript() { local i; for i in abc def ghi ;do echo $i; done ;}
    

    Ok this is a function, not a script (no need of path ./), but output is same

    myscript
    abc
    def
    ghi
    

    Considering result code

    To check for result code, test function will become:

    myscript() { local i;for i in abc def ghi ;do echo $i;done;return $((RANDOM%128));}
    

    1. Storing multiple output in one single variable, showing newlines

    Your operation is correct:

    RESULT=$(myscript)
    

    About result code, you could add:

    RCODE=$?
    

    even in same line:

    RESULT=$(myscript) RCODE=$?
    

    Then

    echo $RESULT 
    abc def ghi
    
    echo "$RESULT"
    abc
    def
    ghi
    
    echo ${RESULT@Q}
    $'abc\ndef\nghi'
    
    printf "%q\n" "$RESULT"
    $'abc\ndef\nghi'
    

    but for showing variable definition, use declare -p:

    declare -p RESULT
    declare -- RESULT="abc
    def
    ghi"
    

    2. Parsing multiple output in array, using mapfile

    Storing answer into myvar variable:

    mapfile -t myvar < <(myscript)
    echo ${myvar[2]}
    ghi
    

    Showing $myvar:

    declare -p myvar
    declare -a myvar=([0]="abc" [1]="def" [2]="ghi")
    

    Considering result code

    In case you have to check for result code, you could:

    RESULT=$(myscript) RCODE=$?
    mapfile -t myvar <<<"$RESULT"
    

    3. Parsing multiple output by consecutives read in command group

    { read firstline; read secondline; read thirdline;} < <(myscript)
    echo $secondline
    def
    

    Showing variables:

    declare -p firstline secondline thirdline
    declare -- firstline="abc"
    declare -- secondline="def"
    declare -- thirdline="ghi"
    

    I often use:

    { read foo;read foo total use free foo ;} < <(df -k /)
    

    Then

    declare -p use free total
    declare -- use="843476"
    declare -- free="582128"
    declare -- total="1515376"
    

    Considering result code

    Same prepended step:

    RESULT=$(myscript) RCODE=$?
    { read firstline; read secondline; read thirdline;} <<<"$RESULT"
    
    declare -p firstline secondline thirdline RCODE
    declare -- firstline="abc"
    declare -- secondline="def"
    declare -- thirdline="ghi"
    declare -- RCODE="50"
    
    0 讨论(0)
  • 2020-11-22 03:47

    In addition to the answer given by @l0b0 I just had the situation where I needed to both keep any trailing newlines output by the script and check the script's return code. And the problem with l0b0's answer is that the 'echo x' was resetting $? back to zero... so I managed to come up with this very cunning solution:

    RESULTX="$(./myscript; echo x$?)"
    RETURNCODE=${RESULTX##*x}
    RESULT="${RESULTX%x*}"
    
    0 讨论(0)
  • 2020-11-22 03:50

    In case that you're interested in specific lines, use a result-array:

    declare RESULT=($(./myscript))  # (..) = array
    echo "First line: ${RESULT[0]}"
    echo "Second line: ${RESULT[1]}"
    echo "N-th line: ${RESULT[N]}"
    
    0 讨论(0)
提交回复
热议问题