如何遍历文件的行?

Tob*_*ler 71 shell control-flow

说我有这个文件:

hello
world
hello world
Run Code Online (Sandbox Code Playgroud)

这个程序

#!/bin/bash

for i in $(cat $1); do
    echo "tester: $i"
done
Run Code Online (Sandbox Code Playgroud)

产出

tester: hello
tester: world
tester: hello
tester: world
Run Code Online (Sandbox Code Playgroud)

我希望for对每一行进行迭代,分别忽略空格,即最后两行应替换为

tester: hello world
Run Code Online (Sandbox Code Playgroud)

使用引号for i in "$(cat $1)";会立即i分配整个文件。我应该改变什么?

wag*_*wag 86

随着forIFS

#!/bin/bash

IFS=$'\n'       # make newlines the only separator
set -f          # disable globbing
for i in $(cat < "$1"); do
  echo "tester: $i"
done
Run Code Online (Sandbox Code Playgroud)

但是请注意,它将跳过空行作为换行符作为 IFS 空白字符,它的序列计为 1,并且忽略前导和尾随的。使用zshand ksh93(not bash),您可以将其更改IFS=$'\n\n'为不特别处理换行符,但请注意,命令替换将始终删除所有尾随换行符(包括尾随空行)。

read(不再cat):

#!/bin/bash

while IFS= read -r line; do
  echo "tester: $line"
done < "$1"
Run Code Online (Sandbox Code Playgroud)

在那里,空行被保留,但请注意,如果最后一行没有被换行符正确分隔,它将跳过最后一行。

  • 谢谢,我不知道一个人可以“&lt;”进入一个完整的循环。虽然现在我看到了它是完全有道理的 (7认同)
  • @GrzegorzWierzowiecki `IFS=` 关闭去除前导和尾随空格。参见[在`while IFS= read..`中,为什么IFS没有效果?](http://unix.stackexchange.com/questions/18922/in-while-ifs-read-why-does-ifs-have -没有效果) (5认同)
  • `while IFS= 读取 -r 行 || [“$行”]; do` 将处理未由换行符正确分隔的尾随行(但它将被添加回来)。 (3认同)
  • 我在第二个例子中看到了“IFS \ read -r line”。真的需要`IFS=`吗?恕我直言,足以说:`while read -r line; 做 echo "tester: $line"; 完成&lt;“$1”` (2认同)

Dmi*_*yas 14

(9 年后 :)
两个提供的答案都会在没有换行符的文件上失败,这将有效地跳过最后一行,不会产生错误,会导致灾难(很难学到:)。

迄今为止我发现的最好的简洁解决方案是“Just Works”(在 bash 和 sh 中):

while IFS='' read -r LINE || [ -n "${LINE}" ]; do
    echo "processing line: ${LINE}"
done < /path/to/input/file.txt
Run Code Online (Sandbox Code Playgroud)

有关更深入的讨论,请参阅此 StackOverflow 讨论:如果文件末尾没有换行符,如何使用“while read”(Bash)读取文件中的最后一行?

注意:如果没有换行符,这种方法会在最后一行添加一个额外的换行符。


Sté*_*las 5

如果可以避免它,特别是在处理文本时就不要这样做。

\n

大多数文本实用程序已经被设计为一次处理一行文本,并且至少对于 GNU 实现来说,可以高效、正确地处理文本并很好地处理错误情况。通过管道将它们并行运行还意味着您可以利用多个处理器来完成这项工作。

\n

这里:

\n
<input.txt sed \'s/^/tester /\' > output.txt\n
Run Code Online (Sandbox Code Playgroud)\n

或者:

\n
<input.txt awk \'{print "tester", $0}\' > output.txt\n
Run Code Online (Sandbox Code Playgroud)\n

有关更多信息,请访问:为什么使用 shell 循环处理文本被认为是不好的做法?

\n

如果它与文本处理无关,并且您确实需要在文件的每行运行一些命令,还请注意 GNUxargs您可以执行以下操作:

\n
xargs -rd\'\\n\' -I@ -a input.txt cp -- @ @.back\n
Run Code Online (Sandbox Code Playgroud)\n

例如。

\n

使用 bash shell,您可以使用内置命令将文件的每一行放入一个数组中readarray

\n
readarray -t lines < input.txt &&\n  for line in "${lines[@]}"; do\n    do-some-non-text-processing-you-cannot-easily-do-with-xargs "$line" || break\n  done\n
Run Code Online (Sandbox Code Playgroud)\n

POSIXly,您可以使用IFS= read -r line从某些输入读取一行,但请注意,如果您使用while readstdin 上的输入文件重定向整个循环,则循环内的命令也将其 stdin 重定向到该文件,因此最好是使用您在循环内关闭的不同 fd:

\n
while\n  IFS= read -r line <&3 ||\n    [ -n "$line" ] # to cover for an unterminated last line.\ndo\n  {\n    do-some-non-text-processing-you-cannot-easily-do-with-xargs "$line" ||\n      break # abort upon failure if relevant\n  } 3<&-\ndone 3< input.txt > output.txt\n
Run Code Online (Sandbox Code Playgroud)\n

read -r line从它读取的行中删除前导和尾随空白字符(前提是它们位于$IFS变量中),但只有yashshell 遵循 POSIX 要求。对于大多数 shell,这仅限于空格和制表符。ksh93 和最新版本bash对所有在语言环境中被视为空格的单字节字符执行此操作。

\n

因此,要读取一行并删除前导和尾随空白,您可以执行以下操作:IFS=$\' \\t\' read -r line。使用 ksh93、yash\xc2\xb9 或最新版本的bash. IFS=$\' \\t\\r\'还会从 Microsoft 世界中删除文本文件中的尾随 CR 字符。

\n
\n

\xc2\xb9 虽然yash还不支持该$\'...\'语法,但您需要IFS=$(printf \' \\t\\r\')那里。

\n


归档时间:

查看次数:

268168 次

最近记录:

5 年,6 月 前