Pertama-tama untuk memotong jawaban yang sepele tetapi tidak dapat diterapkan: Saya tidak dapat menggunakan trik find
+ xargs
variannya (seperti find
dengan -exec
) karena saya perlu menggunakan beberapa ekspresi seperti itu per panggilan. Saya akan kembali ke ini di akhir.
Sekarang untuk contoh yang lebih baik mari kita pertimbangkan:
$ find -L some/dir -name \*.abc | sort
some/dir/1.abc
some/dir/2.abc
some/dir/a space.abc
Bagaimana saya meneruskannya sebagai argumen program
?
Melakukannya saja tidak berhasil
$ ./program $(find -L some/dir -name \*.abc | sort)
gagal karena program
mendapat argumen berikut:
[0]: ./program
[1]: some/dir/1.abc
[2]: some/dir/2.abc
[3]: some/dir/a
[4]: space.abc
Seperti dapat dilihat, jalan dengan ruang terpecah dan program
menganggapnya sebagai dua argumen yang berbeda.
Kutip sampai berfungsi
Tampaknya pengguna pemula seperti saya, ketika dihadapkan dengan masalah seperti itu, cenderung secara acak menambahkan tanda kutip sampai akhirnya berfungsi - hanya di sini sepertinya tidak membantu ...
"$(…)"
$ ./program "$(find -L some/dir -name \*.abc | sort)"
[0]: ./program
[1]: some/dir/1.abc
some/dir/2.abc
some/dir/a space.abc
Karena tanda kutip mencegah pemisahan kata, semua file dilewatkan sebagai argumen tunggal.
Mengutip jalur individu
Pendekatan yang menjanjikan:
$ ./program $(find -L some/dir -name \*.abc -printf '"%p"\n' | sort)
[1]: "some/dir/1.abc"
[2]: "some/dir/2.abc"
[3]: "some/dir/a
[4]: space.abc"
Kutipan di sana, tentu. Tetapi mereka tidak lagi ditafsirkan. Mereka hanyalah bagian dari string. Jadi tidak hanya mereka tidak mencegah pemisahan kata, tetapi juga mereka bertengkar!
Ubah IFS
Lalu aku mencoba bermain-main dengan IFS
. Saya lebih suka find
dengan -print0
dan sort
dengan cara -z
apapun - sehingga mereka tidak akan memiliki masalah pada "jalur kabel" sendiri. Jadi mengapa tidak memaksakan pemisahan kata pada null
karakter dan memiliki semuanya?
$ ./program $(IFS=$'\0' find -L some/dir -name \*.abc -print0 | sort -z)
[0]: ./program
[1]: some/dir/1.abcsome/dir/2.abcsome/dir/a
[2]: space.abc
Jadi masih terbagi pada ruang dan tidak terpecah pada null
.
Saya mencoba untuk menempatkan IFS
tugas di $(…)
(seperti yang ditunjukkan di atas) dan sebelumnya ./program
. Juga saya mencoba sintaks lain seperti \0
, \x0
, \x00
baik dikutip dengan '
dan "
serta dengan dan tanpa $
. Tak satu pun dari mereka yang tampaknya membuat perbedaan ...
Dan di sini saya kehabisan ide. Saya mencoba beberapa hal lagi tetapi semua tampaknya berjalan ke masalah yang sama seperti yang tercantum.
Apa lagi yang bisa saya lakukan? Apakah bisa dilakukan?
Tentu, saya bisa program
menerima pola dan melakukan pencarian sendiri. Tapi itu banyak pekerjaan ganda sambil memperbaikinya ke sintaksis tertentu. (Bagaimana dengan menyediakan file dengan grep
contohnya?).
Saya juga bisa membuat program
menerima file dengan daftar jalur. Kemudian saya dapat dengan mudah membuang find
ekspresi ke beberapa file temp dan menyediakan path ke file itu saja. Ini dapat didukung berada di sepanjang jalur langsung sehingga jika pengguna hanya memiliki jalur sederhana itu dapat disediakan tanpa file perantara. Tapi ini sepertinya tidak bagus - orang perlu membuat file tambahan dan merawatnya, belum lagi implementasi tambahan yang diperlukan. (Di sisi positifnya, bagaimanapun, itu bisa menjadi penyelamatan untuk kasus-kasus di mana jumlah file sebagai argumen mulai menyebabkan masalah dengan panjang baris perintah ...)
Pada akhirnya, izinkan saya mengingatkan Anda lagi bahwa find
+ xargs
(dan sejenisnya) trik tidak akan berfungsi dalam kasus saya. Untuk kesederhanaan deskripsi, saya hanya menunjukkan satu argumen. Tetapi kasus saya yang sebenarnya lebih terlihat seperti ini:
$ ABC_FILES=$(find -L some/dir -name \*.abc | sort)
$ XYZ_FILES=$(find -L other/dir -name \*.xyz | sort)
$ ./program --abc-files $ABC_FILES --xyz-files $XYZ_FILES
Jadi melakukan xargs
dari satu pencarian masih membuat saya berurusan dengan yang lain ...
sumber
mapfile
(atau sinonimnyareadarray
). Tapi itu berhasil!while
loop tidak menghapus array. Yang berarti bahwa jika tidak ada file yang ditemukan array tidak terdefinisi. Sementara jika sudah ditentukan file baru akan ditambahkan (bukan menggantikan yang lama). Tampaknya menambahkandeclare -a ABC_FILES='()';
sebelum berhasilwhile
. (Sementara menambahkanABC_FILES='()';
tidak.)< <
artinya di sini? Apakah sama dengan<<
? Saya tidak berpikir untuk mengubahnya<<
menghasilkan kesalahan sintaks ("token tak terduga` ('"). Jadi apa itu dan bagaimana cara kerjanya?ABC_FILES
. Ini baik saja. Tetapi berguna juga untuk membuatABS_ARGS
array kosong jikaABC_FILES
kosong atau array('--abc-files' "${ABC_FILES[@]}")
. Dengan cara ini nanti saya bisa menggunakannya seperti ini:./program "${ABC_ARGS[@]}" "${XYZ_ARGS[@]}"
dan pastikan itu akan bekerja dengan benar terlepas dari mana (jika ada) dari grup kosong. Atau untuk menyatakannya secara berbeda: cara ini--abc-files
(dan--xyz-files
) akan disediakan hanya jika diikuti oleh beberapa jalur aktual.while read ... done < <(find blah)
adalah pengalihan shell normal<
dari file khusus yang dibuat oleh SUBSTITUSI PROSES . Ini berbeda dari pemipaanfind blah | while read ... done
karena pipa menjalankanwhile
loop dalam subkulit sehingga var (s) yang ditetapkan di dalamnya tidak dipertahankan untuk perintah berikutnya.Anda dapat menggunakan IFS = baris baru (dengan asumsi tidak ada nama file yang mengandung baris baru) tetapi Anda harus mengaturnya di kulit luar SEBELUM substitusi:
Dengan
zsh
tetapi tidakbash
Anda dapat menggunakan null$'\0'
juga. Bahkan di dalambash
kamu dapat menangani baris baru jika ada satu karakter yang cukup aneh yang tidak pernah digunakan sepertiNamun, pendekatan ini tidak menangani permintaan tambahan yang Anda buat di komentar pada jawaban @ steeldriver untuk menghilangkan --afile jika menemukan kosong.
sumber
IFS
untuk berpisahnull
?read -d ''
digunakan dalam metode steeldriver adalah string kosong yang bukan berisi byte nol. (Dan bagaimanapun juga, opsi perintah bukanlah var.)set -o noglob
) sebelum menggunakan operator split + glob tersebut (kecuali dalamzsh
).$'\0'
dan juga sebagai''
.Saya tidak yakin saya mengerti mengapa Anda menyerah
xargs
.String
--xyz-files
hanyalah salah satu dari banyak argumen dan tidak ada alasan untuk menganggapnya spesial sebelum ditafsirkan oleh program Anda. Saya pikir Anda dapat melewatinya dixargs
antara keduafind
hasil:sumber
-print0
di urutan keduafind
. Juga jika akan cara ini saya akan menempatkan--abc-files
sebagaiecho
juga - hanya untuk konsistensi..abc
file maka harus juga tidak ada--abc-files
(sama dengan.xyz
). The solusi berbasis-array oleh steeldriver juga membutuhkan logika ekstra untuk itu tapi logika yang sepele ada sementara mungkin tidak begitu sepele disini menghancurkan keuntungan utama dari solusi ini - kesederhanaan.xargs
tidak akan pernah mencoba untuk membagi argumen dan membuat beberapa perintah, bukan satu, kecuali itu secara eksplisit diperintahkan untuk melakukannya dengan-L
,--max-lines
(-l
),--max-args
(-n
) atau--max-chars
(-s
) argumen. Apakah saya benar? Atau ada beberapa default? Karena program saya tidak akan menangani perpecahan seperti itu dengan benar dan saya lebih suka memiliki kegagalan untuk menyebutnya ...-print0
- diperbaiki, terima kasih. Saya tidak tahu semua jawaban tetapi saya setuju solusi saya membuatnya sulit untuk memasukkan logika tambahan. Saya mungkin akan pergi dengan array sendiri, sekarang ketika saya tahu pendekatan ini. Jawaban saya bukan untuk Anda. Anda sudah menerima jawaban lain dan saya berasumsi masalah Anda terpecahkan. Saya hanya ingin menunjukkan bahwa Anda dapat melewati argumen dari berbagai sumber melaluixargs
, yang tidak jelas pada pandangan pertama. Anda dapat memperlakukannya sebagai bukti konsep. Sekarang kita semua tahu beberapa pendekatan berbeda dan kita dapat secara sadar memilih apa yang cocok untuk kita dalam setiap kasus tertentu.--abc-files
). Tapi Anda benar - ada baiknya mengetahui alternatif Anda! Apalagi saya salah mengira itu tidak mungkin.