temukan -exec dalam skrip bash dengan ekspansi variabel

14

Saya mencoba menjalankan perintah yang mirip dengan yang di bawah ini dalam skrip bash. Itu harus mencari melalui semua subfolder $sourcedirdan menyalin semua file dari jenis tertentu ke tingkat root $targetdir.

#!/bin/bash

# These are set as arguments to the script, not hard-coded
sourcedir="/path/to/sourcedir"
targetdir="/path/to/targetdir"

find "$sourcedir" -type f -name "*.type" -exec sh -c 'cp "$1" "$2/`basename "$1"`"' "{}" "$targetdir" \;

Hal ini tampaknya menjadi cukup dekat, kecuali bahwa {}tidak sedang dilewatkan sebagai $2untuk-exec sh -c ...

Saya ingin melakukan ini sedekat mungkin dengan "jalan yang benar", dengan toleransi untuk karakter khusus dalam nama file (khususnya karakter kutipan tunggal).

Sunting: Saya melihat orang-orang menyarankan menggunakan xargsatau chaining argumen. Saya mendapat kesan bahwa ini hanya ok untuk sejumlah argumen. Jika saya memiliki, misalnya, ribuan file .jpg yang saya coba salin dari sejumlah direktori galeri ke direktori rangkai salindia raksasa, akankah solusi chaining argumen masih berfungsi?

Sunting 2: Masalah saya adalah saya tidak memiliki _sebelum opsi pertama saya untuk sh dalam -execperintah. Bagi siapa saja yang ingin tahu tentang cara membuat perintah find berfungsi, tambahkan _dan semuanya akan baik-baik saja:

find "$sourcedir" -type f -name "*.type" -exec sh -c 'cp "$1" "$2"' _ "{}" "$targetdir" \;

Saya telah menerima jawaban di bawah ini karena itu menyelesaikan tugas yang sama, tetapi lebih efisien dan elegan.

Matius
sumber
4
Itulah sebabnya mengapa xargspernah dibuat, untuk secara otomatis menangani sejumlah besar argumen pada perintah reguler yang memiliki batas. Juga untuk mempertimbangkan, sebagian besar batasan argumen maks, telah jauh ditingkatkan untuk utilitas GNU standar. Anda juga akan melihat manfaat kinerja, menghindari semua proses garpu, yang pada ribuan file relevan.
JM Becker
Dengan gnu-find dan + alih-alih ";", Anda dapat menangani beberapa argumen sekaligus dengan find juga. Dan Anda menyimpan argumen rumit lewat -print0.
pengguna tidak dikenal
@userunknown: Saya merespons ini di bawah jawaban Anda.
JM Becker
@ pengguna tidak diketahui Yah, saya suka kode ini. Paling tidak sepenuhnya kompatibel dengan POSIX dan akan bekerja tanpa hal-hal GNU pada mesin sama sekali. Ada saat-saat ketika Anda tidak butuh ini, terutama pada server di tempat kerja.
sintaksis

Jawaban:

6

Anda ingin menyalin file dari tipe tertentu, ke direktori tertentu? Ini paling baik dilakukan xargs, dan Anda bahkan tidak membutuhkannya sh. Ini adalah cara yang lebih tepat untuk melakukannya, juga harus berjalan lebih efisien.

find "$sourcedir" -type f -name "*.type" | xargs cp -t targetdir

Jika Anda perlu menangani nama file khusus, maka gunakan NULL sebagai pemisah Anda

find "$sourcedir" -type f -name "*.type" -print0 | xargs -0 cp -t "$targetdir"
JM Becker
sumber
1
Untuk kasus ke-2, jangan lupa tambahkan -print0ke finddan -0kexargs
SiegeX
@ SiegeX, sudah melakukan itu, sebelum saya perhatikan komentar Anda.
JM Becker
Juga, NULLtidak perlu jika Anda menggunakan '{}', itu penting ketika Anda tidak menggunakannya. Manfaat nyata antara yang lain, selain POSIXkepatuhan, adalah kinerja.
JM Becker
6

Anda harus memberikan {}argumen ke shell lalu lewati setiap argumen.

find "$sourcedir" -type f -name "*.type" -exec sh -c 'for f; do cp "$f" "$0"; done' "$targetdir" {} +

Catatan : Cara kerjanya adalah arg pertama ke shell adalah nama shell , kita dapat mengeksploitasi ini dengan meneruskan nama sebagai $targetdirdan kemudian menggunakan parameter khusus $0di dalam skrip shell untuk mengakses targetdir itu.

SiegeX
sumber
1
"$targetdir"tidak diperluas di dalam tanda kutip tunggal.
enzotib
5

Jika Anda tidak percaya pada gereja xargs:

find "$sourcedir" -type f -name "*.mp3" -exec cp -t "$targetdir" {} +

Penjelasan:

cp -t a b c d 

salinan b, c dan d ke target ar a.

-exec cmd {} +

menjalankan perintah pada banyak file sekaligus, bukan satu demi satu (yang standar, jika Anda gunakan ";" bukan +). Inilah sebabnya mengapa harus menarik targetdir ke depan dan menandainya secara eksplisit sebagai target.

Ini berfungsi untuk gnu-find dan mungkin tidak untuk implementasi find lainnya. Tentu saja ini bergantung pada -t-flag juga.

TechZilla tentu saja sejauh ini, benar sh tidak diperlukan untuk memanggil cp.

Jika Anda tidak menggunakan xargs, yang sebagian besar tidak perlu dikombinasikan dengan find, Anda diselamatkan dari mempelajari -print0dan -0menandai.

Pengguna tidak diketahui
sumber
1
Obvisouly, metode mana yang lebih disukai seseorang adalah sesuatu yang subjektif. Dengan itu, ada alasan untuk tetap menggunakan xargs. Contoh terbesar, bagaimana jika Anda tidak menemukan file? Saya menggunakan xargs sebagai ganti forloop umum sepanjang waktu, findcakupannya jauh lebih kecil. Selain itu, findhanya mendukung bahwa +jika Anda menggunakan GNU find, itu tidak didefinisikan dalam POSIX. Jadi, sementara Anda harus merasa bebas untuk memilih findsendiri, itu tidak melakukan semua yang dilakukan xargs. Ketika Anda mempertimbangkan GNU, xargsAnda juga akan mendapatkan -Pyang multi-core. Bagaimanapun juga, itu layak dipelajari xargs.
JM Becker
Berbicara subyektivitas, pendapat saya jelas berbeda. Di sisi lain, secara objektif, jawaban Anda juga benar. Ini adalah salah satu dari beberapa solusi terbaik yang tersedia.
JM Becker
@ TechZilla: Saya harap ingat untuk mengunjungi kembali situs ini, ketika menemukan mulai mendukung permintaan paralel. :) Dalam kebanyakan kasus dengan penyalinan / pemindahan, kecepatan disk akan menjadi faktor pembatas, tetapi SSD dapat mengubah gambar. Anda benar, bahwa memberikan solusi non-GNU adalah hal yang baik. Selain itu, solusi-menemukan secara obyektif lebih pendek, lebih sederhana, dan hanya menggunakan dua proses.
pengguna tidak dikenal
0
read -p "SOURCE: " sourcedir
read -p "TYPE: " type
read -p "TARGET: " targetdir
find -L $sourcedir -iname "*.$type" -exec cp -v {} $targetdir \;
Berthad33
sumber
3
Pertimbangkan untuk menambahkan beberapa penjelasan tentang solusi Anda.
HalosGhost