web-dev-qa-db-ja.com

tcshのように動作するshoptglob設定または設定コンボはありますか?

私は長い間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の動作のようです。

7
hepcat72

ファイル名の展開に関連するshoptオプションはdotglobfailglobnocaseglobnullglobのみであり、いずれも(単独または組み合わせて)ありません。 )あなたが望むことを正確に行っているようです。それは本当に良い考えのように聞こえるので、それは残念です。

インタラクティブセッションでfailglobを設定することをお勧めします。そうすることで、次のような望ましくない可能性のあるコマンドを回避できます。

mv -r file1 file2 dir1 dir2 destination-*-dir

ここで、file1が何にも一致せず、nullglobが設定されている場合、file2dir1、およびdir2destination-*-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に一致していても、実際にはディレクトリである場合でも安全です。

4
nxnev