Alguém poderia, por favor, fornecer o código para fazer o seguinte: Suponha que exista um diretório de arquivos, todos os quais precisam ser executados através de um programa. O programa gera os resultados para a saída padrão. Eu preciso de um script que entre em um diretório, execute o comando em cada arquivo e concatize a saída em um grande arquivo de saída.
Por exemplo, para executar o comando em 1 arquivo:
$ cmd [option] [filename] > results.out
ls <directory> | xargs cmd [options] {filenames put in here automatically by xargs} [more arguments] > results.out
ls
para dirigirxargs
. Secmd
está escrito com competência, talvez você possa simplesmente fazercmd <wildcard>
.Respostas:
O seguinte código bash passará $ file para o comando em que $ file representará todos os arquivos em / dir
Exemplo
fonte
/dir/
, o loop ainda será executado uma vez com o valor '*' para$file
, o que pode ser indesejável. Para evitar isso, ative o nullglob pela duração do loop. Adicione esta linha antes do loopshopt -s nullglob
e esta linha após o loopshopt -u nullglob #revert nullglob back to it's normal default state
.done >results.out
(e provavelmente você poderá sobrescrever em vez de acrescentar, como eu assumi aqui).Que tal agora:
-maxdepth 1
O argumento impede que a localização descida recursivamente em qualquer subdiretório. (Se você deseja que esses diretórios aninhados sejam processados, você pode omitir isso.)-type -f
especifica que apenas arquivos simples serão processados.-exec cmd option {}
diz para executarcmd
com o especificadooption
para cada arquivo encontrado, com o nome do arquivo substituído por{}
\;
denota o fim do comando.cmd
execuções individuais é redirecionada pararesults.out
No entanto, se você se importa com a ordem em que os arquivos são processados, é melhor escrever um loop. Acho que
find
processa os arquivos em ordem de inode (embora eu possa estar errado sobre isso), o que pode não ser o que você deseja.fonte
stat
esort
, que obviamente dependem do que é o critério de classificação.-exec
opção? Preciso envolvê-los em aspas simples ou algo assim?find
é sempre a melhor opção,-name
pois você pode filtrar por padrão de nome de arquivo com a opção e pode fazê-lo em um único comando.-exec
opções:find . -name "*.txt" -exec echo {} \; -exec grep banana {} \;
Estou fazendo isso no meu raspberry pi na linha de comando executando:
fonte
As respostas aceitas / com alta votação são ótimas, mas faltam alguns detalhes minuciosos. Esta postagem aborda os casos de como lidar melhor quando a expansão do nome do caminho do shell (glob) falha, quando os nomes de arquivos contêm linhas de linha / símbolos de traço incorporados e movendo a direção de saída do comando para fora do loop for ao gravar os resultados em um Arquivo.
Ao executar a expansão glob do shell usando
*
, é possível que a expansão falhe se não houver arquivos presentes no diretório e uma cadeia glob não expandida for passada para o comando a ser executado, o que pode ter resultados indesejáveis. Obash
shell fornece uma opção de shell estendida para esse usonullglob
. Portanto, o loop se torna basicamente o seguinte dentro do diretório que contém seus arquivosIsso permite que você saia com segurança do loop for quando a expressão
./*
não retornar nenhum arquivo (se o diretório estiver vazio)ou de maneira compatível com POSIX (
nullglob
ébash
específico)Isso permite que você entre no loop quando a expressão falhar pela primeira vez e a condição
[ -f "$file" ]
verificar se a cadeia não expandida./*
é um nome de arquivo válido nesse diretório, o que não seria. Portanto, nessa falha de condição, usandocontinue
retomamos ofor
loop que não será executado posteriormente.Observe também o uso de
--
pouco antes de passar o argumento do nome do arquivo. Isso é necessário porque, como observado anteriormente, os nomes de arquivos do shell podem conter traços em qualquer lugar do nome do arquivo. Alguns dos comandos do shell interpretam isso e os tratam como uma opção de comando quando o nome não é citado corretamente e executa o comando pensando se o sinalizador for fornecido.Os
--
sinais indicam as opções de final de linha de comando nesse caso, o que significa que o comando não deve analisar nenhuma sequência além deste ponto como sinalizadores de comando, mas apenas como nomes de arquivos.A citação dupla dos nomes de arquivos resolve adequadamente os casos em que os nomes contêm caracteres glob ou espaços em branco. Mas os nomes de arquivos * nix também podem conter novas linhas. Portanto, limitamos os nomes de arquivos com o único caractere que não pode fazer parte de um nome de arquivo válido - o byte nulo (
\0
). Comobash
internamente usaC
cadeias de estilo nas quais os bytes nulos são usados para indicar o final da cadeia, é o candidato certo para isso.Portanto, usando a
printf
opção do shell para delimitar arquivos com esse byte NULL usando a-d
opção deread
comando, podemos fazer abaixoO
nullglob
e oprintf
são agrupados, o(..)
que significa que eles são basicamente executados em um sub-shell (shell filho), porque, para evitar anullglob
opção de refletir no shell pai, uma vez que o comando é encerrado. A-d ''
opção deread
comando não é compatível com POSIX, portanto, é necessário umbash
shell para que isso seja feito. Usando ofind
comando, isso pode ser feito comoPara
find
implementações que não suportam-print0
(além das implementações GNU e FreeBSD), isso pode ser emulado usandoprintf
Outra correção importante é mover a redirecionamento para fora do loop for para reduzir um alto número de E / S de arquivo. Quando usado dentro do loop, o shell deve executar chamadas do sistema duas vezes para cada iteração do loop for, uma vez para abrir e outra para fechar o descritor de arquivo associado ao arquivo. Isso se tornará um gargalo no desempenho para executar iterações grandes. A sugestão recomendada seria movê-lo para fora do loop.
Estendendo o código acima com essas correções, você pode fazer
que basicamente coloca o conteúdo do seu comando para cada iteração da entrada do arquivo em stdout e, quando o loop terminar, abra o arquivo de destino uma vez para gravar o conteúdo do stdout e salvá-lo. A
find
versão equivalente do mesmo seriafonte
Uma maneira rápida e suja de realizar o trabalho às vezes é:
Por exemplo, para encontrar o número de linhas em todos os arquivos no diretório atual, você pode:
fonte
~/.local/share/steam
. Funcionou. Excluiu tudo no sistema de propriedade do usuário". relatório de erro.Eu precisava copiar todos os arquivos .md de um diretório para outro, então aqui está o que eu fiz.
for i in **/*.md;do mkdir -p ../docs/"$i" && rm -r ../docs/"$i" && cp "$i" "../docs/$i" && echo "$i -> ../docs/$i"; done
O que é muito difícil de ler, então vamos detalhar.
primeiro CD no diretório com seus arquivos,
for i in **/*.md;
para cada arquivo em seu padrãomkdir -p ../docs/"$i"
crie esse diretório em uma pasta docs fora da pasta que contém seus arquivos. O que cria uma pasta extra com o mesmo nome desse arquivo.rm -r ../docs/"$i"
remova a pasta extra criada como resultado demkdir -p
cp "$i" "../docs/$i"
Copie o arquivo realecho "$i -> ../docs/$i"
Eco o que você fez; done
Viva feliz para semprefonte
**
funcionar, aglobstar
opção shell precisa ser configurada:shopt -s globstar
Você pode usar
xarg
ls | xargs -L 1 -d '\n' your-desired-command
-L 1
faz passar 1 item de cada vez-d '\n'
make output ofls
é dividido com base na nova linha.fonte
Com base na abordagem de Jim Lewis:
Aqui está uma solução rápida, usando
find
e também classificando os arquivos pela data de modificação:Para classificação, consulte:
http://www.commandlinefu.com/commands/view/5720/find-files-and-list-them-sorted-by-modification-time
fonte
-print0
parafind
e-0
para oxargs
qual usar caracteres nulos em vez de qualquer espaço em branco (incluindo novas linhas).-print0
é algo que ajuda, mas toda a necessidades de dutos para usar algo como isso, esort
não éEu acho que a solução simples é:
fonte
Profundidade máxima
Eu achei que funciona muito bem com a resposta de Jim Lewis, basta adicionar um pouco assim:
Ordem de classificação
Se você deseja executar em ordem de classificação, modifique-o assim:
Apenas por exemplo, isso será executado na seguinte ordem:
Profundidade Ilimitada
Se você deseja executar em profundidade ilimitada por determinadas condições, pode usar o seguinte:
em seguida, coloque em cima de cada arquivo nos diretórios filhos assim:
e em algum lugar no corpo do arquivo pai:
fonte