Eu tenho o seguinte trecho de código onde pretendo:
Itere 5 vezes o loop;
obter uma linha de um arquivo (a cada iteração preciso que a próxima linha seja buscada);
use a linha como título de um arquivo;
coloque um carimbo de data/hora no início;
use a linha como argumento para ping; imprima algumas frases informando ao usuário sobre o andamento do código;
A iteração já funciona.
Se eu pular a parte sed, tudo funciona, mas executo o ping 5 vezes no mesmo alvo.
Cada linha do arquivo hosts.txt possui um único endereço IP no qual pretendo executar ping.
for ((i>=1;i<=5;i++))
do
sed -n "$i p" hosts.txt | read output
touch "$output.txt"
date >> "$output.txt"
printf "\nComeçando o teste de $output."
printf "\nTeste em andamento."
ping -c 10 -i 1 "$output" >> "$output.txt"
done
Acredito que o problema é que estou fazendo algo errado com a sintaxe do sed. Se eu tentar bash diretamente sed -n 1p hosts
, recebo a primeira linha. Mas como eu preciso que esse número seja $i, se eu colocar $i logo antes de p bash interpretar ip como uma variável em vez da variável i mais o argumento p depois dela.
Como posso corrigir esse comportamento e não bagunçar tudo de novo?
Responder1
Presumo que você esteja usando o bash, então isso deve funcionar melhor:
for ((i=1;i<=5;i++))
do
sed -n "$i p" hosts.txt | (
read output
touch "$output.txt"
date >> "$output.txt"
printf "\nComeçando o teste de $output."
printf "\nTeste em andamento."
ping -c 10 -i 1 "$output" >> "$output.txt"
)
done
O problema que você estava enfrentando é bash
que a maioria dos intérpretes de shell externos ksh
coloca todos os componentes do pipeline em um subshell para que a output
variável seja perdida imediatamente após ser definida.
Observe também que corrigi seu for
loop que, conforme escrito, tinha um comportamento indefinido porque a i
variável não foi inicializada.
Responder2
O problema é que, como você observou, ambos i
e ip
são nomes de variáveis válidos. Portanto, se você quiser usar o valor $i
imediatamente seguido pelo caractere p
, você pode usar chaves flexíveis para delimitar claramente onde o nome da variável começa e termina:
sed -n "${i}p" hosts.txt | read output
Responder3
Uma solução um pouco mais simples é
for ((i=1; i<=5; i++))
do
read output
date >> "$output.txt"
printf "\nComeçando o teste de %s." "$output"
printf "\nTeste em andamento."
ping -c 10 -i 1 "$output" >> "$output.txt"
done < hosts.txt
Observe o < hosts.txt
no final da última linha, após o done
. Isso abre hosts.txt
uma vez e apenas lê as primeiras cinco linhas dele. Conforme mencionado por don_crissti, fazer sed … hosts.txt | read output
no loop significa que você está lendo o hosts.txt
arquivo cinco vezes. Como read output
é paralelo ao restante do loop (não em um pipeline), o restante do código no loop terá acesso ao valor output
lido do arquivo. Além disso (como também apontou), você não precisa de touch
; irá criarcommand >> file
file
se ainda não existir.
Observe que se um dos seus hostname.txt
arquivos já existir, este código anexará as novas informações a ele. Se você quiser descartar dados antigos e começar do zero, faça date > "$output.txt"
em vez de date >> "$output.txt"
.
Tenha cuidado ao passar dados desconhecidos diretamente para arquivos printf
. No caso (improvável?) de um de seus “nomes de host” conter um %
, seu código irá distorcer esse nome. É melhor passar dados estrangeiros para %s
(para imprimir uma string).
Você não diz se deseja ler apenas as primeiras cinco linhas do hosts.txt
arquivo e depois parar, ou se deseja ler o arquivo inteiro, e acontece que você sabe que ele tem cinco linhas. Se você quiser ler o arquivo inteiro, basta fazer
while read output
do
date >> "$output.txt"
printf "\nComeçando o teste de %s." "$output"
printf "\nTeste em andamento."
ping -c 10 -i 1 "$output" >> "$output.txt"
done < hosts.txt
Quando você chegar ao final do arquivo, a read output
instrução falhará e o loop será encerrado.