私は長い間tcshユーザーから新しいbashユーザーに移行しています(かなり遅れています)。定期的にtcshでたくさんのforeachループを書いたので、代わりにbashのforループの構文を学びましたが、一致しないglobパターンがリテラル文字列としてループを通過したときに驚きました。リテラル文字列がスキップされてshopt -s nullglob
が見つかるように、この動作を変更する方法を探しました。私の理解では、これは理論的にはtcshの動作と同等であるはずですが、今日私は違いを発見しました。 ls ../*.doesnotmatch
を実行すると、結果は現在のディレクトリの内容のリストになりました。具体的には、私はこれを行いました:
bash:
$ shopt -s nullglob
$ ls ../*.sam
extractSplitReads_BwaMem extractSplitReads_BwaMem.xml
$ shopt -u nullglob
$ ls ../*.sam
ls: ../*.sam: No such file or directory
親ディレクトリには、*.sam
に一致するものはなく、特に現在のディレクトリはありません。最初は本当に混乱していましたが、グロブパターンが消えており、引数を指定していないかのようにコマンドが実行されていることに気付きました。例:
$ ls
そこで、failglobを単独で、およびnullglobを使用して設定しようとしましたが、failglobが設定されている限り、一致するパターンが存在するかどうかに関係なく、一致しないglobパターンはコマンドを強制終了します。
bash:
$ shopt -s failglob
$ shopt -s nullglob
$ ls ../vis*/*.xml
../visualization/LAJ.xml
$ ls ../vis*/*.xml ../*.sam
bash: no match: ../*.sam
$ ls ../{vis*/*.xml,*.sam}
bash: no match: ../*.sam
Tcshを使用している場合、すべてのグロブは一致したものだけに要約され、一致するものがない場合は、グロブエラーが発生します。
tcsh:
$ ls ../vis*/*.xml ../*.sam
../visualization/LAJ.xml
$ ls ../{vis*/*.xml,*.sam}
../visualization/LAJ.xml
$ ls ../*.sam
ls: No match.
ショップの設定を確認しましたが、この動作を取得する方法がわかりません。私は何かが足りないのですか? tcshと同じようにグロブを処理するbashまたはtcsh以外に、別の最新のシェルはありますか?何かが一致したときのnullglobの動作が必要ですが、何も一致しないときのfailglobの動作は、tcshの動作のようです。
ファイル名の展開に関連するshopt
オプションはdotglob
、failglob
、nocaseglob
、nullglob
のみであり、いずれも(単独または組み合わせて)ありません。 )あなたが望むことを正確に行っているようです。それは本当に良い考えのように聞こえるので、それは残念です。
インタラクティブセッションでfailglob
を設定することをお勧めします。そうすることで、次のような望ましくない可能性のあるコマンドを回避できます。
mv -r file1 file2 dir1 dir2 destination-*-dir
ここで、file1
が何にも一致せず、nullglob
が設定されている場合、file2
、dir1
、およびdir2
はdestination-*-dir
に移動されます。
一方、シェルスクリプトを作成するときにファイル名の展開に完全に依存することはお勧めできません。そのような拡張が存在するかどうか、そしてそれらが想定されているものであるかどうかを常に検証することをお勧めします。
つまり、これを行う代わりに:
rm -- *.jpg *.txt
このようなことをする方が良いです:
for file in *.jpg *.txt; do
if [ -f "${file}" ]; then
rm -- "${file}"
fi
done
# Or this (non-POSIX, as it uses an array)
for file in *.jpg *.txt; do
if [ -f "${file}" ]; then
files_to_delete+=( "${file}" )
fi
done
if [ "${#files_to_delete[@]}" -gt 0 ]; then
rm -- "${files_to_delete[@]}"
fi
そうすれば、たとえば、一部のファイルが*.txt
に一致していても、実際にはディレクトリである場合でも安全です。