Isso é o que o xargs
faz.
... | xargs command
Eu quero executar um comando java uma vez para cada correspondência de ls | grep pattern -
. Neste caso, acho que eu poderia fazer find pattern -exec java MyProg '{}' \;
, mas estou curioso sobre o caso geral - existe uma maneira fácil de dizer "executar um comando uma vez para cada linha de entrada padrão"? (Em peixe ou bash.)
A resposta aceita tem a ideia certa, mas a chave é passar xargs
a opção -n1
, que significa "Execute o comando uma vez por linha de saída:"
cat file... | xargs -n1 command
Ou, para um único arquivo de entrada, você pode evitar o pipe a partir de cat
e seguir:
<file xargs -n1 command
No Bash ou qualquer outro shell do estilo Bourne (ash, ksh, zsh,…):
while read -r line; do command "$line"; done
read -r
lê uma única linha da entrada padrão ( read
sem -r
interpreta barras invertidas, você não quer isso). Assim, você pode fazer o seguinte:
$ command | while read -r line; do command "$line"; done
$ while read -r line; do command "$line"; done <file
Concordo com Keith, xargs é a ferramenta mais geral para o trabalho.
Eu costumo usar uma abordagem de 3 passos.
Existem maneiras menores e mais rápidas, mas isso quase sempre funciona.
Um exemplo simples:
ls |
grep xls |
awk '{print "MyJavaProg --arg1 42 --arg2 "$1"ls |
grep xls |
awk '{print "MyJavaProg --arg1 42 --arg2 "$1"%pre%"}' |
xargs -0 bash -c
"}' |
xargs -0 bash -c
as duas primeiras linhas selecionam alguns arquivos para trabalhar, então awk prepara uma string legal com um comando para executar e alguns argumentos e $ 1 é a primeira entrada de coluna do pipe. E finalmente eu me certifico de que o xargs envie essa string para o bash que acabou de executá-lo.
É um pouco exagerado, mas essa receita me ajudou em muitos lugares, já que é muito flexível.
O GNU Parallel é feito para esse tipo de tarefa. O uso mais simples é:
cat stuff | grep pattern | parallel java MyProg
Assista ao vídeo de introdução para saber mais: link
Além disso, while read
loop no fish shell (eu suponho que você queira shell de peixe, considerando que você usou peixe tag).
command | while read line
command $line
end
Poucos pontos a serem observados.
read
não aceita o argumento -r
, e ele não interpreta suas barras invertidas, para facilitar o uso do caso de uso mais comum. $line
, pois, diferentemente do bash, o peixe não separa as variáveis por espaços. command
por si só é um erro de sintaxe (para capturar esse uso de argumentos de espaço reservado). Substitua-o pelo comando real. Se você precisa controlar exatamente onde o argumento de entrada é inserido em sua linha de comando ou se você precisa repeti-lo várias vezes, então você precisa usar xargs -I{}
.
Crie uma estrutura de pastas vazia em another_folder
que espelhe as subpastas no diretório atual:
ls -1d ./*/ | xargs -I{} mkdir another_folder/{}
EXEMPLO # 2
Aplique uma operação em uma lista de arquivos proveniente de stdin, nesse caso, faça uma cópia de cada arquivo .html
anexando uma extensão .bak
:
find . -iname "*.html" | xargs -I{} cp {} {}.bak
Na página xargs
man do MacOS / BSD :
-I replstr
Execute utility for each input line, replacing one or more occurrences of
replstr in up to replacements (or 5 if no -R flag is specified) arguments
to utility with the entire line of input. The resulting arguments, after
replacement is done, will not be allowed to grow beyond 255 bytes; this is
implemented by concatenating as much of the argument containing replstr as
possible, to the constructed arguments to utility, up to 255 bytes. The
255 byte limit does not apply to arguments to utility which do not contain
replstr, and furthermore, no replacement will be done on utility itself.
Implies -x.
Página xargs
man do Linux:
-I replace-str
Replace occurrences of replace-str in the initial-
arguments with names read from standard input. Al‐
so, unquoted blanks do not terminate input items;
instead the separator is the newline character.
Implies -x and -L 1.
Ao lidar com insumos potencialmente não-autorizados, eu gosto de ver todo o trabalho 'soletrado' linha por linha para inspeção visual antes de executá-lo (especialmente quando é algo destrutivo como limpar a caixa de correio das pessoas).
Então, o que eu faço é gerar uma lista de parâmetros (ou seja, nomes de usuários), alimentá-los em um arquivo em um registro por linha, assim:
johndoe
jamessmith
janebrown
Então eu abro a lista em vim
, e mangle-o com pesquisa e substituo expressões até obter uma lista de comandos completos que precisam ser executados, assim:
/bin/rm -fr /home/johndoe
/bin/rm -fr /home/jamessmith
Desta forma, se sua regex estiver incompleta, você verá em qual comando haverá problemas potenciais (por exemplo, /bin/rm -fr johnnyo connor
). Dessa forma, você pode desfazer sua regex e tentar novamente com uma versão mais confiável dela. Atrapalhar o nome é notório por isso, porque é difícil cuidar de todos os casos como Van Gogh, O'Connors, St. Clair e Smith-Wesson.
Ter set hlsearch
é útil para fazer isso em vim
, pois ele realçará todas as correspondências, para que você possa identificar facilmente se não corresponder ou corresponder de maneira não intencional.
Uma vez que seu regex é perfeito e captura todos os casos que você pode testar / pensar, então eu normalmente o converto em uma expressão sed para que ele possa ser totalmente automatizado para outra execução.
Para casos em que o número de linhas de entrada o impede de fazer uma inspeção visual, recomendo enfaticamente que o comando seja exibido na tela (ou melhor ainda, um log) antes de ser executado, portanto, se ocorrer erros, você saberá exatamente qual comando causou a falha. Então você pode voltar para sua regex original e ajustar mais uma vez.
Se um programa ignorar o pipe, mas aceitar arquivos como argumentos, você poderá simplesmente apontá-lo para o arquivo especial /dev/stdin
.
Eu não estou familiarizado com o java, mas aqui está um exemplo de como você faria isso para o bash:
$ echo $'pwd \n cd / \n pwd' |bash /dev/stdin
/home/rolf
/
O $ é necessário para o bash converter \n
em novas linhas. Não sei porquê.
Eu prefiro isso - permitindo comandos com várias linhas e código claro
find -type f -name filenam-pattern* | while read -r F
do
echo $F
cat $F | grep 'some text'
done
ref link