Defina o comparador com variáveis dentro de uma variável e, em seguida, faça com que o shell expanda essas variáveis toda vez que for ecoado

1

Aqui está parte do meu script:

#!/bin/sh

n=1

echo "How many repetitions to run (0 = no limit)?"
read reps

if [ $reps = 0 ]; then
    while="true"
else
    while="[ $n -lt $((reps+1)) ]"
fi

echo "How much off-time in-between reps (in minutes)?"
read time

pwr_init

while $while; do
    echo "* Sending power pulse $n"
    pwr_normal
    t=$time
    echo "* Waiting for next power on"
    while [ $t -gt 0 ]; do
        echo "    $t min until next power on"; sleep 60
        t=$((t-1))
    done
    n=$((n+1))
done

Tudo está funcionando exatamente como eu preciso, exceto pelo primeiro loop while que estou realizando. Sempre que a variável $while for chamada, gostaria que o shell expandisse e verificasse as variáveis naquele momento que estão definidas na primeira instrução if .

Eu só posso obter shell para expandir as variáveis no momento da primeira instrução if , mas depois obtenho o comportamento como true porque o comparador é sempre verdadeiro e nunca muda quando $n é incrementado, porque essa variável $while já expandiu tudo durante a primeira instrução if .

Eu tentei métodos como ${!while} e várias combinações de aspas simples e duplas, mas sem sorte. Eu geralmente recebo erros como $n is a bad number ou bad substitution .

Neste momento, a única coisa em que consigo pensar é adicionar ainda mais instruções if em mais funções para verificar e fazer eco desta linha para a instrução while todas as vezes. Eu tenho que acreditar que há uma maneira melhor de fazer isso, no entanto, estou tendo dificuldade em descobrir isso (e também encontrar as palavras-chave certas para pesquisar on-line).

Durante a depuração, uso set -x e posso ver que a cadeia de comparação está sendo avaliada corretamente na maneira como o script é escrito aqui, mas, como mencionei, ela nunca se atualiza quando $n é incrementado.

Alguém tem um truque para esse tipo de substituição que torna a vida um pouco mais fácil? Muito apreciou qualquer insight que possa ser dado a este estranho enigma de expansão variável!

NOTA 1: pwr_init e pwr_normal são funções definidas em outro lugar no script completo.

NOTA 2: Anote o shebang, ele deve ser compatível com POSIX. Eu não posso usar nenhuma técnica específica de festas.

EDIT: Conseguir isso funcionar como eu precisava era menos complicado do que eu pensava, e estou colocando o que eu adicionei abaixo. No entanto, estou deixando a questão em aberto, pois seria ótimo saber se esse tipo de expansão de variável pode ou não ser feito.

Novo script com a função check agrupada em torno da declaração if :

#!/bin/sh

n=1

echo "How many repetitions to run (0 = no limit)?"
read reps

count=$((reps+1))

check() {
    if [ $reps = 0 ]; then
        while="true"
    else
        while="[ $n -lt $count ]"
    fi
}

echo "How much off-time in-between reps (in minutes)?"
read time

pwr_init

while $while; do
    echo "* Sending power pulse $n"
    pwr_normal
    t=$time
    echo "* Waiting for next power on"
    while [ $t -gt 0 ]; do
        echo "    $t min until next power on"; sleep 60
        t=$((t-1))
    done
    n=$((n+1))
    check
done
    
por engsysadmin 26.01.2018 / 22:04

2 respostas

0

Solução extra simples: para executar um "sem fim", basta definir o limite para um número insanamente grande. Um inteiro de 32 bits pode conter números até 4294967296. Permitindo um bit de sinal e arredondando para baixo, podemos supor que podemos contar até pelo menos 2000000000. Como você está dormindo por pelo menos um minuto entre as contagens, isso funciona por até, oh, cerca de 4000 anos.

Então faça:

if [ "$reps" = 0 ]; then
    reps=2000000000
fi

E hardwire a condição para ser

while [ "$n" -le "$reps" ]; do ...

(Se você estiver executando um sistema de 64 bits, você pode contar ainda mais. Não que isso importe. Se você estiver executando um sistema de 16 bits, provavelmente será parafusado de outras maneiras também .)

Solução simples: Basta fazer uma função para verificar se reps é zero ou n é menor que reps .

keep_going() {
    [ "$reps" = 0 ] && return 0
    [ "$n" -le "$reps" ] 
}

e, em seguida, faça a condição

while keep_going; do ...

Sim, você chamará uma função a cada vez. Não, não importa, pois você só faz isso uma vez por minuto no máximo. (E se você se preocupou em ser rápido, você não deveria usar o shell para fazer isso.)

A solução que você está procurando: Use eval . Com as aspas duplas na atribuição para while="[ $n -lt $count ]" , $n e $count serão expandidas imediatamente. O que você precisa é usar aspas simples na atribuição: while='[ $n -lt $count ]' e, em seguida, executar a sequência por meio de eval para que as variáveis sejam expandidas.

while='[ $n -lt $count ]'
while eval "$while" ; do ...
    
por 27.01.2018 / 00:12
1

Você pode tentar definir diferentes funções:

if [ $reps -eq 0 ]; then
    while_test() { true; }
else
    while_test() { test $1 -lt $((reps+1)); }
fi

então

while while_test $n; do

Você pode usar eval em um shell POSIX para trabalhar com a condição em uma variável:

$ cond='[ $n -lt $reps ]'
$ reps=5
$ n=4
$ eval "$cond" && echo 'n < reps' || echo 'n >= reps'
n < reps
$ n=5
$ eval "$cond" && echo 'n < reps' || echo 'n >= reps'
n >= reps

Certifique-se de que a condição esteja definida entre aspas simples, para que as variáveis não sejam expandidas prematuramente.

    
por 26.01.2018 / 22:47