Tenho um arquivo CSV e quero executar um comando para cada linha, usando os campos do arquivo como argumentos separados.
Por exemplo, dado o seguinte arquivo:
foo,42,red
bar,13,blue
baz,27,green
Quero executar os seguintes comandos um após o outro:
my_cmd --arg1 42 --arg2 foo --arg3 red
my_cmd --arg1 13 --arg2 bar --arg3 blue
my_cmd --arg1 27 --arg2 baz --arg3 green
Qual é a maneira mais fácil de conseguir isso? Parece que pode ser possível com xargs, mas não consegui descobrir como exatamente.
O GNU
parallel
pode ler csv diretamente e possui substituição de itens incorporada.Mais ou menos retirado diretamente de
man parallel
:Adicione
-j1
antesmy_cmd
para que essas invocações sejam executadas uma após a outra. Ou não, e faça com que sejam executadas em paralelo.(no Debian e Fedora, está no pacote chamado
parallel
, não emmoreutils
oumoreutils-parallel
)Obrigado, Ole Tange!
Acho o awk um pouco mais fácil do que mexer no xargs, então costumo montar os argumentos usando o awk e depois passá-los para o xargs:
Aqui
-L1
diz "execute um comando por linha de entrada".O seguinte primeiro usa Miller (
mlr
) para converter a entrada CSV sem cabeçalho para a saída JSONL (linhas de objetos JSON únicos). Ojq
processador JSON então lê esses objetos e emite suas partes como argumentos para um comando. A saída é um código shell que pode sereval
-ed.O
@sh
operador output tenta citar os dados fornecidos de uma forma que seria apropriada para um shell. Não é infalível, mas tende a fazer um bom trabalho na maioria das vezes.Você também pode executar coisas diretamente do Miller, mas não sei o quão bem sua
exec()
função lida com valores que precisam ser citados no shell (ou se isso é mesmo um problema). Posso voltar mais tarde e revisar isso quando e se eu tiver tempo para testar isso.Usando Raku (anteriormente conhecido como Perl_6)
...com o módulo Raku
Text::CSV
:Raku é uma linguagem de programação da família Perl que apresenta algumas funções legais para invocar comandos externos. As duas opções são calling
shell
ou callingrun
. De acordo com a Docs, chamarrun
é mais seguro.Acima, quando você declara o
$parser
objeto, você pode definir vários parâmetros, como aceitar um separador sem vírgula (exemplo:my $parser = Text::CSV.new(sep => "|");
). Então o arquivo é lido/analisado linha a linha comgetline()
. Uma saída simples é mostrada acima usandoecho
.Exemplo de entrada:
Saída de exemplo (com
echo
):Abaixo, usando
run "printf", "%s\t", .[0].uc, .[1], .[2].uc given $_; run "printf", "\n";
, separando a saída da coluna com\t
tabulações. Note que aqui adicionamos.uc
a maiúsculas a primeira e a terceira colunas, para mostrar que você ainda pode limpar o texto se precisar (antes de invocar seumy_cmd
):Saída de exemplo (com
printf
):Finalmente, você pode tirar arquivos de entrada da linha de comando usando
$*ARGFILES
a variável dinâmica do Raku. Obviamente, você substituirá seumy_cmd
no lugar deprintf
abaixo:Caso contrário, veja o primeiro link abaixo para saber como salvar a saída em um objeto "Proc" (processo) do Raku, ou o segundo link abaixo para usar "Proc::Async" (interface de processo assíncrona).
https://docs.raku.org/type/Proc
https://docs.raku.org/type/Proc/Async
https://raku.org
Separando a seleção/ordenação de campos (
f=...
abaixo) da adição--argN
(do loop) para que seja fácil modificar campos e/ou ordenar e potencialmente usar o mesmo campo várias vezes, usando anyawk
e um POSIXxargs
:Remova
echo
quando terminar o teste.Dado que, alterar a ordem e duplicar campos é tão simples quanto alterar
f='...'
:Os
\"
s ao redor%s
são para garantir quexargs
os campos que contêm espaços sejam manipulados corretamente, caso contrário, um campo comoa b
seria dividido em 2 argumentos separados paramy_cmd
.