Como eu posso especificar para xargs para executar um comando com todos os argumentos de uma só vez?

0

xargs tem uma opção -n para especificar o número máximo de argumentos para um comando a ser executado a cada vez.

Existe uma maneira de especificar que xargs deve sempre executar qualquer comando com todos os argumentos fornecidos de uma só vez? (Assim como executar o comando com todos os argumentos diretamente sem xargs , não estou tentando evitar falhas devido ao limite do sistema no comprimento da linha de comando)

O seguinte não é a minha pergunta. Por favor, desconsidere-o, se isso te distrair. Eu não quero usar a substituição de comandos, porque remove NUL e novas linhas, então estou pensando em usar xargs como uma alternativa. Mas não quero que xargs agrupe os argumentos para várias execuções, porque os resultados de várias execuções com diferentes subconjuntos de argumentos podem ser diferentes dos resultados da execução única com todos os argumentos, dependendo do comando que xargs executa. Então, quero dizer a xargs para sempre executar todos os argumentos de uma só vez.

    
por Ben 23.11.2018 / 13:59

3 respostas

1

Ainda não tenho certeza se entendi o que você quer, mas combinando a opção -x ("sair se a linha de comando não couber") com a opção -n definida como um valor enorme (maior que o limite do sistema):

a) certifique-se de que xargs seja executado apenas uma vez, independentemente de quantos argumentos forem dados a ele

b) erro se os argumentos não puderam ser ajustados em um único comando por causa do OS ou limite interno xargs.

Exemplo:

$ seq 1 10000 | xargs -n 100000000 -x sh -c 'echo "$#"' sh
10000
$ seq 1 100000 | xargs -n 100000000 -x sh -c 'echo "$#"' sh
xargs: argument list too long

Infelizmente, isso não funciona com os xargs BSD ou solaris. No * BSD, a opção -x fará com que xargs execute seu comando com um único argumento, em vez de sair:

fz11_2$ jot 10000 1 | xargs -n 10000 -x sh -c 'echo $#' sh | head -3
1
1
1
xargs: sh: terminated with signal 13; aborting

Apenas alguns argumentos ridiculamente pequenos para -s farão com que -x seja acionado:

fz11_2$ jot 10000 1 | xargs -s 19 -n 10000 -x sh -c 'echo $#' sh | head -3
xargs: insufficient space for arguments

O padrão parece corresponder ao comportamento xargs do GNU:

-n  number
      Invoke utility using as many standard input arguments as
      possible, up to number (a positive decimal integer) arguments
      maximum. Fewer arguments shall be used if:
      + The command line length accumulated exceeds the size specified
        by the -s option (or {LINE_MAX} if there is no -s option).
      + The last iteration has fewer than number, but not zero,
        operands remaining.
-x
      Terminate if a constructed command line will not fit in the
      implied or specified size (see the -s option above).
    
por 23.11.2018 / 16:42
2

O que você descreve já é o padrão. xargs -n fornece um número máximo de argumentos para usar, mas se você não especificá-lo, xargs usará tantos argumentos quanto possível.

Você mencionou o limite do sistema em sua pergunta e não espera / precisa contornar isso. xargs considera o limite do sistema, mas pode usar um valor menor, conforme descrito no uso da opção --max-chars ( -s ):

 --max-chars=max-chars
 -s max-chars

Use at most max-chars characters per command line, including the command and initial-arguments and the terminating nulls at the ends of the argument strings. The largest allowed value is system-dependent, and is calculated as the argument length limit for exec, less the size of your environment, less 2048 bytes of headroom. If this value is more than 128KiB, 128Kib is used as the default value; otherwise, the default value is the maximum. 1KiB is 1024 bytes.

(ênfase minha)

Você pode verificar o que é com echo | xargs --show-limits . Exemplo de saída:

Your environment variables take up 3712 bytes
POSIX upper limit on argument length (this system): 2091392
POSIX smallest allowable upper limit on argument length (all systems): 4096
Maximum length of command we could actually use: 2087680
Size of command buffer we are actually using: 131072
Maximum parallelism (--max-procs must be no greater): 2147483647

Isso mostra que meu limite rígido é 2087680 (pouco menos de 2 MiB). Se o seu sistema também permitir mais de 128K, você poderá evitar a divisão usando xargs -s 2087680 (ajuste de acordo com seu próprio limite).

