xargs - dołącz każdy argument do parametru

12

Wiem to, biorąc pod uwagę l="a b c",

echo $l | xargs ls

daje

ls a b c

Który konstrukt daje

mycommand -f a -f b -f c
nie-użytkownik
źródło

Odpowiedzi:

13

Jednym ze sposobów na to:

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

Zakłada się a, bi cnie zawiera spacji, znaków nowej linii, cytatów ani ukośników odwrotnych. :)

Z GNU findutilmożesz poradzić sobie z ogólnym przypadkiem, ale jest to nieco bardziej skomplikowane:

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

Można wymienić |separator z jakimś innym charakterze, że nie pojawi się a, blub c.

Edycja: Jak zauważa @MichaelMol , przy bardzo długiej liście argumentów istnieje ryzyko przepełnienia maksymalnej długości argumentów, które można przekazać mycommand. Kiedy tak się stanie, ostatni xargspodzieli listę i uruchomi kolejną kopię mycommand, i istnieje ryzyko, że pozostanie ona nieskończona -f. Jeśli martwisz się tą sytuacją, możesz zastąpić ostatnią z xargs -0powyższych przez coś takiego:

... | xargs -x -0 mycommand

To nie rozwiąże problemu, ale przerwie działanie, mycommandgdy lista argumentów będzie zbyt długa.

Satō Katsura
źródło
1
Ryzykujesz przekroczenie ARG_MAXi -foddzielenie od sparowanego parametru.
Michael Mol
@MichaelMol To dobra uwaga, ale nie sądzę, aby istniał jakiś znaczący sposób poradzenia sobie z tą sytuacją bez wiedzy na jej temat mycommand. Zawsze możesz dodać -xdo ostatniego xargs.
Satō Katsura
Myślę, że właściwym rozwiązaniem prawdopodobnie wcale nie jest xargs, a po prostu użyj, findjeśli można go użyć. To rozwiązanie jest niebezpieczne; powinieneś przynajmniej ostrzec przypadek niepowodzenia w swojej odpowiedzi.
Michael Mol
@MichaelMol Naprawdę nie rozumiem, jak findbyłoby lepszym ogólnym rozwiązaniem, zwłaszcza gdy początkowe argumenty nie są nazwami plików. :)
Satō Katsura
Nie wiemy, jakie są początkowe argumenty; widzimy tylko podany przykład, a nie scenariusz, który zainspirował to pytanie. Intuicja sugeruje, że z nazwanym argumentem -fi przykładowym narzędziem lsużywanym do ilustracji @ not-a-user zajmuje się nazwami plików. I biorąc pod uwagę findoferuje -execargument, który pozwala zbudować wiersz poleceń, jest w porządku. (Tak długo, jak mycommandjest to dozwolone, aby wykonać więcej niż jeden raz. Jeśli tak nie jest, mamy kolejny problem z użyciem xargstutaj ...)
Michael Mol
5

Lepszym sposobem rozwiązania tego problemu (IMO) byłoby:

  • w zsh:

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

    lub za pomocą kompresji tablicy, aby argument nie był dołączony do opcji:

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

    W ten sposób nadal działa, jeśli ltablica zawiera puste elementy lub elementy zawierające spacje lub inny problematyczny znak xargs. Przykład:

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

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

    set l a b c
    mycommand -f$l
    

(AFAIK rci fishnie ma zipowania tablicy)

Dzięki starym powłokom podobnym do Bourne'a bash, zawsze możesz to zrobić (nadal dopuszczając dowolny znak w elementach $@tablicy):

set -- a b c
for i do set -- "$@" -f "$i"; shift; done
mycommand "$@"
Stéphane Chazelas
źródło
1
Innym sposobem na zrobienie tego w bash jest użycie nazwanej zmiennej tablicowej. for i; do args+=('-f' "$i");done; mycommand "${args[@]}". IDK, jeśli jest to szybsze, ale dodanie 2 elementów do tablicy wydaje się, że powinno to być O (n), podczas gdy twoja setpętla prawdopodobnie kopiuje i ponownie analizuje zgromadzoną listę arg za każdym razem (O (n ^ 2)).
Peter Cordes