xargs - tambahkan setiap argumen dengan parameter

12

Saya tahu itu, mengingat l="a b c",

echo $l | xargs ls

hasil panen

ls a b c

Yang membangun menghasilkan

mycommand -f a -f b -f c
bukan pengguna
sumber

Jawaban:

12

Salah satu cara untuk melakukannya:

echo "a b c" | xargs printf -- '-f %s\n' | xargs mycommand

Ini mengasumsikan a,, bdan ctidak mengandung blank, baris baru, kutipan atau garis miring terbalik. :)

Dengan GNU findutilAnda dapat menangani kasus umum, tetapi sedikit lebih rumit:

echo -n "a|b|c" | tr \| \\0 | xargs -0 printf -- '-f\0%s\0' | xargs -0 mycommand

Anda dapat mengganti |pemisah dengan beberapa karakter lain, yang tidak muncul dalam a, batau c.

Sunting: Seperti catatan @MichaelMol , dengan daftar argumen yang sangat panjang ada risiko meluap-luap dari panjang argumen maksimum yang bisa diteruskan mycommand. Ketika itu terjadi, yang terakhir xargsakan membagi daftar dan menjalankan salinan lain mycommand, dan ada risiko itu meninggalkan yang tidak termotivasi -f. Jika Anda khawatir tentang situasi itu, Anda bisa mengganti yang terakhir di xargs -0atas dengan sesuatu seperti ini:

... | xargs -x -0 mycommand

Ini tidak akan menyelesaikan masalah, tetapi akan dibatalkan mycommandsaat daftar argumen terlalu panjang.

Satō Katsura
sumber
1
Anda menjalankan risiko yang sangat buruk untuk melebihi ARG_MAXdan -fmemisahkan dari parameter yang dipasangkan.
Michael Mol
@MichaelMol Itu poin yang bagus, tapi saya tidak berpikir ada cara yang berarti untuk menangani situasi itu tanpa tahu lebih banyak tentang mycommand. Anda selalu bisa menambahkan -xyang terakhir xargs.
Satō Katsura
Saya pikir solusi yang tepat mungkin tidak digunakan xargssama sekali, dan gunakan saja findjika dapat digunakan. Solusi ini berbahaya; Anda setidaknya harus memperingatkan kasus kegagalan dalam jawaban Anda.
Michael Mol
@MichaelMol Saya benar-benar tidak melihat bagaimana findsolusi umum yang lebih baik, terutama ketika argumen awal bukan nama file. :)
Satō Katsura
Kami tidak tahu apa argumen awalnya; kita hanya melihat contoh yang diberikan, bukan skenario yang menginspirasi pertanyaan. Intuition menyarankan bahwa dengan argumen bernama -f, dan dengan alat contoh yang lsdigunakan untuk ilustrasi, @ not-a-user berurusan dengan nama file. Dan diberikan findmenawarkan -execargumen, yang memungkinkan Anda untuk membangun command-line, tidak apa-apa. (Selama mycommanddiizinkan untuk mengeksekusi lebih dari sekali. Jika tidak, maka kita memiliki masalah lain dengan penggunaan di xargssini ...)
Michael Mol
5

Cara yang lebih baik untuk mengatasinya (IMO) adalah:

  • di zsh:

    l=(a b c)
    mycommand -f$^l

    atau menggunakan array zip sehingga argumen tidak dilampirkan ke opsi:

    l=(a b c) o=(-f)
    mycommand "${o:^^l}"

    Dengan begitu, masih berfungsi jika larray berisi elemen kosong atau elemen yang berisi spasi atau karakter bermasalah lainnya xargs. Contoh:

    $ l=(a '' '"' 'x y' c) o=(-f)
    $ printf '<%s>\n' "${o:^^l}"
    <-f>
    <a>
    <-f>
    <>
    <-f>
    <">
    <-f>
    <x y>
    <-f>
    <c>
  • di rc:

    l=(a b c)
    mycommand -f$l
  • di fish:

    set l a b c
    mycommand -f$l

(AFAIK, rcdan fishtidak memiliki zipping array)

Dengan kerang mirip Bourne gaya lama bash, Anda selalu dapat melakukannya (masih mengizinkan karakter apa pun di elemen $@array):

set -- a b c
for i do set -- "$@" -f "$i"; shift; done
mycommand "$@"
Stéphane Chazelas
sumber
1
Cara lain untuk melakukannya di bash adalah dengan variabel array bernama. for i; do args+=('-f' "$i");done; mycommand "${args[@]}". IDK jika ini lebih cepat, tetapi menambahkan 2 elemen ke array sepertinya itu harus O (n), sementara setloop Anda mungkin menyalin dan mem-parsing daftar arg akumulasi setiap waktu (O (n ^ 2)).
Peter Cordes