Precisa de um loop para dormir por uma fração de segundo

11

Na minha máquina eu preciso executar um ciclo que repete 1 comando simples que deve ter um atraso expresso em frações de segundo.

Digamos que eu precise de:

  • para salvar um arquivo com uma enumaração crescente (arquivo-0, arquivo-1, arquivo-2, ...) gerado por algo trivial para este exemplo, como time > file-$x
  • Eu preciso fazer isso a cada 1/70 de segundo (como um exemplo) porque eu gostaria de expressar meu tempo com frações de segundo.

Como posso ser realmente preciso e ter tudo expresso com um script bash?

A fração pode gerar uma quantidade indeterminável, eu preciso ser preciso e então eu preciso de pelo menos 4-5 decimais.

    
por user1717079 12.10.2012 / 22:56

5 respostas

9

Para converter de frações para decimais no bash, faça algo como

myvar=$(echo "scale=4; 5/10" | bc)

Em seguida, para fazer um loop nesse valor,

for i in $(seq 1 1000); do sleep $myvar; done

Minha implementação de suspensão no Debian (GNU) parece aceitar valores de suspensão decimais.

Infelizmente ..

Com esse tipo de precisão (4-5 casas decimais), você vai querer algo como um script perl ou um programa compilado; a sobrecarga de chamar qualquer programa dentro do loop vai adicionar muita instabilidade. Chamar o sono em si levará alguns milissegundos.

Considere o seguinte, um 1 / 10.000 de segundo sono, feito 1000 vezes:

time for i in $(seq 1 1000); do sleep 0.0001; done

real    0m2.099s
user    0m3.080s
sys     0m1.464s

O resultado esperado seria 1/10 de segundo. O sono não chega nem perto das tolerâncias que você quer.

link

usando o Perl :: HiRes do Perl, 1000 * 1000 microssegundos:

my $i=0;
for($i=0;$i<=1000;$i++) {
        usleep(1000);
}

real    0m1.133s
user    0m0.024s
sys     0m0.012s

nos dá muito mais perto de um segundo.

    
por 12.10.2012 / 23:00
6

Talvez você possa simplesmente executar

sleep 0.7

?

man 1 sleep

no meu archlinux distro:

DESCRIPTION Pause for NUMBER seconds. SUFFIX may be 's' for seconds (the default), 'm' for minutes, 'h' for hours or 'd' for days. Unlike most implementations that require NUMBER be an integer, here NUMBER may be an arbitrary floating point number. Given two or more arguments, pause for the amount of time specified by the sum of their values.

    
por 12.10.2012 / 22:58
6

Gerar um processo e carregar um novo executável provavelmente levará alguns milissegundos, portanto esse tipo de precisão não faz sentido. Observe também que o tempo de CPU em muitos sistemas é alocado para processos em fatias de até 10 ms.

Dito isto, algumas implementações de sleep recebem números fracionais de segundos, e tanto zsh como ksh93 podem tornar sua variável $SECONDS especial fracionária com typeset -F SECONDS .

Exemplo (zsh):

$ typeset -F SECONDS=0; for ((i=1; i<=70; i++)); do sleep $((1./70)); date +%s.%N; done | { head -n3;echo ..;tail -n3; }; echo $SECONDS
1350076317.374870501
1350076317.391034397
1350076317.407278461
..
1350076318.464585550
1350076318.480887660
1350076318.497133050
1.1393780000

Oops, ele se desviou. Você pode ajustar o tempo de espera com base em $SECONDS :

$ typeset -F SECONDS=0; for ((i=1; i<=70; i++)); do sleep $((i/70. - SECONDS)); date +%s.%N; done | { head -n3;echo ...;tail -n3; }; echo $SECONDS
1350076420.262775654
1350076420.277012997
1350076420.291302750
../..
1350076421.219682227
1350076421.234134663
1350076421.248255685
1.0020580000

Esses 2 milissegundos extras devem ser contabilizados para executar os últimos comandos sleep e date .

Observe também que zsh tem zselect embutido com tempo limite expresso em centésimo de segundo. E o ksh93 tem sleep embutido (e aceita pontos flutuantes) e seu printf pode imprimir data / hora.

$ typeset -F SECONDS=0; for ((i=1; i<=70; i++)); do ((i<4 || i>67)) && printf '%(%S.%N)T\n' now; sleep $((i/70.-SECONDS)); done; echo $SECONDS
20.823349000
20.837510000
20.851663000
21.780099000
21.794254000
21.808405000
0.9992358685

Se você quiser algo mais preciso, provavelmente vai querer um sistema operacional em tempo real ou um sistema operacional com recursos em tempo real e certamente não usando um shell.

    
por 12.10.2012 / 23:03
2

Se o sleep do seu shell não aceitar fração, use perl.

sleep_fraction() {
  /usr/bin/perl -e "select(undef, undef, undef, $1)"
}

sleep_fraction 0.01428

Se você precisar descobrir a fração, use echo "scale=5; 1/70" | bc

    
por 09.07.2013 / 16:25
0

Em Alpine Linux (Busybox) você pode fazer loop por microssegundos com usleep 10 (equivalente a 0.00001 de segundo)

GNU sleep suporta frações de segundo: sleep 0.00001

    
por 12.09.2016 / 01:21