Estou procurando escrever um script que tenha um .txt
nome de arquivo como argumento, leia a linha do arquivo por linha e passa cada linha para um comando. Por exemplo, ele executa command --option "LINE 1"
, depois command --option "LINE 2"
, etc. A saída do comando está gravado em outro arquivo. Como faço para fazer isso? Não sei por onde começar.
Resposta
Use while read
loop:
: > another_file ## Truncate file. while IFS= read -r LINE; do command --option "$LINE" >> another_file done < file
Outra é redirecionar a saída por bloco:
while IFS= read -r LINE; do command --option "$LINE" done < file > another_file
Por último é abrir o arquivo:
exec 4> another_file while IFS= read -r LINE; do command --option "$LINE" >&4 echo xyz ## Another optional command that sends output to stdout. done < file
Se um dos comandos lê a entrada, seria uma boa ideia de usar outro fd para entrada de modo que os comandos não comam (aqui assumindo ksh
, zsh
ou bash
para -u 3
, use <&3
em vez de portável):
while IFS= read -ru 3 LINE; do ... done 3< file
Fin aliado para aceitar argumentos, você pode fazer:
#!/bin/bash FILE=$1 ANOTHER_FILE=$2 exec 4> "$ANOTHER_FILE" while IFS= read -ru 3 LINE; do command --option "$LINE" >&4 done 3< "$FILE"
Qual deles poderia ser executado como:
bash script.sh file another_file
Idéia extra. Com bash
, use readarray
:
readarray -t LINES < "$FILE" for LINE in "${LINES[@]}"; do ... done
Observação: IFS=
pode ser omitido se você não se importar em ter os valores das linhas cortados de espaços à esquerda e à direita.
Resposta
Outra opção é xargs
.
Com GNU xargs
:
xargs -a file -I{} -d"\n" command --option {} other args
{}
é o marcador de posição para a linha de texto.
Outro xargs
geralmente não têm -a
, -d
, mas alguns têm -0
para entrada delimitada por NUL. Com eles, você pode fazer:
< file tr "\n" "\0" | xargs -0 -I{} command --option {} other args
Em sistemas em conformidade com Unix (-I
é opcional em POSIX e apenas necessário para sistemas em conformidade com UNIX), você “precisaria pré-processar a entrada para citar as linhas no formato esperado por xargs
:
< file sed "s/"/"\\""/g;s/.*/"&"/" | xargs -E "" -I{} command --option {} other args
No entanto, observe que algumas xargs
implementações têm um limite muito baixo no tamanho máximo do argumento (255 no Solaris por exemplo, o mínimo permitido pela especificação Unix).
Comentários
Resposta
Manter a precisão à pergunta:
#!/bin/bash # xargs -n param sets how many lines from the input to send to the command # Call command once per line [[ -f $1 ]] && cat $1 | xargs -n1 command --option # Call command with 2 lines as args, such as an openvpn password file # [[ -f $1 ]] && cat $1 | xargs -n2 command --option # Call command with all lines as args # [[ -f $1 ]] && cat $1 | xargs command --option
Comentários
- funcionou como charme 🙂
- +1 adorei, obrigado
Resposta
A melhor resposta que tenho para und is:
for i in `cat`; do "$cmd" "$i"; done < $file
EDITAR:
… quatro anos depois …
depois de vários votos negativos e mais alguma experiência, eu recomendaria o seguinte agora:
xargs -l COMMAND < file
Comentários
- Isso irá invocar o comando uma vez para cada palavra no arquivo. Além disso, você deve sempre citar referências a variáveis de shell (como em
do "$cmd" "$i";
), a menos que tenha um motivo para não fazê-lo; se o arquivo contivesse um*
como uma palavra por si só, seu código executaria$cmd *
, que, claro, executaria o comando uma lista dos arquivos no diretório atual. - @ G-Man, exceto em
zsh
, o`cat`
já expandiria o*
(o$i
não citado ainda poderia expandir algum curinga (uma segunda rodada) se a expansão de`cat`
apresenta alguns). Em qualquer caso, essa abordagem está realmente errada. - +1. Isso funcionará bem, se cada linha contiver apenas a entrada necessária para o comando sendo usado sem espaços.
- +1, solução xargs gostei
- Isso está funcionando! (a edição posterior
xargs -l COMMAND < file
). Obrigado!
Resposta
sed "s/"/"\\\\""/g;s/.*/\$* "&"/" <<\FILE |\ sh -s -- command echo --option all of the{&}se li$n\es "are safely shell quoted and handed to command as its last argument following --option, and, here, before that echo FILE
SAÍDA
--option all of the{&}se li$n\es "are safely shell --option quoted and handed to command as its last argument --option following --option, and, here, before that echo
Resposta
ed file.txt %g/^/s// / 2,$g/^/-,.j 1s/^/command/ wq chmod 755 file.txt ./file.txt
Pegue todos os linhas de um arquivo e passá-los como argumentos para um único comando, ou seja,
command line1 line2 line3 ....
Se você precisar do --option
sinalize para preceder cada linha, altere o segundo comando para:
%g/^/s// --option /
Comentários
- Para quê -1 usando ed … sério?
- Eu não ‘ não votei contra você, mas a pessoa que o fez provavelmente teve estes motivos: (1) Isso não funciona o que a pergunta pede. Isso invoca o comando uma vez com todo o conteúdo do arquivo na linha de comando; em vez de uma vez por linha .(2) Isso não faz nada para lidar com caracteres que são especiais para o shell (que podem estar no arquivo); por exemplo,
'
,"
,<
,>
,;
, etc. (3) Isso cria um arquivo temporário desnecessário. (4) Coisas como essa geralmente são feitas com “aqui documentos”. (5) Seused
comandos são desajeitados; os dois primeiros comandos podem ser reduzidos a%s/^/ /
e%j
.
<file xargs -L 1 -I{} command --option {} other args