在 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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-18 08:28:34  来源:igfitidea点击:

Looping through lines in a file in bash, without using stdin

linuxbashshellloops

提问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。以下内容还包括对该假设的测试:

##代码##