Versões antigas de xargs permitem até que você forneça um limite maior do que o sistema operacional permite. Com o 4.4.2, um aviso foi exibido, mas o valor especificado aceito de qualquer maneira, e você obtém o mesmo erro que você veria ao executar o comando diretamente:

$ seq 1 2000000 | xargs -s 2100000 echo | wc -l
xargs: value for -s option should be < 2092927
xargs: echo: Argument list too long
0

Mas nas versões 4.6, o limite rígido é respeitado:

$ seq 1 2000000 | xargs -s 2100000 echo | wc -l
xargs: value for -s option should be <= 2091392
23
    
por 23.11.2018 / 15:31
2

Alguns esclarecimentos:

Em

cmd1 $(cmd2)

Aqui está o que acontece com a saída de cmd2 :

  • em bash , os NULs são descartados. YMMV com outros shells (em zsh eles são preservados, alguns shells ignoram tudo após o primeiro NUL).
  • os caracteres da nova linha são removidos
  • o resultado é dividido de acordo com $IFS cujo valor padrão é SPC, TAB, NL na maioria das shells, SPC, TAB, NL, NUL em zsh .
  • cada trabalho está sujeito a globbing (exceto em zsh ). Por exemplo, * se torna a lista de arquivos não ocultos no diretório atual.
  • a lista resultante é passada como argumentos para cmd1 . A chamada do sistema execve() para fazer isso falhará se essa lista (combinada com o ambiente) for muito grande (e no caso do Linux também se qualquer argumento único for maior que 128 KiB).

Em

cmd2 | xargs cmd1

Veja o que acontece com a saída de cmd2 :

  • em algumas implementações, os bytes dessa saída são interpretados como caracteres. Por exemplo, em uma localidade usando um conjunto de caracteres UTF-8, a seqüência e2 80 86 bytes compõe um caractere U + 2006 que é o caractere SIX-PER-EM SPACE , geralmente considerado como um caractere em branco. Se alguns bytes não formam caracteres válidos, dependendo da implementação, eles podem ser removidos ou causar uma falha, o comportamento não é especificado pelo POSIX se a entrada não for texto
  • Se houver bytes NUL, novamente, isso não é texto, então o comportamento varia com a implementação
  • Se houver linhas maiores que LINE_MAX (que em alguns sistemas podem ser tão baixas quanto 1024), o comportamento também não é especificado.
  • xargs analisa sua entrada conforme seu próprio formato esperado. Nesse formato, espaço em branco e caracteres de nova linha são entendidos como delimitadores (portanto, novas linhas finais seriam divididas da mesma forma), cuja lista varia de acordo com a implementação (incluindo U + 2006, por exemplo) e '...' , "..." e backslash são usados como operadores de cotação para evitar que esses delimitadores e outros operadores de cotação sejam tratados especialmente.
  • que resulta em uma lista ou palavras .
  • em algumas implementações, se qualquer uma dessas palavras for _ , será tratada como um indicador lógico de fim de arquivo e tudo depois de ser ignorado.
  • se qualquer uma dessas palavras for maior que algum limite (em muitos sistemas, com menos de 255 bytes), o comportamento não é especificado.
  • as palavras restantes são passadas como argumentos para cmd1 , mas se essa lista for muito grande, em vez de falhar como no caso de substituição de comando, xargs tenta encaixar o máximo possível (com um pouco de margem que varia com a implementação) para que execve() seja bem-sucedido e executa o comando quantas vezes forem necessárias para usar todos os argumentos.

Agora:

  • se você quiser passar toda a saída de cmd2 como um argumento para cmd1 :
    • se essa saída contiver caracteres NUL, você não pode, pois essa é uma limitação da chamada do sistema execve() , não da substituição de comandos nem xargs .
    • se essa saída for maior que o limite ARG_MAX, você não poderá (embora haja maneiras de aumentar esse limite em alguns sistemas), novamente uma limitação de exexve() . Você poderia se cmd1 fosse, por exemplo, uma construção ou função de seu shell (como não há execve() envolvido), mas mesmo assim, na prática, somente zsh suporta argumentos com NUL nesse caso.
    • no Linux, se for maior que 128KiB, você não pode. Esse limite não pode ser aumentado.
    • Caso contrário (se não houver nenhuma NUL e ela for pequena o suficiente), você sempre poderá fazer: out=$(cmd2; echo .); cmd1 "${out%.}" ou com GNU xargs : cmd2 | xargs -0 cmd1
por 23.11.2018 / 18:39

Tags