Porque há diferença entre esses dois códigos:?

1. Porque há diferença entre esses dois códigos:?

Roberto Furtado
Kingflare

(usa Debian)

Enviado em 09/01/2018 - 13:10h

Galera, eu to começando a estudar shell script e pintou uma dúvida, com o seguinte script:
 i=0
cat /etc/passwd | while read line
do
i=$((i+1))
done
echo $i


Então a ideia era que o programa retornasse a quantidade de linhas desse arquivo, mas ele não retorna. Porém, se eu fizer assim:
i=0
while read line
do
i=$((i+1))
done < /etc/passwd
echo $i


Ele retorna o valor correto, porque isso ocorre? Enquanto o primeiro retorna 0.




  


2. Re: Porque há diferença entre esses dois códigos:?

Patrick Costa da Silva
patrickpcs

(usa Nenhuma)

Enviado em 09/01/2018 - 13:23h

wc -l /caminho/do/arquivo 



3. Re: Porque há diferença entre esses dois códigos:?

Roberto Furtado
Kingflare

(usa Debian)

Enviado em 09/01/2018 - 14:47h

Amigo o arquivo tem 29 linhas, o que eu gostaria de saber é porque em um código contabiliza e no outro não. Mas já descobri, no caso do primeiro código é criado um "sub shell" e a variável só é incrementada naquele escopo, aí sair do subprocesso a variável volta a ser a de fora do escopo, ou seja, com o valor inicial.


4. Porque há diferença entre esses dois códigos:?

morvan bliasby
morvan

(usa Fedora)

Enviado em 09/01/2018 - 15:42h

No primeiro código, falha por o arquivo a ser iterado estar fora do laço. É justamente ele a ser percorrido, sob pena de falha.
Morvan, Usuário GNU-Linux #433640. Seja Legal; seja Livre. Use GNU-Linux.


5. Re: Porque há diferença entre esses dois códigos:?

Paulo
paulo1205

(usa Ubuntu)

Enviado em 15/01/2018 - 03:59h

Você precisa entender como o shell trata a execução de pipelines e de comandos compostos, especialmente quando há redirecionamento de arquivos.

Essas coisas estão documentadas na manpage de cada shell (e os diferentes sabores de shell têm comportamentos diferentes: aqui, eu vou falar do bash), de modo que, se você ler a sessão relevante, pode tirar suas próprias conclusões.

Sobre pipelines, é importante você notar que cada componente da pipeline executa num processo separado do shell original, mesmo que seja um comando interno do shell, tal como read ou while. Assim sendo, qualquer alteração de variável que você faça em qualquer parte da pipeline vai acontecer dentro de um processo distinto do original e, portanto, não se refletirá nesse processo. Isso explica a falha na primeira versão do seu programa.

Comandos compostos têm múltiplas formas, incluindo laços de repetição como os de while ou for e os de execução condicional, como if ou case. O cuidado que você tem de ter com comandos compostos é que eventuais redirecionamentos afetam todos os comandos individuais que compõem o comando composto, e não apenas o comando aparentemente mais externo. Um erro relativamente comum é a pessoa colocar um comando interativo, que deveria pedir confirmação de alguma operação ao usuário, dentro de um laço de repetição que teve a entrada redirecionada, mais ou menos parecido com o seguinte.

while read arquivo; do
rm -i "$arquivo"
done < /tmp/lista_de_arquivos.txt


O erro no código acima é que o comando rm vai entender que a confirmação de se o arquivo deve ser apagada ou não deve vir da próxima linha do arquivo usado no redirecionamento.

Uma solução, nesse caso, seria redirecionar apenas a leitura do nome do arquivo a ser apagado. Para tanto, poder-se-ia fazer o seguinte.

exec 3</tmp/lista_de_arquivos.txt  # Associa arquivo ao descritor nº 3.
while read -u 3 arquivo; do # Manda o read (e não todo o comando composto!) ler especificamente do descritor nº 3.
rm -i "$arquivo"
done
exec 3<&- # Fecha (desativa) o descritor nº 3.



Assim sendo, acho que o melhor modo (i.e. mais seguro, para você poder colocar outras coisas dentro do bloco a ser repetido sem afetar nem ser afetado por redirecionamentos) de reescrever seu programa seria o seguinte.

i=0
exec 3</etc/passwd
while read -u 3 line
do
((++i))
done
exec 3<&-
echo $i