Cara memberi daftar yang dipisahkan koma sebagai argumen untuk perintah selanjutnya

9

Saya memiliki skrip s1yang menampilkan daftar angka yang dipisahkan dengan ',' misalnya 1,2,3,4. Sekarang saya ingin memberikan angka-angka ini ke skrip s2sebagai argumen, sehingga s2 akan dijalankan pada masing-masing dan menampilkan hasilnya dalam baris terpisah. Misalnya, jika s2 mengalikan angka dengan dua, ini akan menjadi hasil yang saya cari:

2
4
6
8

Yang saya lakukan sekarang adalah:

s1 | xargs -d "," | xargs -n1 s2

Tapi aku merasa seperti melakukannya dengan cara bodoh! Jadi pertanyaan saya adalah:

Apa cara yang tepat untuk melakukannya?

Masalah saya dengan solusi saya adalah memanggil xargs dua kali dan mengulangi input dua kali yang tentu saja tidak masuk akal bagi saya melalui kinerja! Jawabannya xargs -d "," -n1kelihatannya bagus, tapi saya tidak yakin apakah itu hanya dilakukan satu kali. Jika ya, harap verifikasi itu dalam jawaban Anda, dan saya akan menerimanya. By the way, saya lebih suka tidak menggunakan Perl karena masih iterasi dua kali dan juga Perl mungkin tidak ada pada banyak sistem.

yukashima huksay
sumber
1
Jika itu berhasil mengapa menyebutnya bodoh. Jika waktu eksekusi itu penting maka itu masalah lain lagi, tinggalkan di sini
George Udosen
2
Coba inis1 | xargs -d "," -n1 s2
George Udosen
1
Saya menduga beberapa masalah salah paham tentang dampak iterasi. Iterasi atas elemen-elemen dalam sesuatu seperti array asosiatif buruk karena biaya berjalan struktur data, tetapi "iterasi" secara umum tidak buruk secara inheren. Secara khusus, membaca data baris demi baris sebagaimana ada pada STDIN, bukan masalah kinerja yang sangat besar. Masalah kinerja di sini adalah lebih banyak biaya menelurkan proses baru dan menyiapkan pipa. Selama Anda tidak sering melakukannya (seperti dalam satu lingkaran), mengkhawatirkan kinerja xargs mungkin merupakan contoh pengoptimalan prematur.
dannysauer

Jawaban:

18

Ini harus bekerja sama baiknya:

s1 | xargs -d "," -n1 s2

Kasus cobaan:

printf 1,2,3,4 | xargs -d ',' -n1 echo

Hasil:

1
2
3
4

Jika s1menampilkan daftar yang diikuti oleh karakter baris baru, Anda ingin menghapusnya karena jika tidak panggilan terakhir akan dengan 4\nbukannya 4:

s1 | tr -d '\n' | xargs -d , -n1 s2
George Udosen
sumber
6

Jika s2dapat menerima beberapa argumen, Anda bisa melakukan:

(IFS=,; ./s2 $(./s1))

yang sementara mengesampingkan IFS menjadi koma, semua dalam subkulit, sehingga s2melihat output s1dipecah oleh koma. Subshell adalah cara mudah untuk mengubah IFS tanpa menyimpan nilai sebelumnya atau meresetnya.

Versi sebelumnya dari jawaban ini salah, mungkin karena pengaturan IFS yang tersisa, merusak hasil. Terima kasih kepada ilkkachu karena menunjukkan kesalahan saya .

Untuk secara manual mengulang output dan menyediakannya secara individual s2, di sini menunjukkan penghematan & pengaturan ulang IFS:

oIFS="$IFS"
IFS=,
for output in $(./s1); do ./s2 "$output"; done
IFS="$oIFS"

atau jalankan bit IFS dalam subkulit seperti sebelumnya:

(
IFS=,
for output in $(./s1); do ./s2 "$output"; done
)
Jeff Schaller
sumber
1
Apakah Anda yakin tentang yang pertama? bash -c 'IFS=, printf "%s\n" $(echo 1,2,3)'mencetak 1,2,3pada sistem saya, yaitu tidak ada pemisahan.
ilkkachu
Saya akan menguji ulang sedikit, tapi saya curiga perilaku yang berbeda berdasarkan builtins vs program eksternal.
Jeff Schaller
sama dengan /usr/bin/printfdan/bin/echo
ilkkachu
1
@ilkkachu Anda benar, pemisahan kata terjadi sebelum IFS ditugaskan kembali dalam kasus ini (IFS=,; printf "%s\n" $(echo 1,2,3)), di sisi lain, harus berfungsi.
Undercat memuji Monica
1

Coba ini:

s1 | perl -pe 's/,/\n/g' | xargs -n1 s2
Gilles Quenot
sumber
( Ruang ...)
Peter Mortensen
3
Kenapa tidak sederhana saja tr ',' '\n'? Tidak perlu memanggil sesuatu yang (relatif) seberat Perl dan ekspresi reguler.
David Foerster