は?パラメータ拡張時に特別な方法で処理されますか?

は?パラメータ拡張時に特別な方法で処理されますか?

git status --porcelain -bプロンプトで使用するためにfromの出力を解析しようとしていますが、パラメータ拡張を実行すると奇妙な動作が発生します。

このコードは問題を示しています。

#!/bin/bash
IFS=$'\n'
touch ab
status_arr=( $(git status --porcelain -b) )
for (( i=0; i<${#status_arr[@]}; i++ )); do
    echo ${status_arr[$i]}
    echo ${status_arr[$i]:0:2}
done

きれいなgitディレクトリで実行すると、次のような結果が表示されます。

$ bash sandbox/statusline/issue.sh 
## master...origin/master
##
?? ab
ab

??出力の4行目にエコーが表示されることが予想され、実際にスクリプトをまたはtouch abcに変更するとtouch aこれが表示されます。

私はこれについて非常に混乱しており、bashで明白なものを見逃しているようですが、インターネット検索では役に立つものは得られません。

これが既知の「もの」である場合、それを回避するか完全に避ける方法はありますか?

答え1

?ファイル名を一致させるために使用されるシェルグローバル文字。単一文字と一致します。したがって、名前のファイルがあるため、abこの??パターンはそのファイルと一致します。

これが起こる理由は、パラメータ拡張がいいえリーダー。

答え2

引用符のない変数またはコマンドの置換は文字列として解釈されるのではなく、ファイル名のワイルドカードパターンのリストとして解釈されます。つまり、変数値またはコマンド出力は、文字で区切られた別々の部分に分割されますIFS(このステップはフィールド分割と呼ばれます)。その後、各部分はワイルドカードパターンとして解釈され、パターンが一部のファイルと一致する場合、一致するファイル名のリストに置き換えられます。それ以外の場合は、パターンは変更されていません(このステップはファイル名の生成と呼ばれます)。

たとえば、改行のみを含み、パターンに一致するファイルがないため、5文字の文字列を含む単一要素の配列にstatus_arr=( $(git status --porcelain -b) )設定します。デフォルト値にスペースが含まれている場合は、2文字列が2回表示される2要素の配列に設定されます。status_arr?? ab'IFS?? abIFSstatus_arrab

変数またはコマンドの置換が二重引用符で囲まれている場合、結果の文字列はそのまま使用されます。フィールド分割とファイル名の生成は、引用符のない置換でのみ機能します。

を実行してファイル名の生成を完全に無効にできますset -f。これは分割を利用したい場合に便利ですIFS。置き換えられた出力set -fだけでなく、ファイル名の生成も完全に無効になることに注意してください。set -f; echo **

#!/bin/bash
IFS=$'\n'
set -f
touch ab
status_arr=( $(git status --porcelain -b) )
for (( i=0; i<${#status_arr[@]}; i++ )); do
    echo "${status_arr[$i]}"
    echo "${status_arr[$i]:0:2}"
done

(ここでファイル名の生成はまだ無効になっており、考慮される要素は構文に文字をstatus_arr含めることができないため、ステートメントから二重引用符を省略するのは安全です。しかしこれは非常に脆弱です。 、. の状態と値はそれ以降は変更されません。IFSechoset -fIFS

関連情報