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 extglobopsi, seperti ini (ganti lsdengan cpdan 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 extglobpilihan 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 .catau .hfile, 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])
.catau .hfile 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 extglobadalah GLOBIGNOREvariabel . 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 GLOBIGNOREuntuk dapat rm *techno*di direktori sumber.
Anda juga dapat menggunakan forloop yang cukup sederhana :
for f in `find . -not -name "*Music*"`
do
cp $f /target/dir
done
-maxdepth 1untuk non-rekursif?
finddi 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
bashAnda dapat menggunakan while IFS='' read -r filename, tapi kemudian baris baru masih menjadi masalah. Secara umum yang terbaik adalah tidak menggunakan lsuntuk menghitung file; alat seperti findlebih 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, findatau grepuntuk mengobati dua daftar file sebagai set dan "diff" dengan menggunakan comm:
comm -23 <(ls) <(ls *Music*)
commlebih disukai daripada diffkarena 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 lsdan 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 *.txtfile dalam direktori saat ini, kecuali yang dimulai dengan angka.
Ini bekerja di bash, dash, zshdan 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/*.txtakan menyebabkan forloop untuk mengulangi semua file /some/diryang 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 casepola [0-9]*) baris akan dilewati ( continuepernyataan melompat ke iterasi forloop 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]*|*.bakuntuk melewati file kedua .bakfile , dan file yang tidak dimulai dengan angka.
*.txtbukan 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
cphalaman manual pada MacOS memiliki -apilihan tetapi melakukan sesuatu yang sama sekali berbeda. Platform mana yang mendukung ini?
ls /dir/*/!(base*)