在 bash 中循环遍历文件中的行,而不使用 stdin
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/19727576/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Looping through lines in a file in bash, without using stdin
提问by Yannick
I am foxed by the following situation.
我对以下情况感到困惑。
I have a file list.txt that I want to run through line by line, in a loop, in bash. A typical line in list.txt has spaces in. The problem is that the loop contains a "read" command. I want to write this loop in bash rather than something like perl. I can't do it :-(
我有一个文件 list.txt,我想在 bash 中逐行、循环地运行它。list.txt 中的典型行包含空格。问题在于循环包含“读取”命令。我想用 bash 而不是像 perl 这样的东西来写这个循环。我做不到:-(
Here's how I would usually write a loop to read from a file line by line:
这是我通常会如何编写一个循环来逐行读取文件:
while read p; do
echo $p
echo "Hit enter for the next one."
read x
done < list.txt
This doesn't work though, because of course "read x" will be reading from list.txt rather than the keyboard.
但这不起作用,因为当然“读取 x”将从 list.txt 而不是键盘读取。
And this doesn't work either:
这也不起作用:
for i in `cat list.txt`; do
echo $i
echo "Hit enter for the next one."
read x
done
because the lines in list.txt have spaces in.
因为 list.txt 中的行中有空格。
I have two proposed solutions, both of which stink:
我有两个提议的解决方案,它们都很糟糕:
1) I could edit list.txt, and globally replace all spaces with "THERE_SHOULD_BE_A_SPACE_HERE" . I could then use something like sed, within my loop, to replace THERE_SHOULD_BE_A_SPACE_HERE with a space and I'd be all set. I don't like this for the stupid reason that it will fail if any of the lines in list.txt contain the phrase THERE_SHOULD_BE_A_SPACE_HERE (so malicious users can mess me up).
1) 我可以编辑 list.txt,并用 "THERE_SHOULD_BE_A_SPACE_HERE" 全局替换所有空格。然后我可以在我的循环中使用类似 sed 的东西,用一个空格替换 THERE_SHOULD_BE_A_SPACE_HERE,我就准备好了。我不喜欢这个愚蠢的原因,如果 list.txt 中的任何行包含短语 THERE_SHOULD_BE_A_SPACE_HERE(因此恶意用户可以把我搞砸),它就会失败。
2) I could use the while loop with stdin and then in each loop I could actually launch e.g. a new terminal, which would be unaffected by the goings-on involving stdin in the original shell. I tried this and I did get it to work, but it was ugly: I want to wrap all this up in a shell script and I don't want that shell script to be randomly opening new windows. What would be nice, and what might somehow be the answer to this question, would be if I could figure out how to somehow invoke a new shell in the command and feed commands to it without feeding stdin to it, but I can't get it to work. For example this doesn't work and I don't really know why:
2)我可以将while循环与stdin一起使用,然后在每个循环中我实际上可以启动例如一个新终端,它不受原始shell中stdin的影响。我试过这个,我确实让它工作了,但它很难看:我想把所有这些都包装在一个 shell 脚本中,我不希望这个 shell 脚本随机打开新窗口。如果我能想出如何以某种方式在命令中调用一个新的 shell 并向它提供命令而不向它提供标准输入,那么什么会很好,并且可能以某种方式成为这个问题的答案,但我无法得到它工作。例如,这不起作用,我真的不知道为什么:
while read p; do
bash -c "echo $p; echo ""Press enter for the next one.""; read x;";
done < list.txt
This attempt seems to fail because "read x", despite being in a different shell somehow, is still seemingly reading from list.txt. But I feel like I might be close with this one -- who knows.
这种尝试似乎失败了,因为“读取 x”,尽管不知何故处于不同的外壳中,但似乎仍在从 list.txt 读取。但我觉得我可能和这个人很亲近——谁知道呢。
Help!
帮助!
回答by Jo So
You must open as a different file descriptor
您必须作为不同的文件描述符打开
while read p <&3; do
echo "$p"
echo 'Hit enter for the next one'
read x
done 3< list.txt
Update: Just ignore the lengthy discussion in the comments below. It has nothing to do with the question or this answer.
更新:请忽略下面评论中的冗长讨论。它与问题或此答案无关。
回答by Anssi
I would probably count lines in a file and iterate each of those using eg. sed. It is also possible to read infinitely from stdinby changing while condition to: while true;
and exit reading with ctrl+c.
我可能会计算文件中的行数并使用例如迭代每个行。sed。也可以通过将 while 条件更改为:并使用ctrl+c退出读取来从stdin无限while true;
读取。
line=0 lines=$(sed -n '$=' in.file)
while [ $line -lt $lines ]
do
let line++
sed -n "${line}p" in.file
echo "Hit enter for the next ${line} of ${lines}."
read -s x
done
AWK is also great tool for this. Simple way to iterate through input would be like:
AWK 也是一个很好的工具。遍历输入的简单方法如下:
awk '{ print (
tty -s <& 2|| exit 1
while read -r line; do
echo "$line"
echo 'Hit enter'
read x <& 2
done < file
)
; printf "%s", "Hit enter for the next"; getline < "-" }' file
回答by Henk Langeveld
As an alternative, you can read from stderr, which by default is connected to the tty as well. The following then also includes a test for that assumption:
作为替代方案,您可以从 stderr 读取,默认情况下它也连接到 tty。以下内容还包括对该假设的测试:
##代码##