У меня есть следующий фрагмент кода, в котором я собираюсь:
Выполнить цикл 5 раз;
получить строку из файла (на каждой итерации мне нужно извлечь следующую строку);
использовать строку в качестве заголовка файла;
поместить временную метку в его начало;
использовать строку в качестве аргумента для ping; вывести несколько предложений, информирующих пользователя о ходе выполнения кода;
Итерация уже работает.
Если я пропускаю часть sed, все работает, но я запускаю ping 5 раз против одной и той же цели.
Каждая строка файла hosts.txt имеет один IP-адрес, который я собираюсь пинговать.
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
Я думаю, проблема в том, что я делаю что-то неправильно с синтаксисом sed. Если я попробую напрямую в bash sed -n 1p hosts
, я получу первую строку. Но так как мне нужно, чтобы это число было $i, если я поставлю $i прямо перед p, bash интерпретирует ip как переменную, а не как переменную i плюс аргумент p после нее.
Как мне исправить это поведение и не испортить все снова?
решение1
Я предполагаю, что вы используете bash, поэтому это должно работать лучше:
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
Проблема, с которой вы столкнулись, заключается в том bash
, что большинство внешних интерпретаторов оболочки ksh
помещают все компоненты конвейера в подоболочку, поэтому output
переменная сразу теряется после установки.
Обратите внимание, что я исправил ваш for
цикл, который в том виде, в котором он был написан, имел неопределенное поведение, поскольку i
переменная не была инициализирована.
решение2
Проблема в том, что, как вы заметили, и i
и ip
являются допустимыми именами переменных. Так что если вы хотите использовать значение, $i
за которым сразу следует символ p
, вы можете использовать изогнутые фигурные скобки, чтобы четко разграничить начало и конец имени переменной:
sed -n "${i}p" hosts.txt | read output
решение3
Немного более простое решение:
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
Обратите внимание < hosts.txt
на в конце последней строки, после done
. Это открывается hosts.txt
один раз и просто считывает первые пять строк из него. Как упомянул don_crissti, выполнение sed … hosts.txt | read output
в цикле означает, что вы читаете hosts.txt
файл пять раз. Поскольку выполняется read output
параллельно с остальной частью цикла (не в конвейере), остальная часть кода в цикле будет иметь доступ к значению output
read из файла. Также (как также указал don), вам не нужно touch
; создастcommand >> file
file
если его еще не существует.
Обратите внимание, что если один из ваших hostname.txt
файлов уже существует, этот код добавит к нему новую информацию. Если вы хотите удалить старые данные и начать с нуля, сделайте date > "$output.txt"
вместо date >> "$output.txt"
.
Будьте осторожны, передавая неизвестные данные напрямую в printf
. В (маловероятном?) случае, если одно из ваших «имен хостов» содержит %
в себе , ваш код исказит это имя. Лучше передавать чужие данные в %s
(чтобы вывести строку).
Вы не говорите, хотите ли вы прочитать только первые пять строк файла hosts.txt
и затем остановиться, или вы хотите прочитать весь файл, и вы случайно знаете, что он состоит из пяти строк. Если вы хотите прочитать весь файл, просто сделайте
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
Когда вы дойдете до конца файла, оператор read output
завершится ошибкой, и цикл прервется.