Como fornecer uma lista separada por vírgula como argumentos para o próximo comando

9

Eu tenho um script s1que gera uma lista de números separados por ',' por exemplo 1,2,3,4. Agora, quero atribuir esses números ao script s2como argumentos, para que o s2 seja executado em cada um deles e produza o resultado em uma linha separada. Por exemplo, se s2 multiplica números por dois, este seria o resultado que estou procurando:

2
4
6
8

O que estou fazendo agora é:

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

Mas sinto que estou fazendo isso de uma maneira tão tola! Então, minha pergunta é:

Qual é a maneira correta de fazer isso?

Meu problema com a minha solução é que ele está chamando xargs duas vezes e iterando sobre a entrada duas vezes, o que não é razoável para meus olhos, é claro, por meio do desempenho! A resposta xargs -d "," -n1parece boa, mas não tenho certeza se está apenas repetindo uma vez. Caso isso aconteça, verifique isso em sua resposta e eu aceito. A propósito, prefiro não usar o Perl, pois ele ainda está iterando duas vezes e também o Perl pode não existir em muitos sistemas.

yukashima huksay
fonte
1
Se está funcionando, por que chamar isso de tolo? Se o tempo de execução é importante, então isso é outra licença importa mais aqui
George Udosen
2
Tente istos1 | xargs -d "," -n1 s2
George Udosen
1
Suspeito que parte do problema esteja entendendo mal o impacto da iteração. A iteração sobre os elementos em algo como uma matriz associativa é ruim por causa da despesa de caminhar nessa estrutura de dados, mas "iterar" em geral não é inerentemente ruim. Especificamente, a leitura de dados linha por linha, conforme aparece no STDIN, não é um grande problema de desempenho. A questão do desempenho aqui é mais o custo de gerar um novo processo e configurar o pipeline. Desde que você não faça isso com frequência (como em um loop), se preocupar com o desempenho do xargs é provavelmente um exemplo de otimização prematura.
dannysauer

Respostas:

18

Isso deve funcionar igualmente:

s1 | xargs -d "," -n1 s2

Caso de teste:

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

Resultado:

1
2
3
4

Se s1gerar essa lista seguida por um caractere de nova linha, você deverá removê-la, caso contrário a última chamada estaria com em 4\nvez de 4:

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

Se s2puder aceitar vários argumentos, você poderá:

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

que substitui temporariamente o IFS por vírgula, tudo em um subshell, para que s2a saída seja s1dividida por vírgulas. O subshell é uma maneira abreviada de alterar o IFS sem salvar o valor anterior ou redefini-lo.

Uma versão anterior desta resposta estava incorreta, provavelmente devido a uma configuração restante do IFS, corrompendo os resultados. Obrigado a ilkkachu por apontar meu erro .

Para fazer um loop manual sobre as saídas e fornecê-las individualmente s2, demonstrando aqui a economia e a redefinição do IFS:

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

ou execute os bits IFS em um subshell como antes:

(
IFS=,
for output in $(./s1); do ./s2 "$output"; done
)
Jeff Schaller
fonte
1
Você tem certeza sobre o primeiro? bash -c 'IFS=, printf "%s\n" $(echo 1,2,3)'imprime 1,2,3no meu sistema, ou seja, não há divisão.
Ilkkachu
Vou testar novamente um pouco, mas suspeito de comportamento diferente com base em programas internos versus externos.
Jeff Schaller
mesmo com /usr/bin/printfe/bin/echo
ilkkachu
1
@ilkkachu Você está correto, a divisão de palavras ocorre antes que o IFS seja reatribuído. Nesse caso (IFS=,; printf "%s\n" $(echo 1,2,3)), por outro lado, deve funcionar.
undercat aplaude Monica
1

Tente o seguinte:

s1 | perl -pe 's/,/\n/g' | xargs -n1 s2
Gilles Quenot
fonte
( O espaço ...)
Peter Mortensen
3
Por que não simplesmente tr ',' '\n'? Não há necessidade de chamar algo tão (relativamente) pesado como Perl e expressões regulares.
David Foerster