How to return an array in bash without using globals?

后端 未结 18 2024
Happy的楠姐
Happy的楠姐 2020-11-29 21:36

I have a function that creates an array and I want to return the array to the caller:

create_array() {
  local my_list=(\"a\", \"b\", \"c\")
  echo \"${my_li         


        
18条回答
  •  死守一世寂寞
    2020-11-29 21:55

    [Note: the following was rejected as an edit of this answer for reasons that make no sense to me (since the edit was not intended to address the author of the post!), so I'm taking the suggestion to make it a separate answer.]

    A simpler implementation of Steve Zobell's adaptation of Matt McClure's technique uses the bash built-in (since version == 4) readarray as suggested by RastaMatt to create a representation of an array that can be converted into an array at runtime. (Note that both readarray and mapfile name the same code.) It still avoids globals (allowing use of the function in a pipe), and still handles nasty characters.

    For some more-fully-developed (e.g., more modularization) but still-kinda-toy examples, see bash_pass_arrays_between_functions. Following are a few easily-executable examples, provided here to avoid moderators b!tching about external links.

    Cut the following block and paste it into a bash terminal to create /tmp/source.sh and /tmp/junk1.sh:

    FP='/tmp/source.sh'     # path to file to be created for `source`ing
    cat << 'EOF' > "${FP}"  # suppress interpretation of variables in heredoc
    function make_junk {
       echo 'this is junk'
       echo '#more junk and "b@d" characters!'
       echo '!#$^%^&(*)_^&% ^$#@:"<>?/.,\\"'"'"
    }
    
    ### Use 'readarray' (aka 'mapfile', bash built-in) to read lines into an array.
    ### Handles blank lines, whitespace and even nastier characters.
    function lines_to_array_representation {
        local -a arr=()
        readarray -t arr
        # output array as string using 'declare's representation (minus header)
        declare -p arr | sed -e 's/^declare -a [^=]*=//'
    }
    EOF
    
    FP1='/tmp/junk1.sh'      # path to script to run
    cat << 'EOF' > "${FP1}"  # suppress interpretation of variables in heredoc
    #!/usr/bin/env bash
    
    source '/tmp/source.sh'  # to reuse its functions
    
    returned_string="$(make_junk | lines_to_array_representation)"
    eval "declare -a returned_array=${returned_string}"
    for elem in "${returned_array[@]}" ; do
        echo "${elem}"
    done
    EOF
    chmod u+x "${FP1}"
    # newline here ... just hit Enter ...
    

    Run /tmp/junk1.sh: output should be

    this is junk
    #more junk and "b@d" characters!
    !#$^%^&(*)_^&% ^$#@:"<>?/.,\\"'
    

    Note lines_to_array_representation also handles blank lines. Try pasting the following block into your bash terminal:

    FP2='/tmp/junk2.sh'      # path to script to run
    cat << 'EOF' > "${FP2}"  # suppress interpretation of variables in heredoc
    #!/usr/bin/env bash
    
    source '/tmp/source.sh'  # to reuse its functions
    
    echo '`bash --version` the normal way:'
    echo '--------------------------------'
    bash --version
    echo # newline
    
    echo '`bash --version` via `lines_to_array_representation`:'
    echo '-----------------------------------------------------'
    bash_version="$(bash --version | lines_to_array_representation)"
    eval "declare -a returned_array=${bash_version}"
    for elem in "${returned_array[@]}" ; do
        echo "${elem}"
    done
    echo # newline
    
    echo 'But are they *really* the same? Ask `diff`:'
    echo '-------------------------------------------'
    
    echo 'You already know how to capture normal output (from `bash --version`):'
    declare -r PATH_TO_NORMAL_OUTPUT="$(mktemp)"
    bash --version > "${PATH_TO_NORMAL_OUTPUT}"
    echo "normal output captured to file @ ${PATH_TO_NORMAL_OUTPUT}"
    ls -al "${PATH_TO_NORMAL_OUTPUT}"
    echo # newline
    
    echo 'Capturing L2AR takes a bit more work, but is not onerous.'
    echo "Look @ contents of the file you're about to run to see how it's done."
    
    declare -r RAW_L2AR_OUTPUT="$(bash --version | lines_to_array_representation)"
    declare -r PATH_TO_COOKED_L2AR_OUTPUT="$(mktemp)"
    eval "declare -a returned_array=${RAW_L2AR_OUTPUT}"
    for elem in "${returned_array[@]}" ; do
        echo "${elem}" >> "${PATH_TO_COOKED_L2AR_OUTPUT}"
    done
    echo "output from lines_to_array_representation captured to file @ ${PATH_TO_COOKED_L2AR_OUTPUT}"
    ls -al "${PATH_TO_COOKED_L2AR_OUTPUT}"
    echo # newline
    
    echo 'So are they really the same? Per'
    echo "\`diff -uwB "${PATH_TO_NORMAL_OUTPUT}" "${PATH_TO_COOKED_L2AR_OUTPUT}" | wc -l\`"
    diff -uwB "${PATH_TO_NORMAL_OUTPUT}" "${PATH_TO_COOKED_L2AR_OUTPUT}" | wc -l
    echo '... they are the same!'
    EOF
    chmod u+x "${FP2}"
    # newline here ... just hit Enter ...
    

    Run /tmp/junk2.sh @ commandline. Your output should be similar to mine:

    `bash --version` the normal way:
    --------------------------------
    GNU bash, version 4.3.30(1)-release (x86_64-pc-linux-gnu)
    Copyright (C) 2013 Free Software Foundation, Inc.
    License GPLv3+: GNU GPL version 3 or later 
    
    This is free software; you are free to change and redistribute it.
    There is NO WARRANTY, to the extent permitted by law.
    
    `bash --version` via `lines_to_array_representation`:
    -----------------------------------------------------
    GNU bash, version 4.3.30(1)-release (x86_64-pc-linux-gnu)
    Copyright (C) 2013 Free Software Foundation, Inc.
    License GPLv3+: GNU GPL version 3 or later 
    
    This is free software; you are free to change and redistribute it.
    There is NO WARRANTY, to the extent permitted by law.
    
    But are they *really* the same? Ask `diff`:
    -------------------------------------------
    You already know how to capture normal output (from `bash --version`):
    normal output captured to file @ /tmp/tmp.Ni1bgyPPEw
    -rw------- 1 me me 308 Jun 18 16:27 /tmp/tmp.Ni1bgyPPEw
    
    Capturing L2AR takes a bit more work, but is not onerous.
    Look @ contents of the file you're about to run to see how it's done.
    output from lines_to_array_representation captured to file @ /tmp/tmp.1D6O2vckGz
    -rw------- 1 me me 308 Jun 18 16:27 /tmp/tmp.1D6O2vckGz
    
    So are they really the same? Per
    `diff -uwB /tmp/tmp.Ni1bgyPPEw /tmp/tmp.1D6O2vckGz | wc -l`
    0
    ... they are the same!
    

提交回复
热议问题