我有以下我想要的代码片段:
迭代5次循环;
从文件中获取一行(每次迭代我都需要获取下一行);
使用该行作为文件的标题;
在其开头添加一个时间戳;
使用该行作为 ping 的参数;打印一些句子,通知用户代码的进度;
迭代已经起作用了。
如果我跳过 sed 部分,一切都会正常,但我对同一目标运行 ping 5 次。
文件hosts.txt 的每一行都有一个我打算对其进行ping 操作的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
,就像大多数外部 shell 解释器一样ksh
,将所有管道组件放入子 shell 中,因此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
从文件中读取的值。另外(正如 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
语句将失败,循环将终止。