假设我有这个文件:
hello
world
hello world
这个节目
#!/bin/bash
for i in $(cat $1); do
echo "tester: $i"
done
输出
tester: hello
tester: world
tester: hello
tester: world
我希望for
对每一行单独进行迭代,忽略空格,即最后两行应替换为
tester: hello world
使用引号for i in "$(cat $1)";
会导致i
一次分配整个文件。我应该改变什么?
答案1
与for
和IFS:
#!/bin/bash
IFS=$'\n' # make newlines the only separator
set -f # disable globbing
for i in $(cat < "$1"); do
echo "tester: $i"
done
但请注意,它将跳过空行,因为新队作为 IFS 空白字符,它的序列计为 1,并且忽略前导和尾随的序列。使用zsh
和ksh93
(不是bash
),您可以将其更改IFS=$'\n\n'
为换行符而不进行特殊处理,但请注意,所有尾随换行符(包括尾随空行)将始终被命令替换删除。
或者和read
(不再cat
):
#!/bin/bash
while IFS= read -r line; do
echo "tester: $line"
done < "$1"
在那里,空行被保留,但请注意,如果最后一行没有由换行符正确分隔,它将跳过最后一行。
答案2
(9年后:)
两个提供的答案都会在末尾没有换行符的文件上失败,这将有效地跳过最后一行,不会产生错误,会导致灾难(学到了惨痛的方法:)。
迄今为止我发现的最简洁的解决方案“Just Works”(在 bash 和 sh 中):
while IFS='' read -r LINE || [ -n "${LINE}" ]; do
echo "processing line: ${LINE}"
done < /path/to/input/file.txt
如需更深入的讨论,请参阅 StackOverflow 讨论:如果文件末尾没有换行符,如何使用“读取时”(Bash)读取文件中的最后一行?
请注意:如果没有换行符,此方法会在最后一行中添加一个额外的换行符。
答案3
如果你可以避免它,特别是在处理文本时就不要这样做。
大多数文本实用程序已经被设计为一次处理一行文本,并且至少对于 GNU 实现来说,可以高效、正确地处理文本并很好地处理错误情况。通过管道将它们并行运行还意味着您可以利用多个处理器来完成这项工作。
这里:
<input.txt sed 's/^/tester /' > output.txt
或者:
<input.txt awk '{print "tester", $0}' > output.txt
更多相关信息请访问:为什么使用 shell 循环处理文本被认为是不好的做法?
如果它与文本处理无关,并且您确实需要在文件的每行运行一些命令,还请注意 GNUxargs
您可以在哪里执行以下操作:
xargs -rd'\n' -I@ -a input.txt cp -- @ @.back
例如。
使用 bash shell,您可以使用内置命令将文件的每一行放入一个数组中readarray
:
readarray -t lines < input.txt &&
for line in "${lines[@]}"; do
do-some-non-text-processing-you-cannot-easily-do-with-xargs "$line" || break
done
POSIXly,您可以使用IFS= read -r line
从某些输入读取一行,但请注意,如果您使用 stdin 上的输入文件重定向整个while read
循环,则循环内的命令也将其 stdin 重定向到该文件,因此最好是使用您在循环内关闭的不同 fd:
while
IFS= read -r line <&3 ||
[ -n "$line" ] # to cover for an unterminated last line.
do
{
do-some-non-text-processing-you-cannot-easily-do-with-xargs "$line" ||
break # abort upon failure if relevant
} 3<&-
done 3< input.txt > output.txt
read -r line
从它读取的行中删除前导和尾随空白字符(前提是它们位于变量中)$IFS
,但只有yash
shell 遵循 POSIX 要求。对于大多数 shell,这仅限于空格和制表符。 ksh93 和最新版本bash
对所有在语言环境中被视为空格的单字节字符执行此操作。
因此,要读取一行并删除前导和尾随空白,您可以执行以下操作:IFS=$' \t' read -r line
。使用 ksh93、yash1 或最新版本的bash
.IFS=$' \t\r'
还会从 Microsoft 世界中删除文本文件中的尾随 CR 字符。
尽管yash
还不支持该$'...'
语法,但您需要IFS=$(printf ' \t\r')
那里。
答案4
要读取所有行,无论它们是否以新行结束:
cat "somefile" | { cat ; echo ; } | while read line; do echo $line; done
来源:我的开源项目https://sourceforge.net/projects/command-output-to-html-table/