Como executar um comando várias vezes, usando o bash shell?


153

Existe uma maneira de executar um comando (por exemplo ps aux|grep someprocess) por n vezes?

Algo como:

run -n 10  'ps aux|grep someprocess'

Eu quero usá-lo interativamente, por favor, não poste scripts.

Atualização: A razão pela qual estou perguntando isso é que trabalho em muitas máquinas e não quero importar todos os meus scripts adaptados etc. em todas as caixas para obter a mesma funcionalidade em todas as máquinas.


Respostas:


197

Eu não acho que exista um comando ou shell embutido para isso, pois é um subconjunto trivial para o qual o forloop de shell Bourne foi projetado e a implementação de um comando como esse é, portanto, bastante simples.

Para iniciantes, você pode usar um forloop fictício :

for i in `seq 10`; do command; done

Ou de forma equivalente, de acordo com a sugestão de JimB, usando o Bash embutido para gerar sequências:

for i in {1..10}; do command; done

Isso itera dez vezes a execução de commandcada vez - pode ser um canal ou uma série de comandos separados por ;ou &&. Você pode usar a $ivariável para saber em qual iteração você está.

Se você considerar este script de uma linha um script e, por algum motivo não especificado (mas talvez válido), indesejável, poderá implementá-lo como um comando, talvez algo como isto no seu .bashrc (não testado):

#function run
run() {
    number=$1
    shift
    for i in `seq $number`; do
      $@
    done
}

Uso:

run 10 command

Exemplo:

run 5 echo 'Hello World!'

16
@mahatmanich, Um loop não é um script. Não há nada impedindo você de usar ... em um terminal interativo.
Zoredache 24/05

3
Bem, o one-liner acima é o tipo de maneira padrão de fazê-lo e é bastante simples. Por que isso não é bom para você? Talvez você esteja fazendo a pergunta errada? Qual é o objetivo principal de seus scripts ou ações que você deseja repeti-los várias vezes? Talvez exista uma solução melhor se colocarmos o problema de uma maneira diferente.
Patkos Csaba

6
@mahatmanich - for é o bash embutido para iteração.
22811 JimB

3
@Eduardo Ivanec - FYI, bash possui um intervalo incorporado como seq: {1..10}
JimB

1
Parece que a sequência de colchete embutido não suporta substituição de variável (consulte stackoverflow.com/a/3737773/713554 ). A função que você deu funciona perfeitamente quando colocada, .bashrcse {1..$number} for trocada por ela `seq $number`.
29312 Leo

29

ps aux | grep someprocessparece que você deseja assistir a alterações de um programa por um tempo fixo. Eduardo deu uma resposta que responde exatamente à sua pergunta, mas existe uma alternativa watch:

watch 'ps aux | grep someprocess'

Observe que eu coloquei o comando entre aspas simples para evitar que o shell interprete o comando como "run watch ps aux" e canalize o resultado grep someprocess. Outra maneira de executar o comando anterior seria:

watch ps aux \| grep someprocess

Por padrão, watchatualiza a cada dois segundos, que pode ser alterado usando a -nopção Por exemplo, se você quiser ter um intervalo de 1 segundo:

watch -n 1 'ps aux | grep someprocess'

21

Apenas por diversão

pgrep ssh ;!!;!!;!!;!!;!!;!!

Alguém pode explicar isso, por favor? Parece muito intrigante.
Daniel Vartanov

4
; é apenas separador de comandos. !! repetir o último comando no bash. Então, execute o 'pgrep ssh' e repita-o 6 vezes.
Piotr Kukielka

Embora isso seja instrutivo, não é muito intuitivo. One-liner forparece o melhor caminho. De qualquer forma, obrigado pela resposta.
erm3nda

1
Não cmd ;!!em uma linha basta repetir o comando inserido antes da linha atual? Teria que ser duas historyentradas separadas .
Joel Purra

substitua ;por &&para parar na primeira falha.
user881300 6/10

16

semelhante às respostas anteriores, mas não requer o loop for:

seq 10 | xargs -I -- echo "hello"

saída de pipe de seq para xargs sem argumentos ou opções


2
Isso fará apenas uma execução echo "hello" 1 2 3 4 5 6 7 8 9 10. Você precisa executar xargs com -n 1um processo para cada entrada (número) e com -P 10execução paralela (10 processos paralelos). Terminando com seq 10 | xargs -n 1 -P 10 echo.
Alexander Klimetschek

16

Tente o seguinte:

yes ls | head -n5 | bash

Isso requer que o comando seja executado em um sub-shell, uma pequena penalidade de desempenho. YMMV. Basicamente, você obtém o comando "yes" para repetir a string "ls" N vezes; enquanto "head -n5" encerrou o loop em 5 repetições. O canal final envia o comando para o shell de sua escolha.

cshCascas semelhantes a incidentes têm um repeatcomando interno. Você pode usar isso para executar seu comando em um sub-shell bash!


5

Maneira POSIX

http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_06_04_01

x=10
while [ $x -gt 0 ]; do
    command
    x=$(($x-1))
done

que naturalmente poderia ser transformado em um forro:

x=10; while [ $x -gt 0 ]; do command; x=$(($x-1)); done

e parece ser a maneira mais portátil e, portanto, menos provável de fazer você instalar programas.

Os seguintes são menos portáteis:

  • expansão de chave {1..10}: específica do bash e geraria uma linha enorme se o argumento fosse grande
  • seq: GNU
  • yes: GNU

E se você se cansar da portabilidade, considere o GNU parallel:

sudo apt-get install parallel
seq 100 | parallel echo {}

que executa comandos em paralelo e tem muitas opções interessantes.

Consulte também: https://stackoverflow.com/questions/169511/how-do-i-iterate-over-a-range-of-numbers-defined-by-variables-in-bash


O GNU Parallel foi desenvolvido para ser muito portátil. sh, zsh, ksh, e bashtodos são bem suportados na maioria das plataformas. csh, tcsh, fishtem algum apoio. Usando parallel --embedvocê pode até criar um script que possa ser facilmente movido para outro sistema que não tenha o GNU Parallel instalado.
precisa

2

Uma implementação de shell de peixes da runfunção de @ eduardo-ivanec acima

function run
  set number $argv[1]
  for i in (seq $number)
    eval $argv[2..-1]
  end
end

0

Isso seria 'watch' - deveria fazer parte de quase todos os sistemas unix usando procps

Você pode ver alterações no FS ou movimentos interessantes em um sistema. Por exemplo,

watch "df -hP | grep /tmp" 

Basta colocar seus comandos como argumento (consulte man watch)


Já existe uma resposta com relógio ... ver logo abaixo ...
mahatmanich

0

No macOS, você pode usar o repeatcomando

repeat 10 ps aux | grep someprocess
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.