【发布时间】:2012-03-24 04:03:25
【问题描述】:
给定一个包含多行的文本文件,我想遍历 Bash 脚本中的每一行。我曾尝试使用cut,但cut 不接受\n(换行符)作为分隔符。
这是我正在使用的文件的示例:
one
two
three
four
有谁知道我如何在 Bash 中循环遍历此文本文件的每一行?
【问题讨论】:
给定一个包含多行的文本文件,我想遍历 Bash 脚本中的每一行。我曾尝试使用cut,但cut 不接受\n(换行符)作为分隔符。
这是我正在使用的文件的示例:
one
two
three
four
有谁知道我如何在 Bash 中循环遍历此文本文件的每一行?
【问题讨论】:
我发现自己遇到了同样的问题,这对我有用:
cat file.cut | cut -d$'\n' -f1
或者:
cut -d$'\n' -f1 file.cut
【讨论】:
使用cat 进行连接或显示。这里不需要。
file="/path/to/file"
while read line; do
echo "${line}"
done < "${file}"
【讨论】:
cut 方法慢。
cut 方法更快(O(n) 时间复杂度)并且更具可读性。 OP 询问如何遍历文件中的每一行。使用cut,必须知道文件中的行数并使用cut 编写那么多语句,或者编写一个while 循环给你O(n + n²) 时间复杂度。
简单使用:
echo -n `cut ...`
这会抑制末尾的 \n
【讨论】:
cat FILE|while read line; do # 'line' is the variable name
echo "$line" # do something here
done
或(见评论):
while read line; do # 'line' is the variable name
echo "$line" # do something here
done < FILE
【讨论】:
while ... done < file
因此,已经提供了一些非常好的(可能更好)的答案。但是看看原始问题的措辞,在想要使用 BASH for 循环时,令我惊讶的是,没有人提到改变 Field Separator IFS 的解决方案。这是一个纯 bash 解决方案,就像接受的读取行一样
old_IFS=$IFS
IFS='\n'
for field in $(<filename)
do your_thing;
done
IFS=$old_IFS
【讨论】:
如果您确定输出将始终以换行符分隔,请使用 head -n 1 代替 cut -f1(请注意,您在脚本中提到了 for 循环,而您的问题最终与脚本无关)。
许多其他答案,包括已接受的答案,都不必要地多行。无需在多行上执行此操作或更改系统上的默认分隔符。
另外,Ivan 用-d$'\n' 提供的解决方案在Mac OSX 或CentOS 7 上都不适用于我。由于他的回答已经有四年了,我认为$ 字符的逻辑一定有所改变对于这种情况。
【讨论】:
read 命令。您不应该使用cut 对文件中的每一行执行顺序迭代,因为cut 不是为此而设计的。
将每个 FILE 中选定的部分行打印到标准输出。 —
man cut
您应该在 IFS 设置为 \n 的函数范围内使用 while 循环和 read -r 命令并将标准输入重定向到您的文件,并在使用时使用 -E echo.
processFile() { # Function scope to prevent overwriting IFS globally
file="$1" # Any file that exists
local IFS="\n" # Allows spaces and tabs
while read -r line; do # Read exits with 1 when done; -r allows \
echo -E "$line" # -E allows printing of \ instead of gibberish
done < $file # Input redirection allows us to read file from stdin
}
processFile /path/to/file
为了遍历文件的每一行,我们可以使用 while 循环。这将使我们可以根据需要进行多次迭代。
while <condition>; do
<body>
done
我们可以使用read 命令将标准输入中的一行存储在一个变量中。在我们可以使用它从文件中读取一行之前,我们需要重定向标准输入以指向我们的文件。我们可以通过输入重定向来做到这一点。根据man pages for bash,重定向的语法是[fd]<file,其中fd 默认为标准输入(又名文件描述符0)。我们可以将它放在 while 循环之前或之后。
while <condition>; do
<body>
done < /path/to/file
# or the non-traditional way
</path/to/file while <condition>; do
<body>
done
现在我们的文件可以从标准输入读取,我们可以使用read。在我们的上下文中read 的语法是read [-r] var...,其中-r 保留\(反斜杠)字符,而不是将其用作转义序列字符,var 是存储变量的名称输入。您可以有多个变量来存储输入的片段,但我们只需要一个变量来读取整行。除此之外,要保留来自echo 的任何输出中的任何反斜杠,您可能需要使用-E 标志来禁用对反斜杠转义的解释。如果您有任何缩进(空格或制表符),您需要暂时将IFS(输入字段分隔符)变量更改为仅"\n";通常设置为" \t\n"。
main() {
local IFS="\n"
read -r line
echo -E "$line"
}
main
read 来结束我们的while循环?据我所知,实际上只有一种可靠的方法可以确定您何时完成使用read 读取文件:检查read 的退出值。如果read 的退出值为0,那么我们成功读取了一行,如果它是1 或更高,那么我们到达了EOF(文件结束)。考虑到这一点,我们可以在 while 循环的条件部分调用read。
processFile() {
# Could be any file you want hardcoded or dynamic
file="$1"
local IFS="\n"
while read -r line; do
# Process line here
echo -E "$line"
done < $file
}
processFile /path/to/file1
processFile /path/to/file2
【讨论】:
我的观点是“cut”使用 '\n' 作为其默认分隔符。 如果要使用cut,我有两种方法:
cut -d^M -f1 file_cut
我通过在 Ctrl+V 后单击 Enter 来制作 ^M。另一种方法是
cut -c 1- file_cut
这有帮助吗?
【讨论】:
cut 不使用 '\n' 作为其默认分隔符。