No programa a seguir, se eu definir a variável $foo
como o valor 1 na primeira if
instrução, ela funcionará no sentido de que seu valor será lembrado após a instrução if. No entanto, quando defino a mesma variável com o valor 2 dentro de um if
que está dentro de uma while
instrução, ela é esquecida após o while
loop. Está se comportando como se eu estivesse usando algum tipo de cópia da variável $foo
dentro do while
loop e estou modificando apenas essa cópia específica. Aqui está um programa de teste completo:
#!/bin/bash
set -e
set -u
foo=0
bar="hello"
if [[ "$bar" == "hello" ]]
then
foo=1
echo "Setting \$foo to 1: $foo"
fi
echo "Variable \$foo after if statement: $foo"
lines="first line\nsecond line\nthird line"
echo -e $lines | while read line
do
if [[ "$line" == "second line" ]]
then
foo=2
echo "Variable \$foo updated to $foo inside if inside while loop"
fi
echo "Value of \$foo in while loop body: $foo"
done
echo "Variable \$foo after while loop: $foo"
# Output:
# $ ./testbash.sh
# Setting $foo to 1: 1
# Variable $foo after if statement: 1
# Value of $foo in while loop body: 1
# Variable $foo updated to 2 inside if inside while loop
# Value of $foo in while loop body: 2
# Value of $foo in while loop body: 2
# Variable $foo after while loop: 1
# bash --version
# GNU bash, version 4.1.10(4)-release (i686-pc-cygwin)
bash
while-loop
scope
sh
Eric Lilja
fonte
fonte
SC2030: Modification of foo is local (to subshell caused by pipeline).
Respostas:
O
while
loop é executado em um subshell. Portanto, quaisquer alterações feitas na variável não estarão disponíveis quando o subshell terminar.Em vez disso, você pode usar uma string here para reescrever o loop while para estar no processo principal do shell; somente
echo -e $lines
será executado em um subshell:Você pode se livrar do feio
echo
na string here acima, expandindo as seqüências de barra invertida imediatamente ao atribuirlines
. A$'...'
forma de citação pode ser usada lá:fonte
<<< "$(echo -e "$lines")"
para simples<<< "$lines"
tail -f
vez de texto fixo?while read -r line; do echo "LINE: $line"; done < <(tail -f file)
(obviamente o loop não será encerrado, pois continua aguardando a entrada detail
).while
loop oufor
loop; em vez disso, o uso do subshell, ou seja, emcmd1 | cmd2
,cmd2
está em um subshell. Portanto, se umfor
loop for executado em uma subshell, o comportamento inesperado / problemático será exibido.ATUALIZADO # 2
A explicação está na resposta de Blue Moons.
Soluções alternativas:
Eliminar
echo
Adicione o eco dentro do documento aqui é
Executar
echo
em segundo plano:Redirecione para um identificador de arquivo explicitamente (lembre-se do espaço
< <
!):Ou apenas redirecione para
stdin
:E um para
chepner
(eliminandoecho
):A variável
$lines
pode ser convertida em uma matriz sem iniciar um novo sub-shell. Os caracteres\
en
tem de ser convertido para algum personagem (por exemplo, uma verdadeira caractere de nova linha) e usar a variável IFS (Campo Interno Separator) para dividir a string em elementos da matriz. Isso pode ser feito como:O resultado é
fonte
lines
único objetivo da variável parece alimentar owhile
loop.for line in $(echo -e $lines); do ... done
Você é o 742342º usuário a fazer perguntas frequentes sobre o bash. A resposta também descreve o caso geral de variáveis definidas em subshells criados por pipes:
fonte
Hmmm ... eu quase juro que isso funcionou para o shell Bourne original, mas não tenho acesso a uma cópia em execução agora para verificar.
Há, no entanto, uma solução muito trivial para o problema.
Altere a primeira linha do script de:
para
Et voila! Uma leitura no final de um pipeline funciona perfeitamente, supondo que você tenha o shell Korn instalado.
fonte
Eu uso o stderr para armazenar dentro de um loop e ler dele fora. Aqui var i é inicialmente definido e lido dentro do loop como 1.
Ou use o método heredoc para reduzir a quantidade de código em um subshell. Observe que o valor iterativo i pode ser lido fora do loop while.
fonte
Que tal um método muito simples
fonte
Essa é uma pergunta interessante e aborda um conceito muito básico no shell e subshell Bourne. Aqui, forneço uma solução diferente das soluções anteriores, fazendo algum tipo de filtragem. Vou dar um exemplo que pode ser útil na vida real. Este é um fragmento para verificar se os arquivos baixados estão em conformidade com uma soma de verificação conhecida. O arquivo de soma de verificação é semelhante ao seguinte (mostrando apenas 3 linhas):
O script de shell:
O pai e o subshell se comunicam através do comando echo. Você pode escolher um texto fácil de analisar para o shell pai. Esse método não quebra sua maneira de pensar normal, apenas que você precisa fazer algum pós-processamento. Você pode usar grep, sed, awk e muito mais para fazer isso.
fonte