Gitを使用して、ディレクトリ内のファイルに対するすべての段階的でない修正のbash配列を取得しようとしています。次のコードは、ディレクトリで変更されたすべてのファイルを印刷します。
git -C $dir/.. status --porcelain | grep "^.\w" | cut -c 4-
この印刷
"Directory Name/File B.txt"
"File A.txt"
使ってみよう
arr1=($(git status --porcelain | grep "^.\w" | cut -c 4-))
しかし、その後
for a in "${arr1[@]}"; do echo "$a"; done
${arr1[@]}
(周辺に引用符が付いたりなしで印刷されます)
"Directory
Name/File
B.txt"
"File
A.txt"
私も試しました
git -C $dir/.. status --porcelain | grep "^.\w" | cut -c 4- | readarray arr2
しかし、その後
for a in "${arr2[@]}"; do echo "$a"; done
(引用符を含めて除く${arr2[@]}
)何も印刷しません。あらかじめ使用してもdeclare -a arr2
効果はありません。
私の質問は:この値を配列としてどのように読み取ることができますか? (これは私のアルゴスプラグインで使用されます。Git バー、もし私のコードをすべて見ることができるように)。
答え1
長い話を短く
バッシュから:
readarray -t arr2 < <(git … )
printf '%s\n' "${arr2[@]}"
あなたの質問には2つの異なる質問があります
殻が壊れています。
これを行うとき:arr1=($(git … ))
「コマンド拡張」には引用符がないため、シェル分割とglobの影響を受けます。
シェル分割が何をするのかを正確に見るには、printfを使用してください。
$ printf '<%s> ' $(echo word '"one simple sentence"') <word> <"one> <simple> <sentence">
これは次の方法で回避できます。引用する:
$ printf '<%s> ' "$(echo word '"one simple sentence"')" <word "one simple sentence">
しかし、これはまた所望の改行分割を防止する。
パイプライン
実行時:git … | … | … | readarray arr2
配列変数
arr2
が設定されていますが、|
パイプ()が閉じると消えます。最後のサブシェルに滞在している場合は、次の値を使用できます。
$ printf '%s\n' "First value." "Second value." | { readarray -t arr2; printf '%s\n' "${arr2[@]}"; } First value. Second value.
arr2
しかし、パイプラインを離れた後は、その価値は維持されません。
解決策
改行を分割するには読み取りを使用する必要がありますが、パイプは使用できません。
古いものから新しいものまで:
リングの形。
配列のない古いシェルの場合(位置引数を持つ唯一の準配列):set -- while IFS='' read -r value; do set -- "$@" "$value" done <<-EOT $(printf '%s\n' "First value." "Second value.") EOT printf '%s\n' "$@"
配列設定(ksh、zsh、bash)
i=0; arr1=() while IFS='' read -r value; do arr1+=("$value") done <<-EOT $(printf '%s\n' "First value." "Second value.") EOT printf '%s\n' "${arr1[@]}"
Here-string here document() の代わりに here-string() を使用できます。<<
<<<
i=0; arr1=() while IFS='' read -r value; do arr1+=("$value") done <<<"$(printf '%s\n' "First value." "Second value.")" printf '%s\n' "${arr1[@]}"
プロセスの置き換え
これをサポートするシェル(ksh、zsh、bash)では、これを使用して<( … )
ここで文字列を置き換えることができます。i=0; arr1=() while IFS='' read -r value; do arr1+=("$value") done < <(printf '%s\n' "First value." "Second value.") printf '%s\n' "${arr1[@]}"
違いは
<( )
NULバイトをエクスポートする機能ですが、文字列はNULを削除(または警告)できます。デフォルトでは、ここでは末尾の改行文字が文字列に追加されます。 AFAIK他に何かあるかもしれません。readarrayは次の目的で
使用されます。readarray
bash
[ㅏ](別名mapfile
)ループを避ける:readarray -t arr2 < <(printf '%s\n' "First value." "Second value.") printf '%s\n' "${arr2[@]}"
[ㅏ]kshでは、使用する前に変数を消去する必要がありますread -A
が、改行文字を分割して入力全体を一度に読むには少し「魔法」が必要です。
IFS=$'\n' read -d '' -A arr2 < <(printf '%s\n' "First value." "Second value.")
必要です。mapfile
zshからモジュールをロードする似たようなことをしてみてください。
答え2
readarray にパイプすると、arr2 配列を正しく埋めるサブシェルが起動してから終了します。 readarrayへの入力としてプロセス置換を使用します。
readarray -t arr2 < <(git ...)
答え3
あなたは近いです
これは「空白文字を含むファイル名」の問題です。
デフォルトでは、区切り文字は空白文字です。これはIFS
環境変数によって設定されます。
環境変数を一時的に変更するには、次のコマンドを使用します。
ifs_backup=$IFS
IFS=$(echo -en "\n\b")
その後、このコマンドの出力は次のようになります。
for a in "${arr1[@]}"; do echo "$a"; done
する:
"Directory Name/File B.txt"
"File A.txt"
回復IFS
:
IFS=$ifs_backup
答え4
コードの内部部分に引用符を追加します。
arr1=("$(git status --porcelain | grep "^.\w" | cut -c 4-)")