Bagaimana cara saya melakukan tindakan pada semua file dengan ekstensi spesifik dalam subfolder dengan cara yang elegan?


18

Taruhan terbaik saya saat ini adalah:

for i in $(find . -name *.jpg); do echo $i; done

Masalah: tidak menangani spasi dalam nama file.

Catatan: Saya juga suka cara grafis untuk melakukan ini, seperti perintah "tree".


Tidak bisakah Anda memberikan tanda kutip saja $i? Saya melakukan itu dan itu berfungsi dengan baik. Apakah ada yang salah dengan pendekatan itu?
Umar Farooq Khawaja

Jawaban:


26

Cara kanonik adalah melakukan

find . -name '*.jpg' -exec echo {} \;

(ganti \;dengan +untuk meneruskan lebih dari satu file echosekaligus)

atau (spesifik GNU, meskipun beberapa BSD sekarang juga memilikinya):

find . -name '*.jpg' -print0 | xargs -r0 echo

zsh:

for i (**/*.jpg(D)) echo $i

2
Jika Anda menggunakan xargs -0 , Anda harus mencocokkannya dengan find -0
itsbruce

1
@ MichaelKjörling, tanpa mengutip {} tidak ada bedanya. {} diperluas dengan find, bukan shell. Perbaikan tidak akan menggunakan echoyang memperluas urutan melarikan diri seperti \b(setidaknya konforman Unix echo).
Stéphane Chazelas

1
@sch Apakah Anda yakin? Halaman findmanual ditampilkan find . -type f -exec file '{}' \;di EXAMPLESbagian. Mungkin beberapa kerang akan memperlakukan kawat gigi secara khusus.
QuasarDonkey

1
Kutipan tidak membahayakan tetapi tidak membuat perbedaan. Semua '{}', \{\}, {}, "{}", '{'}diperluas oleh shell (apapun Bourne-seperti shell) untuk satu argumen untuk menemukan bahwa adalah dua karakter "{" dan "}". Ganti "menemukan" dengan "echo menemukan", atau printf '<%s>\n' findjika Anda ingin melipatgandakan-cek.
Stéphane Chazelas


2

Jawaban yang lebih baik telah diberikan.

Tetapi perhatikan bahwa spasi bukan satu-satunya masalah dalam kode yang Anda berikan. tab, baris baru dan karakter wildcard juga merupakan masalah.

Dengan

IFS='
'
set -f
for i in $(find . -name '*.jpg'); do echo "$i"; done

Maka hanya karakter baris baru yang menjadi masalah.


1

Apa yang Anda sebutkan adalah salah satu masalah dasar yang dihadapi orang, ketika mereka mencoba membaca nama file. Terkadang, orang-orang dengan pengetahuan terbatas dan memiliki kesalahpahaman tentang struktur file dan folder cenderung lupa bahwa " Dalam UNIX Semuanya adalah file ". Jadi mereka tidak mengerti bahwa mereka perlu menangani spasi juga, karena nama file dapat terdiri dari 2 kata atau lebih dengan spasi.

Solusi: Jadi salah satu cara yang lebih dikenal untuk melakukan ini adalah dengan melakukan pembacaan yang bersih .

Kita di sini akan membaca semua file yang ada dan menyimpannya dalam sebuah variabel, lain kali ketika melakukan pemrosesan yang diinginkan kita hanya perlu menyimpan variabel itu di dalam tanda kutip yang akan menjaga nama file dengan spasi. Ini adalah salah satu cara dasar untuk melakukannya, namun, jawaban lain yang diberikan oleh orang lain di sini juga berfungsi.

SCRIPT:

 find /path/to/files/ -iname "*jpg" | \
  while read I; do
   cp -v --parent "$I" /backup/dir/
  done

Di sini saya membaca file dengan memberikan path kepada mereka dan apa pun yang saya baca saya menyimpannya di dalam variabel I, yang akan saya kutip di kemudian hari saat memprosesnya untuk melestarikan ruang sehingga memprosesnya dengan benar.

Semoga ini bisa membantu Anda dalam beberapa cara.


1
"Semuanya adalah file" mengacu pada sesuatu yang lain. Solusi Anda spesifik untuk GUN dan tidak mengatasi karakter garis miring terbalik atau baris baru dalam nama file. "while read" loop in shells (IMO) seringkali merupakan indikasi praktik skrip shell yang buruk.
Stéphane Chazelas

@sch: huh, dan saya dulu berpikir, tidak apa-apa. Terima kasih telah menunjukkan itu. Saya mungkin perlu melihatnya lebih lanjut.
The Dark Knight

maaf, maksud saya "GNU", bukan "GUN" di atas (ini pertama kalinya saya menyadari bahwa itu adalah anagram BTW ...)
Stéphane Chazelas

1

Cukup dengan finddan bash:

find . -name '*.jpg' -exec bash -c '
    echo "treating file $1 in bash, from path : ${1%/*}" 
' -- {} \;

Dengan cara ini, kami menggunakan $1dalam bash, seperti dalam naskah dasar, yang membuka perspektif yang bagus untuk melakukan tugas-tugas lanjutan (atau tidak).

Cara yang lebih efisien (untuk menghindari keharusan memulai bash baru untuk setiap file):

find . -name '*.jpg' -exec bash -c 'for i do
    echo "treating file $i in bash, from path : ${i%/*}" 
  done' -- {} +

0

Di versi bash terbaru, Anda dapat menggunakan globstaropsi:

shopt -s globstar
for file in **/*.jpg ; do
    echo "$file"
done

Untuk tindakan sederhana, Anda bahkan dapat melewatkan loop sepenuhnya:

echo **/*.jpg

Sementara itu bekerja di zsh (di mana fitur berasal dari) dan dengan ksh93 (dengan set -G), itu tidak boleh digunakan dalam bash karena versi bash rusak secara mendasar (seperti GNU grep -r) karena turun ke symlink ke direktori (setara dengan find -Latau zsh's ***/*.jpg). Juga perhatikan bahwa bertentangan dengan menemukan, itu akan menghapus dotfiles dan tidak turun ke dotdirs (secara default).
Stéphane Chazelas
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.