このコードはbash v4.4では実行されますが、bash v3.2では実行されないのはなぜですか?

このコードはbash v4.4では実行されますが、bash v3.2では実行されないのはなぜですか?

次のbashスクリプトがあります。

#!/bin/bash

encl0=( 0,0 0,1 0,2 0,3 0,4 0,5 0,7 0,8 0,9 0,10 0,11 0,12 0,13 0,14 0,15 )
MISSING_DISKS=()
OLDIFS=$IFS
IFS=$'\n'
MISSING_DISKS+=($({ printf '0 %s\n' {0..15}; printf '0 %s\n' "${encl0[@]#0,}"; } | sort | uniq -u))
IFS=$OLDIFS
echo "$({ printf '0 %s\n' {0..15}; printf '0 %s\n' "${encl0[@]#0,}"; } | sort | uniq -u)"
echo "${MISSING_DISKS[@]}"
if ((${#MISSING_DISKS[@]}>1)); then
    echo "Greater than 1"
else
    echo "Success"
fi

bash v4.4を使用して実行すると、期待どおりに動作します。

$ /usr/local/bin/bash test.sh
0 6
0 6
Success

ただし、bash v3.2を使用して実行すると、次のような結果は発生しません。

$ /bin/bash test.sh
0 6
0 0 0 0 1 2 3 4 5 7 8 9 10 11 12 13 14 15 0 1 0 10 0 11 0 12 0 13 0 14 0 15 0 2 0 3 0 4 0 5 0 6 0 7 0 8 0 9
Greater than 1

MISSING_DISKSこれを設定するコマンドの出力とは異なるものに設定する方法がわかりません。この問題の原因を知っている人はいますか?

ベストアンサー1

スペース、タブ、または改行の使用は常にほとんど失敗します。

ここで重要な質問が発生します。

encl0=( 0,0 0,1 0,2 0,3 0,4 0,5 0,7 0,8 0,9 )
IFS=$'\n'
printf '<0 %s>\n' "${encl0[@]#0,}"

Bash 3.2で実行している場合:

$ b32sh ./script
<0 0 1 2 3 4 5 7 8 9>

拡張は"${encl0[@]#0,}"値のリストではなく文字列として扱われます。

IFS に空白がある場合、または編集されていない配列の各値を拡張する場合、この問題は発生しません。

#!/bin/bash
encl0=( 0,0 0,1 0,2 0,3 0,4 0,5 0,7 0,8 0,9 )
IFS=$' \n'
printf '<0 %s>\n' "${encl0[@]#0,}"

実装する:

$ b32sh ./script
<0 0>
<0 1>
<0 2>
<0 3>
<0 4>
<0 5>
<0 7>
<0 8>
<0 9>

または:

#!/bin/bash
encl0=( 0,0 0,1 0,2 0,3 0,4 0,5 0,7 0,8 0,9 )
IFS=$'\n'
printf '<0 %s>\n' "${encl0[@]}"

実装する:

b32sh ./so
<0 0,0>
<0 0,1>
<0 0,2>
<0 0,3>
<0 0,4>
<0 0,5>
<0 0,7>
<0 0,8>
<0 0,9>

IFS=$OLDIFS エコラインをテストする前にIFSを復元するため、問題はスクリプトに隠されています。

この問題を回避する1つの方法は、printfでスペースを使用しないことです。

#!/bin/bash
encl0=( 0,0 0,1 0,2 0,3 0,4 0,5 0,7 0,8 0,9 0,10 0,11 0,12 0,13 0,14 0,15 )
MISSING_DISKS=()
OLDIFS=$IFS
IFS=$' \n'
MISSING_DISKS+=($({ printf '0x%s\n' {0..15}; printf '0x%s\n' "${encl0[@]#0,}"; } | sort | uniq -u))
echo "test $({ printf '0x%s\n' {0..15}; printf '0x%s\n' "${encl0[@]#0,}"; } | sort | uniq -u)"
echo "var  ${MISSING_DISKS[@]}"
if ((${#MISSING_DISKS[@]}>1)); then
    echo "Greater than 1"
else
    echo "Success"
fi
IFS=$OLDIFS

別のオプションは、代替配列を使用してIFSを改行に変更した後に代替拡張を防ぐことです。

#!/bin/bash
OLDIFS=$IFS
encl0=( 0,0 0,1 0,2 0,3 0,4 0,5 0,7 0,8 0,9 0,10 0,11 0,12 0,13 0,14 0,15 )
IFS=$' \n'; arr=("${encl0[@]#0,}")
MISSING_DISKS=()
IFS=$'\n'
MISSING_DISKS+=($({ printf '0 %s\n' {0..15}; printf '0 %s\n' "${arr[@]}"; } | sort | uniq -u))
echo "test $({ printf '0 %s\n' {0..15}; printf '0 %s\n' "${arr[@]}"; } | sort | uniq -u)"
echo "var  ${MISSING_DISKS[@]}"
if ((${#MISSING_DISKS[@]}>1)); then
    echo "Greater than 1"
else
    echo "Success"
fi
IFS=$OLDIFS

私はあなたに提供します:

  • 次の規則に従ってください。 CAPSの変数は環境変数です。
  • +=コードのこの時点で空の配列を増やす必要はありませんMISSING_DISKS+=
  • 後で IFS から空白を削除したくない場合は、printf で空白以外の文字を使用します。これにより、スクリプトがより強力になります。

これらの変更が行われると、スクリプトは次のようになります。

#!/bin/bash
oldIFS=$IFS
encl0=( 0,0 0,1 0,2 0,3 0,4 0,5 0,7 0,8 0,9 0,10 0,11 0,12 0,13 0,14 0,15 )
unset missing_disks

IFS=' '
           arr=($(printf '0-%s\n' "${encl0[@]#0,}"))
           arr+=($(printf '0-%s\n' {0..15}))

missing_disks=($(printf '%s\n' "${arr[@]}" | sort | uniq -u))

           echo "test $(printf '0-%s\n' "${arr[@]}" | sort | uniq -u)"
           echo "var  ${missing_disks[@]}"

((${#missing_disks[@]}>1)) && echo "Greater than 1" || echo "Success"
IFS=$oldIFS

おすすめ記事