xargs - anexa cada argumento com um parâmetro

12

Eu sei que, dado l="a b c",

echo $l | xargs ls

rendimentos

ls a b c

Qual construção produz

mycommand -f a -f b -f c
não usuário
fonte

Respostas:

12

Uma maneira de fazer isso:

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

Isso pressupõe a, be cnão contém espaços em branco, novas linhas, aspas ou barras invertidas. :)

Com o GNU, findutilvocê pode lidar com o caso geral, mas é um pouco mais complicado:

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

Você pode substituir o |separador com algum outro personagem, que não aparece em a, bou c.

Editar: Como observa o @MichaelMol , com uma lista muito longa de argumentos, existe o risco de exceder o tamanho máximo dos argumentos que podem ser passados mycommand. Quando isso acontece, o último xargsdividirá a lista e executará outra cópia de mycommand, e existe o risco de deixar um não terminado -f. Se você se preocupar com essa situação, poderá substituir o último xargs -0acima por algo assim:

... | xargs -x -0 mycommand

Isso não resolverá o problema, mas interromperá a execução mycommandquando a lista de argumentos for muito longa.

Satō Katsura
fonte
1
Você corre um risco bastante feio de exceder ARG_MAXe -fseparar seu parâmetro emparelhado.
Michael Mol
@MichaelMol Esse é um bom argumento, mas não acho que exista uma maneira significativa de lidar com essa situação sem saber mais mycommand. Você sempre pode adicionar -xao último xargs.
Satō Katsura
Eu acho que a solução adequada provavelmente não deve ser usada xargs, e use apenas findse puder ser usada. Essa solução é perigosa; você deve pelo menos avisar sobre o caso de falha em sua resposta.
22617 Michael Michael
@MichaelMol Realmente não vejo como findseria uma solução geral melhor, principalmente quando os argumentos iniciais não são nomes de arquivos. :)
Satō Katsura
Não sabemos quais são os argumentos iniciais; vemos apenas o exemplo, não o cenário que inspirou a pergunta. A intuição sugere que, com um argumento nomeado -fe uma ferramenta de exemplo lsusada para ilustração, @ não-usuário lida com nomes de arquivos. E, como findoferece o -execargumento, que permite construir uma linha de comando, tudo bem. (Contanto que mycommandseja permitido executar mais de uma vez. Se não for, teremos outro problema com o uso xargsaqui ...) #
1119 Michael Mol Michael
5

Uma maneira melhor de resolvê-lo (IMO) seria:

  • em zsh:

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

    ou usando o zip da matriz para que o argumento não seja anexado à opção:

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

    Dessa forma, ainda funcionará se a lmatriz contiver elementos vazios ou elementos que contenham espaços ou qualquer outro caractere problemático xargs. Exemplo:

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

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

    set l a b c
    mycommand -f$l

(AFAIK rce fishnão possui zíper na matriz)

Com cascas de estilo Bourne do estilo antigo bash, você sempre pode fazer (ainda permitindo qualquer caractere nos elementos da $@matriz):

set -- a b c
for i do set -- "$@" -f "$i"; shift; done
mycommand "$@"
Stéphane Chazelas
fonte
1
Outra maneira de fazer isso no bash é com uma variável de matriz nomeada. for i; do args+=('-f' "$i");done; mycommand "${args[@]}". IDK se for mais rápido, mas anexar 2 elementos a uma matriz parece que deve ser O (n), enquanto seu setloop provavelmente copia e analisa a lista de argumentos acumulados todas as vezes (O (n ^ 2)).
Peter Cordes