名前に「音楽」という単語が含まれるファイルとフォルダを除くディレクトリの内容をコピーするとします。
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
Shellオプションを使用すると、コマンドラインでより強力なパターンマッチングを実行できます。
shopt -s extglob
でオンにし、shopt -u extglob
でオフにします。
この例では、最初に次のことを行います。
$ shopt -s extglob
$ cp !(*Music*) /target_directory
利用可能な完全なext ended glob bing演算子は(man bash
からの抜粋):
Shoptビルトインを使用してextglob Shellオプションが有効になっている場合、いくつかの拡張パターンマッチング演算子が認識されます。以下の説明では、パターンリストは、|で区切られた1つ以上のパターンのリストです。複合パターンは、次のサブパターンの1つ以上を使用して形成できます。
- ?(パターンリスト)
指定されたパターンの0回または1回の出現に一致します- *(パターンリスト)
指定されたパターンの0回以上の出現と一致します- +(パターンリスト)
指定されたパターンの1つ以上の出現と一致します- @(パターンリスト)
指定されたパターンのいずれかと一致します- !(パターンリスト)
指定されたパターンの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/
完了したら、ソースディレクトリでunset GLOBIGNORE
をrm *techno*
にできるようにします。
私の個人的な好みは、grepとwhileコマンドを使用することです。これにより、強力でありながら読み取り可能なスクリプトを記述できるため、最終的に目的どおりに実行できます。さらに、エコーコマンドを使用すると、実際の操作を実行する前にドライランを実行できます。例えば:
ls | grep -v "Music" | while read filename
do
echo $filename
done
コピーするファイルを印刷します。リストが正しい場合、次のステップは次のように単純にエコーコマンドをコピーコマンドに置き換えることです。
ls | grep -v "Music" | while read filename
do
cp "$filename" /target_directory
done
また、非常に単純なfor
ループを使用することもできます。
for f in `find . -not -name "*Music*"`
do
cp $f /target/dir
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
Findには非常に多くのオプションがあり、含めるものと除外するものをかなり具体的に取得できます。
編集:コメントのアダムは、これは再帰的であると述べました。 findオプションmindepthとmaxdepthは、これを制御するのに役立ちます。
extglob
、find
、またはgrep
を使用しない、ここでまだ見たことのないトリックは、2つのファイルリストをセットとして扱い、comm
を使用して"diff"それらを扱うことです。
comm -23 <(ls) <(ls *Music*)
comm
は、余分な問題がないため、diff
よりも望ましいです。
これは、セット1のすべての要素ls
を返します。これは、セット2のls *Music*
でもnotです。これには、適切に機能するために両方のセットがソートされている必要があります。 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行目では、パターン/some/dir/*.txt
により、for
ループが、/some/dir
で終わる名前を持つ.txt
内のすべてのファイルを反復処理します。
2行目では、不要なファイルを除外するためにcaseステートメントが使用されています。 – ${FILE##*/}
式は、ファイル名(ここでは/some/dir/
)から先頭のディレクトリ名コンポーネントを取り除き、パターンがファイルのベース名のみと一致するようにします。 (サフィックスに基づいてファイル名のみを除外する場合は、これを代わりに$FILE
に短縮できます。)
3行目では、case
パターン[0-9]*
)行に一致するすべてのファイルがスキップされます(continue
ステートメントは、for
ループの次の反復にジャンプします)。 –必要に応じて、ここでもっと面白いことができます。 [!a-z]*
を使用して文字(a〜z)で始まらないすべてのファイルをスキップしたり、複数のパターンを使用していくつかの種類のファイル名をスキップしたりできます。 [0-9]*|*.bak
は、.bak
ファイルと数字で始まらないファイルの両方のファイルをスキップします。
これは正確に「音楽」を除外してそれを行います
cp -a ^'Music' /target
これと音楽のようなものを除外するためのあれ?*または*?音楽
cp -a ^\*?'complete' /target
cp -a ^'complete'?\* /target