cat a.txt | xargs -I % echo %
No exemplo acima, xargs assume echo %
como argumento de comando. Mas, em alguns casos, preciso de vários comandos para processar o argumento em vez de um. Por exemplo:
cat a.txt | xargs -I % {command1; command2; ... }
Mas xargs não aceita este formulário. Uma solução que eu sei é que posso definir uma função para agrupar os comandos, mas não é um pipeline, não a prefiro. Existe outra solução?
while
loop que pode conter vários comandos.Respostas:
... ou, sem um uso inútil de gato :
Para explicar alguns dos pontos mais sutis:
O uso de em
"$arg"
vez de%
(e a ausência de-I
naxargs
linha de comando) é por motivos de segurança: A transmissão de dados nash
lista de argumentos da linha de comando em vez de substituí-la no código impede o conteúdo que os dados possam conter (como$(rm -rf ~)
, por exemplo , exemplo malicioso) de ser executado como código.Da mesma forma, o uso de
-d $'\n'
é uma extensão GNU que fazxargs
com que cada linha do arquivo de entrada trate como um item de dados separado. Isso ou-0
(que espera NULs em vez de novas linhas) é necessário para impedir que os xargs tentem aplicar uma análise semelhante ao shell (mas não totalmente compatível com o shell) ao fluxo que lê. (Se você não possui o GNU xargs, podetr '\n' '\0' <a.txt | xargs -0 ...
obter a leitura orientada a linhas sem-d
).O
_
é um espaço reservado para$0
, de modo que outros valores de dados adicionados porxargs
se tornem$1
e avancem, o que passa a ser o conjunto de valores padrão que umfor
loop percorre.fonte
sh -c
- observe que o ponto-e-vírgula após cada comando não é opcional, mesmo que seja o último comando na lista.command1
ecommand2
; Mais tarde eu percebi que eles não são necessários.}
:sh -c '{ command1; command2; }' -- but it's not required at the end of a command sequence that doesn't use braces:
sh -c 'command1; command2'`%
caractere em algum lugar na sua cadeia de caracteres transmitidash -c
, isso é propenso a vulnerabilidades de segurança: um nome de arquivo contendo$(rm -rf ~)'$(rm -rf ~)'
(e essa é uma substring perfeitamente legal para ter dentro de um nome de arquivo em sistemas de arquivos UNIX comuns!) Causará a alguém um dia muito ruim .Com o GNU Parallel, você pode:
Assista aos vídeos de introdução para saber mais: https://www.youtube.com/playlist?list=PL284C9FF2488BC6D1
Por motivos de segurança, é recomendável usar o gerenciador de pacotes para instalar. Mas se você não puder fazer isso, poderá usar esta instalação de 10 segundos.
A instalação de 10 segundos tentará fazer uma instalação completa; se isso falhar, uma instalação pessoal; se isso falhar, uma instalação mínima.
fonte
Esta é apenas outra abordagem sem xargs nem cat:
fonte
IFS
, ele ignorará os espaços em branco à esquerda e à esquerda nos nomes dos arquivos; a menos que você adicione-r
, os nomes de arquivos com barras invertidas literais terão esses caracteres ignorados.xargs
. (Isso é difícil de expandir para fazer algo semelhante à opção GNUxargs
'-P<n>
)$ command | while read line; do c1 $line; c2 $line; done
Você pode usar
{} = variável para cada linha no arquivo de texto
fonte
file.txt
contiver um dado com$(rm -rf ~)
como substring?Uma coisa que faço é adicionar ao .bashrc / .profile esta função:
então você pode fazer coisas como
que é menos detalhado que xargs ou -exec. Você também pode modificar a função para inserir o valor da leitura em um local arbitrário nos comandos de cada um, se também precisar desse comportamento.
fonte
Eu prefiro o estilo que permite o modo de execução a seco (sem
| sh
):Também funciona com tubos:
fonte
-P
opção ... (Se não, eu uso principalmente-exec
emfind
, desde os meus entradas são principalmente os nomes de arquivo)Um pouco atrasado para a festa.
Eu uso o formato abaixo para compactar meus diretórios com milhares de pequenos arquivos antes de migrar. Se você não precisar de aspas simples dentro de comandos, deve funcionar.
Com algumas modificações, tenho certeza de que será útil para alguém. Testado em
Cygwin
(babun)find .
Encontre aqui-maxdepth 1
Não entre em diretórios filhos! -path .
Excluir. / O caminho do diretório atual-type d
corresponde apenas aos diretórios-print0
Separe a saída por bytes nulos \ 0| xargs
Canalizar para xargs A-0
entrada é bytes separados por nulo O espaço-I @@
reservado é @@. Substitua @@ pela entrada. Comandobash -c '...'
Run Bash{...}
Agrupamento de comandos&&
Executar o próximo comando apenas se o comando anterior tiver sido encerrado com êxito (saída 0)Final
;
é importante, caso contrário, falhará.Resultado:
Atualização de julho de 2018:
Se você gosta de hacks e brincadeiras, aqui está algo interessante:
Resultado:
Explicação:
- Crie um script de liner único e armazene-o em uma variável
-
xargs
leiaa.txt
e execute-o comobash
script-
@@
garanta que toda vez que uma linha inteira seja passada- Colocar
@@
após--
garanta que@@
seja tomado como entrada de parâmetro posicional para obash
comando, não umbash
inícioOPTION
, ou seja, como-c
ele, o que significarun command
--
é mágico, funciona com muitas outras coisas, ou sejassh
, atékubectl
fonte
find . -type f -print0|xargs -r0 -n1 -P20 bash -c 'f="{}";ls -l "$f"; gzip -9 "$f"; ls -l "$f.gz"'
(É um pouco mais fácil ao converter loops)"$@"
é a única maneira de evitar isso ... (com-n1
se você quiser limitar o número de parâmetros))--
é usado pelo shell para dizer que não há mais opções a serem aceitas. Isso permite que haja um-
após o--
também. Você pode obter uma saída muito interessante e confusa se não o fizer, por exemplo,grep -r
quando incluir no padrão-
! O modo como você o pronuncia não esclarece isso, na verdade não explica como isso funciona. Iirc é uma coisa POSIX, mas de qualquer maneira vale a pena apontar isso, eu acho. Apenas algo a considerar. E eu amo esse bônus btw!Esta parece ser a versão mais segura.
(
-0
Pode ser removido e otr
substituído por um redirecionamento (ou o arquivo pode ser substituído com um nulo separados arquivo em vez). É principalmente lá desde que eu uso principalmentexargs
comfind
a-print0
saída) (Isso também pode ser relevante emxargs
versões sem a-0
extensão)É seguro, já que args passará os parâmetros para o shell como uma matriz ao executá-lo. O shell (pelo menos
bash
) os passaria como uma matriz inalterada para os outros processos quando todos forem obtidos usando["$@"][1]
Se você usar
...| xargs -r0 -I{} bash -c 'f="{}"; command "$f";' ''
, a atribuição falhará se a sequência contiver aspas duplas. Isso é verdade para todas as variantes que usam-i
or-I
. (Devido à sua substituição em uma sequência, você sempre pode injetar comandos inserindo caracteres inesperados (como aspas, reticulares ou cifrões) nos dados de entrada)Se os comandos puderem usar apenas um parâmetro por vez:
Ou com processos um pouco menos:
Se você possui o GNU
xargs
ou outro com a-P
extensão e deseja executar 32 processos em paralelo, cada um com no máximo 10 parâmetros para cada comando:Isso deve ser robusto contra quaisquer caracteres especiais na entrada. (Se a entrada for nula separada.) A
tr
versão obterá alguma entrada inválida se algumas das linhas contiverem novas linhas, mas isso é inevitável com um arquivo separado por nova linha.O primeiro parâmetro em branco para
bash -c
é devido a isso: (Nabash
página de manual ) (Obrigado @clacke)fonte
"$@"
bash -c 'command1 "$@"; command2 "$@";' arbitrarytextgoeshere
bash
com-c
obtém primeiro (após os comandos) um argumento que será o nome do processo, depois recebe os argumentos posicionais. Experimentebash -c 'echo "$@" ' 1 2 3 4
e veja o que sai.Outra solução possível que funciona para mim é algo como -
Observe o 'bash' no final - presumo que seja passado como argv [0] para o bash. Sem essa sintaxe, o primeiro parâmetro para cada comando é perdido. Pode ser qualquer palavra.
Exemplo:
fonte
"$@"
, estará dividindo e expandindo a lista de argumentos.Meu BKM atual para isso é
É lamentável que isso use perl, que é menos provável de ser instalado que o bash; mas lida com mais entradas do que a resposta aceita. (Congratulo-me com uma versão onipresente que não depende de perl.)
@ Sugestão de KeithThompson de
é ótimo - a menos que você tenha o caractere de comentário do shell # em sua entrada, nesse caso parte do primeiro comando e todo o segundo comando serão truncados.
Hashes # podem ser bastante comuns, se a entrada for derivada de uma lista de sistemas de arquivos, como ls ou find, e seu editor criar arquivos temporários com # em seu nome.
Exemplo do problema:
Opa, aqui está o problema:
Ahh, isso é melhor:
fonte
ls | xargs -I % sh -c 'echo 1 "%"; echo 2 "%"'