関数が返すときに長さが異なるのはなぜですか?

関数が返すときに長さが異なるのはなぜですか?

次に、この長さが異なる配列を提供し、さらに返す理由をご存知ですか"${distro[@]}"

禁煙健康増進協会

#!/bin/bash

. ./two.sh
    
hello(){
  arr=$(get_array)
  echo "Length arr:" "${#arr[@]}" # array is not preserved and returns 1, but why?
  show_length # returns length as correct 3
}

"$@"

2.sh

#!/bin/bash

get_array(){
  distro=("redhat linux" "debian linux" "gentoo linux")
  echo "${distro[@]}"
}

show_length(){
  distro=("redhat linux" "debian linux" "gentoo linux")
  echo "Length distro:  ${#distro[@]}"
}

なぜ:

sh ./one.sh hello
Length arr: 1
Length distro:  3

答え1

のある関数では配列を返すことはできませんecho "${distro[@]}"。それがすることは関数の標準出力で印刷するだけです。これは構造化されていないバイトストリームにすぎず、他の配列要素を分離することはできません。

実際に起こるのは、"${distro[@]}"さまざまなパラメータに展開され、すべてに渡され、印刷され、echoスペースechoに関連付けられていることです。効果的に3要素配列(redhat linux、、、)を単一の文字列に変換します。debian linuxgentoo linuxredhat linux debian linux gentoo linux

これに応じて、stdoutを文字列として読み込み、.(配列割り当てが含まれ、デフォルト設定を使用して生成されたトークン化とワイルドカードのコマンド置換が適用されます)に割り当てるスカラー割り当てもありarr=$(get_array)ます。get_arrayarrarr=( $(get_array) )6つの要素この出力の配列(redhat、、、、、、、)です。)linuxdebianlinuxgentoolinux


回避策は、改行文字を区切り文字として配列要素を印刷し、通常のトークン化よりもスマートな方法を使用して反対側の配列に読み込むことです。名前配列を内部関数に渡し、その中にデータを格納するようにします。

readarray(Bash 4.0 ベース) を使用して、1 行に 1 つの値を印刷します。

#!/bin/bash
foo() {
    local arr=("foo bar" "doo")
    printf "%s\n" "${arr[@]}"
}
readarray -t arr2 < <(foo)
printf "read %d elements: " "${#arr2[@]}"
printf "<%s> " "${arr2[@]}"
printf "\n"

ただし、配列が空の場合は改行printf文字が印刷され続け、反対側の端の結果は要素の配列になります。

あるいは、NULバイトを区切り文字として(Bash 4.4以降)、任意の配列要素を渡し、空の配列の問題を解決することもできます。

#!/bin/bash
foo() {
    local arr=("foo bar" $'new\nline')
    if (( ${#arr[@]} )); then
        printf "%s\0" "${arr[@]}"
    fi
}
readarray -t -d '' arr2 < <(foo)
# use declare -p for unambiguous shell-quoted output
declare -p arr2   

または、変数名を渡してnamerefを使用してください。

#!/bin/bash
bar() {
    local arr=("foo bar" "doo")
    declare -n _name="$1"
    _name=("${arr[@]}")
}
bar arr2
printf "got %d elements: " "${#arr2[@]}"
printf "<%s> " "${arr2[@]}"
printf "\n"

関連情報