find: -exec vs xargs (alias Mengapa "find | xargs basename" rusak?)

10

Saya mencoba untuk menemukan semua file dari jenis tertentu yang tersebar di subdirektori, dan untuk keperluan saya, saya hanya perlu nama file. Saya mencoba menghapus komponen jalur melalui basename, tetapi tidak berhasil dengan xargs:

$ find . -name '*.deb' -print | xargs basename 
basename: extra operand `./pool/main/a/aalib/libaa1_1.4p5-37+b1_i386.deb'
Try `basename --help' for more information.

Saya mendapatkan hal yang sama (kesalahan yang persis sama) dengan salah satu dari variasi ini:

$ find . -name '*.deb' -print0 | xargs -0 basename 
$ find . -name '*.deb' -print | xargs basename {}

Ini, di sisi lain, berfungsi seperti yang diharapkan:

$ find . -name '*.deb' -exec basename {} \;
foo
bar
baz

Ini terjadi pada Cygwin dan Debian 5.0.3 yang terbaru. Diagnosis saya adalah xargs karena suatu alasan melewatkan dua jalur input ke nama dasar, tetapi mengapa? Apa yang terjadi di sini?

quack quixote
sumber

Jawaban:

23

Karena basenamehanya ingin satu parameter ... tidak BANYAK. Dan xargsmenciptakan banyak parameter.

Untuk mengatasi masalah Anda yang sebenarnya (hanya cantumkan nama file):

 find . -name '*.deb' -printf "%f\n"

Yang mencetak hanya 'nama samaran' (man find):

 %f     File's name with any leading directories
        removed (only the last element).
akira
sumber
1
oooh .... / menampar dahi lagi / saya pikir saya perlu buku "find for dummies" ...
quack quixote
Saya pikir intinya xargsadalah bahwa ia membuat daftar argumen dan memberi makan masing-masing ke perintah yang datang setelah? kalau tidak, apa bedanya itu find . -name '*.deb' | basename?
WindowsMaker
Nama dasar GNU sekarang memiliki -aopsi: "mendukung banyak argumen dan memperlakukan masing-masing sebagai nama".
Uskup
1
@WindowsMaker xargsmengkonversi stdinke perintah argumen. Di satu sisi, itu kebalikan dari echo, yang mengubah argumennya menjadi stdout. Perbedaan antara find ... | xargs -n1 basenameatau find ... | xargs basename -adan find ... | basenamebahwa kedua mantan akan bekerja dengan implementasi dari basenamepengabaian itu stdin.
8bittree
19

Coba ini:

find . -name '*.deb' | xargs -n1 basename
perlguy9
sumber
ini bukan penjelasannya, ini solusinya. dan solusinya sama baiknya dengan hanya memanggil 'basename' melalui -exec untuk setiap file yang ditemukan.
akira
4
+1 ... walaupun bukan penjelasan, ini akan mengarahkan saya untuk menyelidiki sakelar xargs yang Anda tunjukkan, yang pada akhirnya akan mengarahkan saya ke gerakan menampar dahi yang saya gunakan membaca jawaban akira dan john t ...
quack quixote
1
Beginilah cara saya melakukannya. Saya tidak merasa ingin mempelajari semua seluk beluk findperintah, jadi saya hanya menggunakannya untuk mencari dan mendaftar file, dan saya menggunakan xargs untuk yang lainnya.
Ryan C. Thompson
4

basename hanya menerima satu argumen. Menggunakan -execberfungsi dengan baik karena masing {}- masing diganti dengan nama file saat ini sedang diproses, dan perintah dijalankan sekali per file yang cocok , alih-alih mencoba mengirim semua argumen ke nama file dalam sekali jalan.

John T
sumber
3

xargs dapat dipaksa untuk hanya melewati satu argumen juga ...

find . -name '*.deb' -print | xargs -n1 basename

Ini berfungsi, namun jawaban yang diterima menggunakan finddengan cara yang lebih tepat. Saya menemukan pertanyaan ini mencari xargs basenamemasalah karena saya menggunakan perintah lain untuk mendapatkan daftar lokasi file. The -n1bendera untuk xargsadalah jawaban utama bagi saya.

Flet
sumber