UNIX / Linuxシェルでパターンマッチングを行うときに、逆または負のワイルドカードを使用するにはどうすればよいですか?

StackOverflow https://stackoverflow.com/questions/216995

質問

名前に「Music」という単語が含まれるファイルとフォルダを除くディレクトリの内容をコピーするとします。

cp [exclude-matches] *Music* /target_directory

これを達成するには、[exclude-matches]の代わりに何をすべきですか?

役に立ちましたか?

解決

Bashでは、次のように extglob オプションを有効にすることで実行できます( ls cp に置き換えて、ターゲットディレクトリを追加します。コース)

~/foobar> shopt extglob
extglob        off
~/foobar> ls
abar  afoo  bbar  bfoo
~/foobar> ls !(b*)
-bash: !: event not found
~/foobar> shopt -s extglob  # Enables extglob
~/foobar> ls !(b*)
abar  afoo
~/foobar> ls !(a*)
bbar  bfoo
~/foobar> ls !(*foo)
abar  bbar

後でextglobを無効にできます

shopt -u extglob

他のヒント

extglob シェルオプションを使用すると、コマンドラインでより強力なパターンマッチングが可能になります。

shopt -s extglob で有効にし、 shopt -u extglob で無効にします。

この例では、最初に次のことを行います。

$ shopt -s extglob
$ cp !(*Music*) /target_directory

利用可能な完全な ext 終了 glob bing演算子は( man bash からの抜粋):

  

shoptビルトインを使用してextglobシェルオプションが有効になっている場合、   パターン一致演算子が認識されます。以下の説明では、   tern-listは、|で区切られた1つ以上のパターンのリストです。複合パターン   次のサブパターンの1つ以上を使用して形成できます。

     
      
  • ?(pattern-list)
      指定されたパターンの0回または1回の出現に一致します
  •   
  • *(pattern-list)
      指定されたパターンの0回以上の出現に一致します
  •   
  • +(pattern-list)
      指定されたパターンの1つ以上の出現に一致します
  •   
  • @(pattern-list)
      指定されたパターンのいずれかに一致
  •   
  • !(pattern-list)
      指定されたパターンの1つ以外のすべてに一致します
  •   

したがって、たとえば、現在のディレクトリにある .c または .h ファイル以外のすべてのファイルを一覧表示するには、次のようにします。

$ ls -d !(*@(.c|.h))

もちろん、通常のシェルグロビングは機能するため、最後の例は次のように書くこともできます。

$ ls -d !(*.[ch])

bashではありません(私が知っていることですが):

cp `ls | grep -v Music` /target_directory

これはまさにあなたが探していたものではないことは知っていますが、それはあなたの例を解決します。

execコマンドを使用することによるメモリコストを回避したい場合は、xargsを使用するとより良い結果が得られると思います。次の方がより効率的な代替手段だと思います

find foo -type f ! -name '*Music*' -exec cp {} bar \; # new proc for each exec



find . -maxdepth 1 -name '*Music*' -prune -o -print0 | xargs -0 -i cp {} dest/

bashでは、 shopt -s extglob の代わりに GLOBIGNORE 変数。それほど良くはありませんが、覚えやすいと思います。

元のポスターが欲しかった例:

GLOBIGNORE="*techno*"; cp *Music* /only_good_music/

完了したら、 GLOBIGNOREを設定解除して、ソースディレクトリで rm * techno * できるようにします。

非常に単純な for ループを使用することもできます:

for f in `find . -not -name "*Music*"`
do
    cp $f /target/dir
done

私の個人的な好みは、grepとwhileコマンドを使用することです。これにより、強力でありながら読み取り可能なスクリプトを記述できるため、最終的に目的どおりに実行できます。さらに、エコーコマンドを使用すると、実際の操作を実行する前にドライランを実行できます。例:

ls | grep -v "Music" | while read filename
do
echo $filename
done

は、コピーするファイルを印刷します。リストが正しい場合、次のステップは次のように単にechoコマンドをcopyコマンドに置き換えることです。

ls | grep -v "Music" | while read filename
do
cp "$filename" /target_directory
done

このための1つの解決策は、findで見つけることができます。

$ mkdir foo bar
$ touch foo/a.txt foo/Music.txt
$ find foo -type f ! -name '*Music*' -exec cp {} bar \;
$ ls bar
a.txt

検索には非常に多くのオプションがあり、含めるものと除外するものを具体的に指定できます。

編集:コメントのAdamは、これは再帰的であると述べました。 findoption mindepthとmaxdepthは、これを制御するのに役立ちます。

ここでまだ見たことのない、 extglob find 、または grep を使用しないトリックは、2つのファイルを扱うことです。セットとしてリストし、" diff" を使用して comm を使用します:

comm -23 <(ls) <(ls *Music*)

comm は、余分な残骸がないため、 diff よりも望ましいです。

これは、セット1のすべての要素 ls を返しますが、これらはセット2 ls * Music * にも含まれません。これには、適切に機能するために両方のセットがソートされている必要があります。 ls とglob展開には問題ありませんが、 find などを使用している場合は、必ず sort を呼び出してください。

comm -23 <(find . | sort) <(find . | grep -i '.jpg' | sort)

潜在的に有用。

次の作品は、数字で始まるファイルを除く、現在のディレクトリ内のすべての *。txt ファイルをリストします。

これは、 bash dash zsh およびその他すべてのPOSIX互換シェルで機能します。

for FILE in /some/dir/*.txt; do    # for each *.txt file
    case "${FILE##*/}" in          #   if file basename...
        [0-9]*) continue ;;        #   starts with digit: skip
    esac
    ## otherwise, do stuff with $FILE here
done
  1. 1行目では、パターン / some / dir / *。txt により、 for ループが / some /のすべてのファイルを反復処理します。名前が .txt で終わるdir

  2. 2行目では、不要なファイルを取り除くためにcaseステートメントが使用されています。 &#8211; $ {FILE ## * /} 式は、ファイル名から先頭のディレクトリ名コンポーネント(ここでは / some / dir / )を取り除き、パターンがファイルのベース名。 (サフィックスに基づいてファイル名のみを除外する場合は、代わりに $ FILE に短縮できます。)

  3. 3行目では、 case パターン [0-9] * )行に一致するすべてのファイルがスキップされます( continue ステートメントは、 for ループの次の反復にジャンプします)。 &#8211;必要に応じて、ここでもっと面白いことをすることができます。 [!a-z] * を使用して文字(a&#8211; z)で始まらないすべてのファイルをスキップしたり、複数のパターンを使用していくつかの種類のファイル名をスキップしたりできます。 [0-9] * | * .bak は、 .bak ファイルと数字で始まらないファイルの両方をスキップします。

これは正確に「音楽」を除外して実行します

cp -a ^'Music' /target

これと、Music?*や*?Musicなどを除外するもの

cp -a ^\*?'complete' /target
cp -a ^'complete'?\* /target
ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top