Katakanlah saya ingin menyalin isi direktori tidak termasuk file dan folder yang namanya mengandung kata 'Musik'.
cp [exclude-matches] *Music* /target_directory
Apa yang harus dilakukan sebagai pengganti [kecocokan kecocokan] untuk mencapai ini?
Katakanlah saya ingin menyalin isi direktori tidak termasuk file dan folder yang namanya mengandung kata 'Musik'.
cp [exclude-matches] *Music* /target_directory
Apa yang harus dilakukan sebagai pengganti [kecocokan kecocokan] untuk mencapai ini?
Jawaban:
Di Bash Anda dapat melakukannya dengan mengaktifkan extglob
opsi, seperti ini (ganti ls
dengan cp
dan tambahkan direktori target, tentu saja)
~/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
Anda nanti dapat menonaktifkan extglob dengan
shopt -u extglob
f
, kecuali foo
?
The extglob
pilihan shell memberi Anda pencocokan pola yang lebih kuat pada command line.
Anda menyalakannya shopt -s extglob
, dan mematikannya dengan shopt -u extglob
.
Dalam contoh Anda, pada awalnya Anda akan melakukan:
$ shopt -s extglob
$ cp !(*Music*) /target_directory
Tersedia penuh ext berakhir gumpal operator bing adalah (kutipan dari man bash
):
Jika opsi extglob shell diaktifkan menggunakan shopt builtin, beberapa operator pencocokan pola yang diperluas dikenali. Daftar pola adalah daftar satu atau lebih pola yang dipisahkan oleh |. Pola komposit dapat dibentuk menggunakan satu atau lebih dari sub-pola berikut:
- ? (daftar pola)
Cocok dengan nol atau satu kemunculan pola yang diberikan- * (daftar pola)
Cocok dengan nol atau lebih kemunculan pola yang diberikan- + (daftar pola)
Cocok dengan satu atau lebih kemunculan pola yang diberikan- @ (daftar pola)
Cocok dengan salah satu pola yang diberikan- ! (daftar pola)
Cocokkan apa pun kecuali salah satu pola yang diberikan
Jadi, misalnya, jika Anda ingin membuat daftar semua file di direktori saat ini yang bukan .c
atau .h
file, Anda akan melakukan:
$ ls -d !(*@(.c|.h))
Tentu saja, shell global globing berfungsi dengan baik, jadi contoh terakhir juga dapat ditulis sebagai:
$ ls -d !(*.[ch])
.c
atau .h
file tersebut adalah direktori.
D
, tetapi bukan isi dari subdirektori yang mungkin terkandung dalam direktori D
.
Bukan di bash (yang saya tahu), tetapi:
cp `ls | grep -v Music` /target_directory
Saya tahu ini bukan apa yang Anda cari, tetapi ini akan memecahkan contoh Anda.
Jika Anda ingin menghindari biaya mem menggunakan perintah exec, saya yakin Anda bisa melakukan yang lebih baik dengan xargs. Saya pikir berikut ini adalah alternatif yang lebih efisien
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/
Dalam bash, alternatif shopt -s extglob
adalah GLOBIGNORE
variabel . Ini tidak benar-benar lebih baik, tetapi saya merasa lebih mudah diingat.
Contoh yang mungkin diinginkan oleh pengirim aslinya:
GLOBIGNORE="*techno*"; cp *Music* /only_good_music/
Setelah selesai, unset GLOBIGNORE
untuk dapat rm *techno*
di direktori sumber.
Anda juga dapat menggunakan for
loop yang cukup sederhana :
for f in `find . -not -name "*Music*"`
do
cp $f /target/dir
done
-maxdepth 1
untuk non-rekursif?
find
di backticks akan pecah dengan cara yang tidak menyenangkan jika ia menemukan nama file tidak trivial.
Preferensi pribadi saya adalah menggunakan grep dan perintah while. Ini memungkinkan seseorang untuk menulis skrip yang kuat namun dapat dibaca memastikan bahwa Anda akhirnya melakukan apa yang Anda inginkan. Ditambah dengan menggunakan perintah echo Anda dapat melakukan dry run sebelum melakukan operasi yang sebenarnya. Sebagai contoh:
ls | grep -v "Music" | while read filename
do
echo $filename
done
akan mencetak file yang akhirnya akan Anda salin. Jika daftar sudah benar, langkah selanjutnya adalah cukup mengganti perintah gema dengan perintah salin sebagai berikut:
ls | grep -v "Music" | while read filename
do
cp "$filename" /target_directory
done
bash
Anda dapat menggunakan while IFS='' read -r filename
, tapi kemudian baris baru masih menjadi masalah. Secara umum yang terbaik adalah tidak menggunakan ls
untuk menghitung file; alat seperti find
lebih cocok.
for file in *; do case ${file} in (*Music*) ;; (*) cp "${file}" /target_directory ; echo ;; esac; done
Sebuah trik yang saya belum melihat di sini belum yang tidak menggunakan extglob
, find
atau grep
untuk mengobati dua daftar file sebagai set dan "diff" dengan menggunakan comm
:
comm -23 <(ls) <(ls *Music*)
comm
lebih disukai daripada diff
karena tidak memiliki cruft tambahan.
Ini mengembalikan semua elemen dari set 1 ls
,, yang tidak juga di set 2 ls *Music*
,. Ini membutuhkan kedua set agar diurutkan agar berfungsi dengan benar. Tidak ada masalah untuk ls
dan ekspansi global, tetapi jika Anda menggunakan sesuatu seperti find
, pastikan untuk memohon sort
.
comm -23 <(find . | sort) <(find . | grep -i '.jpg' | sort)
Berpotensi berguna.
comm -23 <(ls) exclude_these.list
Satu solusi untuk ini dapat ditemukan dengan find.
$ mkdir foo bar
$ touch foo/a.txt foo/Music.txt
$ find foo -type f ! -name '*Music*' -exec cp {} bar \;
$ ls bar
a.txt
Temukan memiliki beberapa opsi, Anda bisa mendapatkan cukup spesifik tentang apa yang Anda sertakan dan kecualikan.
Sunting: Adam dalam komentar mencatat bahwa ini bersifat rekursif. menemukan opsi mindepth dan maxdepth dapat berguna dalam mengendalikan ini.
find -maxdepth 1 -not -name '*Music*'
/ target_directory
Karya-karya berikut mencantumkan semua *.txt
file dalam direktori saat ini, kecuali yang dimulai dengan angka.
Ini bekerja di bash
, dash
, zsh
dan semua kerang kompatibel POSIX lainnya.
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
Sejalan satu pola /some/dir/*.txt
akan menyebabkan for
loop untuk mengulangi semua file /some/dir
yang namanya berakhir dengan .txt
.
Dalam baris kedua, pernyataan kasus digunakan untuk menyingkirkan file yang tidak diinginkan. - ${FILE##*/}
Ekspresi menghapus komponen dir nama terkemuka dari nama file (di sini /some/dir/
) sehingga patters dapat mencocokkan dengan hanya nama file. (Jika Anda hanya membuang nama file berdasarkan sufiks, Anda dapat mempersingkat ini menjadi $FILE
.)
Pada baris ketiga, semua file yang cocok dengan case
pola [0-9]*
) baris akan dilewati ( continue
pernyataan melompat ke iterasi for
loop berikutnya). - Jika Anda ingin, Anda dapat melakukan sesuatu yang lebih menarik di sini, misalnya seperti melewatkan semua file yang tidak dimulai dengan huruf (a – z) menggunakan [!a-z]*
, atau Anda dapat menggunakan beberapa pola untuk melewati beberapa jenis nama file misalnya [0-9]*|*.bak
untuk melewati file kedua .bak
file , dan file yang tidak dimulai dengan angka.
*.txt
bukan hanya *
). Diperbaiki sekarang
ini akan melakukannya tidak termasuk persis 'Musik'
cp -a ^'Music' /target
ini dan itu untuk mengecualikan hal-hal seperti Musik? * atau *? Musik
cp -a ^\*?'complete' /target
cp -a ^'complete'?\* /target
cp
halaman manual pada MacOS memiliki -a
pilihan tetapi melakukan sesuatu yang sama sekali berbeda. Platform mana yang mendukung ini?
ls /dir/*/!(base*)