bashスクリプトでrm -v!(* .yaml)を実行するには?

bashスクリプトでrm -v!(* .yaml)を実行するには?

スクリプトは次のとおりです。

rm -v !\(*.yaml\) ;\

これは生産します

rm: cannot remove '!(*.yaml)': No such file or directory

しかし、コマンドラインではうまく機能します。脱出のためにさまざまな方法を試しました。

'\!\(\*.yaml\)'
`\!\(\*.yaml\)`
`!\(*.yaml\"\)`
"\!\(\*.yaml\)"

適切なエスケープシーケンスを見つけることができないようです。理解できません。括弧を削除することが私の最初のステップでした。そして脱出を試みたし!、その後は*。また、バックティックエスケープ解除を試してみましたが、「rm欠落オペランド」エラーが発生しました。ちょっとめちゃくちゃ。すでに約1時間を費やしました。 "yamlの代わりにすべてをrm"してください...誰でもバグを見つけたり、修正を提案したりできますか?

私も #!/bin/sh と #!/bin/bash を試しました。少しでも効果がないかと思いました。

答え1

!(x)Korn シェルグローバル演算子です。この演算子を含むが1以降にのみサポートされるglob演算子bashのサブセット。kshshopt -s extglob

だから:

shopt -s extglob
rm -f -- !(*.yaml)

名前が . で終わるファイルを除き、現在のディレクトリから隠されていないすべてのファイルが削除されます.yaml

とにかくグローバルオペレーターはいいえ引用時にそのように認識されるので\(Bourneシェルの引用演算子の1つ)を使用することは役に立ちません。

シェルでこれに対応するものは次のとおりですzsh

rm -f -- ^*.yaml

これを行うにはset -o extendedglob

(または!(x)それ以降もサポートされます)。emulate kshset -o kshglob

sh拡張機能を使用してサポートshに基づく実装など、いくつかの実装を見つけることができますが、言語には同等の実装はありません。ksh!(x)


¹これはシェルの解析に影響を与えるため、shoptコマンドを実行する必要があります。今後これらのkshスプレッド演算子の1つを含む行は次のとおりです。読むそして解析します。たとえば、bash -c 'shopt -s extglob; echo !(x)'行全体が最初に解析された後(extglobがまだ開かれていない)shopt実行(まあ、構文エラーがなかった場合は実行された)となるため、構文エラーが発生します。bash -O extglob -c 'echo !(x)'とても良いです。

答え2

GLOBIGNOREBashには、globから削除するファイル名をシェルに伝え、残りのファイル名(この場合rm -v)を引き続き使用する変数という機能があります。

以下はデモです:

mkdir /tmp/this-test
cd /tmp/this-test
touch one.yaml two.yaml three.json four.txt
echo *

生産:

four.txt one.yaml three.json two.yaml

ここでGLOBIGNORE.yaml ファイルを無視するように設定し、その効果を無効にします。

GLOBIGNORE='*.yaml'
echo *
GLOBIGNORE=
echo *

生産:

four.txt three.json
four.txt one.yaml three.json two.yaml

このコマンドは、名前で終わらないディレクトリを削除rm -v *します。echo *.yaml

GLOBIGNORE:それぞれコロン()で区切られたglob式のリストがあります。たとえば、 GLOBIGNORE='*.yaml:*.json' 上記のデモでこの値を設定すると、このGLOBIGNORE値はglobリストから.yamlファイルと.jsonファイル名を削除するため、そのファイルのみが表示four.txtまたは削除されます。

答え3

findとrmを組み合わせる別の方法...

5つのファイルを含むフォルダから始める

cd /path/to/files/
touch one.yaml two.yaml three.txt four.yaml five.json

名前パターンを除外する find ユーティリティを使用できますが、非再帰的検索に find を使用するには、-minlength および -maxlength を 1 に設定する必要があります。

find /path/to/files -mindepth 1 -maxdepth 1 -type f ! -name '*.yaml' -exec rm -v '{}' \;
removed '/path/to/files/three.txt'
removed '/path/to/files/five.json'

その後、見つかったファイルごとに個別に rm -v を使用できます。ファイルの数が多い場合は、各ファイルに対して個別にrmを呼び出すため、速度が遅くなる可能性があります。

ただし、-deleteを使用すると、詳細な出力なしではるかに迅速に同じ効果が得られます。

find /path/to/files -mindepth 1 -maxdepth 1 -type f ! -name '*.yaml' -delete

関連情報