シェルスクリプトでバグを追跡中に、このコードスニペットで次の動作が見つかりました。
declare -a filelist
readarray filelist < <(ls -A)
readonly filelist
for file in "${filelist[@]}"; do
sha256sum ${filelist[$file]} | head -c 64
done
配列がfilelist
二重引用符で囲まれていない場合、コマンドは成功します。私はコーディングを改善するためにShellCheckを使用しており、次の提案を受けました。
二重引用符はワイルドカードとワードセパレータを防ぎます。
この場合、単語の分割について心配することはありませんが、他の多くの場合は心配するので、コードの一貫性を維持しようとします。ただし、配列を二重引用符で囲むと、コマンドは失敗します。コードを単一の要素に減らすと、次のような結果が表示されます。
bash-5.0# sha256sum ${filelist[0]} | head -c 64
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
bash-5.0# sha256sum "${filelist[0]}" | head -c 64
sha256sum: can't open 'file1
': No such file or directory
明らかに...二重引用符は使用できません。この場合、単語の分離は問題にならないからです。しかし、これからもそうかもしれないので、投稿をしたいと思いました。
私の質問は2つの部分で構成されています。
- 上記の二重引用符の配列に加えて、単語の分割を防ぐための「ベストプラクティス」方法はありますか?
配列の一重引用符はどこから来たのですか?編集:一重引用符はありません。一重引用符は、開けられないファイル名を表示するエラーです。
また、疑問に思われecho ${filelist[0]}
ない追加の改行文字が含まれるのはなぜですかecho "${filelist[0]}"
?
答え1
参照配列の拡張にはまったく問題はありません。
もちろん、結果を知って受け入れるならば、引用しなくても大丈夫です。引用符のない拡張子は、分割とワイルドカードの影響を受けます。そして、コードから${filelist[…]}
IFS文字を削除(文字列に<space>
、<tab>
またはが含まれている場合は分割<newline>
)を実行する必要があります。
これは引用されていない拡張が実行する作業であり、末尾<newline>
。
何作る問題は、使用するときにreadarray
各配列要素から末尾の区切り文字を削除しないことです。これにより、エラーメッセージに反映された末尾が保持されます
。<newline>
使用できるものは次のとおりです。
readarray -t filelist < <(ls -A)
この-t
オプションは、各ファイル名から末尾の改行をすべて削除します。
-t 読み取った各行から末尾の区切り文字(デフォルトの改行)を削除します。
しかし、コードには他の問題があります。
配列を宣言または消去する必要はありません
filelist
。これはデフォルトでreadarrayによって行われます。これは他の状況でも必要です。解析された出力は必要ありません
ls
。実際、これは悪い考えです。配列のファイルのリストを取得する最も簡単な方法は次のとおりです。filelist=( ./* )
そして、より良い結果を得るためにディレクトリを使用しないことをお勧めします。
for file in ./*; do [[ -f $file ]] && filelist+=( "$file" ) done
ループ内では
$file
var値を使用する必要があります。for file in "${filelist[@]}"; do sha256sum "$file" | head -c 64 done
for file in "${!filelist[@]}"; do
リストを使用しない限り鍵ソート。リスト全体を処理するだけです。一つsha256sumに電話してください。
sha256sum "${filelist[@]}" | cut -c -64
改善されたスクリプトは次のとおりです。
filelist=() # declare filelist as an array and empty it.
for file in ./*; do
if [[ -f $file ]]; then
filelist+=( "$file" )
fi
done
declare -r filelist # declare filelist as readonly.
sha256sum "${filelist[@]}" | cut -c -64
答え2
この場合、噴射について心配しない
まあ、実際にはそれに頼る配列項目から末尾の改行を削除してください!
バッシュのreadarray
(mapfile
)区切り文字はデフォルトで保持されます。マニュアルページやコマンドラインヘルプには明確に記載されていないようですが、オプションがあります。削除する区切り文字はデフォルトでは削除されません。
-t Remove a trailing delim (default newline) from each line read.
したがって、配列の実際の文字列は次のようになります。file1[newline]
引用符がない場合、単語の区切りは末尾のスペースを削除して改行を修正します。ただし、ファイル名にスペースが含まれていると、トークン化によって通常どおりスペースが混乱します。二重引用符で囲まれた配列はこれを防ぎます。最初の質問に答えるには、ベストプラクティスは二重引用符です。、ここには必要ない追加の改行文字があります。
$@
(少し混乱した例外は、二重引用符で囲まれた配列です。ここで、二重引用符で囲まれた文字列は、各配列要素に対して1つずつ複数の単語を生成します。)
コマンドライン${filelist[$file]}
でも使用できます。sha256sum
これはうまくいきません。file
インデックスではなく、配列から受け取った値がすでに含まれています。
最小限の修正で次のように動作できます。
declare -a filelist
readarray -t filelist < <(ls -A)
readonly filelist
for file in "${filelist[@]}"; do
sha256sum "$file" | head -c 64
done
declare
(私は明示的なものも本当に必要だとは思わない。)
ls
上記の問題はこの内容とは関係ありません。ファイル名を1行に1つずつファイルに保存し、このオプションを使用せずにreadarray
/を使用してmapfile
ファイルを読み取ると、-t
同じ問題が発生します。 (またはの出力を読む場合find
、この場合には使用できますfind -exec
。)
もちろん、これははい役に立たない使用ls
のいくつかのバージョンは、ls
出力からファイル名を破損する可能性があります。 (私の考えでは、GNU lsがパイプに出力するときにこれをしないようです。)
Bashでは、配列をglobで埋めることができます。
shopt -s dotglob
filelist=(*)
for file in *; do ...
あるいは、配列に保存せずにglobでループを実行します。
shopt -s dotglob
for file in *; do ...
必須であることを参照してくださいshopt -s dotglob
*
いくつかの書類を入手するには、シェルによって異なります。
答え3
コードスニペットに基づく問題の1つは解析中ですls
。これは危険で多くの問題に満ちているので、避けるのが最善です。
代わりに
declare -a filelist
readarray filelist < <(ls -A)
readonly filelist
for file in "${filelist[@]}"; do
よりシンプルで安全です!
for file in *; do
この場合:
for file in *; do
sha256sum "${file}" | head -c 64
done
readarray
また、改行を含む呼び出し時に渡されたリテラルデータを保存するのにも役立ちます。したがって、引用された値をエコーすると改行文字が保持されます。引用しないと、シェルはそれをトークン間のスペースとして無視します。これがsha256sum
失敗の理由でもあります。名前付きファイルがある場合は、foo
そのファイルに対応しないreadarray
値を渡してください。foo\n
一部の変数値が誤って削除されたため、これを逆参照すると問題が「修正」されます。