如何从Bash中读取整行

14

我有一个名为 file.txt 的文件,其内容如下:

i love this world

I hate stupid managers
I love linux

I have MS

当我执行以下操作时:

for line in `cat file.txt`; do
echo $line
done

它会输出如下结果

I
love
this
world
I
..
..

但我需要像下面这样的完整行作为输出,有什么想法吗?

i love this world

I hate stupid managers
I love linux

I have MS

2
你的问题看起来像是想要执行 cat file.txt - Zac Thompson
我赞同Zac的观点,你需要做更复杂的事情,而且可能已经有一个Unix工具可以完成它。 - tobyodavies
抱歉,也许我应该更清楚地表达,我需要逐行获取并解析这些行。因此,在我的情况下,cat 命令无法提供帮助。 - webminal.org
sed/awk 不够强大?亵渎!:D - tobyodavies
他们是:D..但我的需求有点不同。 - webminal.org
非常感谢大家对此提供的意见。 - webminal.org
5个回答

27
while read -r line; do echo "$line"; done < file.txt

6

正如评论中@Zac所指出的,您发布的问题的最简单解决方案是cat file.txt,因此我必须假设有更有趣的事情发生,因此我提供了两个解决问题的选项:

这里有两件事情可以做,要么将IFS(内部字段分隔符)设置为换行符并使用现有代码,要么在while循环中使用readline命令。

IFS="
"

或者

(while read line ; do
    //do something
 done) < file.txt

5
IFS=$'\n' 更易读。没有必要将 while 循环放入子shell中。 - Dennis Williamson
1
我发现 ( ... ) 符号更易于阅读,而在Bash脚本中性能很少是一个考虑因素。 - tobyodavies
1
@toby Bash有$'string'结构,它会特殊处理某些反斜杠转义字符,例如\n表示换行符(完整列表请参见bash手册)。 - SiegeX
1
@SiegeX,具有讽刺意味的是,我经常使用该语法来使用转义单引号,但不知道它让我使用换行符。 - tobyodavies
1
@toby 对于你所描述的情况,我通常发现使用双引号并转义 $ 更易读。例如 echo "joe's foo\$bar" 而不是 echo $'joe\'s foo$bar'。你会看到的另一个更常见的转义序列是 NUL 字符 $'\0' - SiegeX
显示剩余2条评论

2
我相信问题是如何一次性读入整行。下面的简单脚本可以完成这个任务。如果你不为 "read" 指定变量名,它将把整行内容存储在变量 $REPLY 中。

cat file.txt | while read; do echo $REPLY; done

戴夫...


还要注意的是,这种方式在文件读取后也将控制权传回CLI。如果您将命令的结果管道传输到while而不是文件,则这一点尤为重要,因为EOF具有文件,而CLI输出没有,会使命令等待更多内容。例如: find . -name 'readme.txt' | while read line; do echo $line; done(返回给用户) 与 while read line; do echo $line; done | find . -name 'readme.txt'(等待输入) - Neil Monroe
我有理由使用它,而且它完美地工作了,例如:%cat file | while read; do echo $REPLY | pipe_to_process_each_line >> file; done - javafueled

1

如果文件来自stdin,您可以使用read来完成它。如果您需要在已经将stdin用于其他目的的脚本中间执行此操作,则可以暂时重新分配stdin文件描述符。

#!/bin/bash
file=$1 

# save stdin to usually unused file descriptor 3
exec 3<&0

# connect the file to stdin
exec 0<"$file"

# read from stdin 
while read -r line
do
    echo "[$line]"
done

# when done, restore stdin
exec 0<&3

重定向到 while 循环有什么问题吗? - tobyodavies
不,只是添加了一种不同的方法来突出文件描述符操作,这是晦涩的但有其用途。 - thatbrentguy
例如,如果您想调用一些现有的库函数,该函数已经编写为使用stdin,但您希望它从文件中获取数据,则可以使用此技术来实现,而无需重新编写该函数。 - thatbrentguy
Bash库,一个有趣的概念...通常情况下,即使没有子shell,人们也更愿意调用子shell吧?而且bash函数本身就有自己的stdin。 - tobyodavies

-2

尝试

(while read l; do echo $l; done) < temp.txt

读取:从标准输入中读取一行并将其拆分为字段。
从标准输入中或从文件描述符FD(如果提供了-u选项)中读取单个行。该行被拆分为字段,就像单词拆分一样,第一个单词被分配给第一个名称,第二个单词被分配给第二个名称,依此类推,任何剩余的单词都被分配给最后一个名称。只有在$IFS中找到的字符才被识别为单词分隔符。

1
你不需要(也不应该)使用括号,这会创建一个多余的子shell。另外,我没有给你-1,但我猜想有人这样做是因为你的答案与我几分钟前给出的完全重复。 - SiegeX
我个人喜欢 ( ... ) 符号,我觉得它更容易阅读...在Bash中加速1ms的效果很少有用。 - tobyodavies
@Siege:自然而然,同时会得到几乎相同的明显答案。(我写作时你的回答还没有出现;不引用help read可以节省一分钟,但对谁有益呢?) - Ulrich Schwarz
@toby 使用while循环与重定向的主要目的是因为它不会创建子shell,而与速度无关,也不需要额外的fork。这与子shell中的更改不会反映到父shell有关。例如:i=0; (while read -r line; do ((i++)); done) < infile; echo $i 无论'infile'包含多少行,父shell始终会输出0作为i,这是由于子shell导致的。 - SiegeX
@SigeX 是的,然而在大多数情况下我认为这是一件好事:D 最终你需要知道它们之间的区别以及为什么选择其中之一。 - tobyodavies

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接