【问题标题】:bash: append newline when redirecting filebash:重定向文件时附加换行符
【发布时间】:2011-06-11 06:04:51
【问题描述】:

这是我逐行读取文件的方法:

while read ROW
do
...
done < file

我不使用其他语法

cat file | while read ROW
do
...
done

因为管道创建了一个子外壳并使我丢失了环境变量。 如果文件不以换行符结尾,则会出现问题:未读取最后一行。后一种语法很容易解决这个问题,只需回显一个换行符:

(cat file; echo) | while read ROW
do
...
done

如何在不打开子shell 也不创建临时文件(列表很大)的情况下在前一种语法中做同样的事情?

【问题讨论】:

  • 我指定:“丢失环境变量”是指在循环内部所做的更改在外部不可见(例如计数器)。当然,在循环中我仍然可以访问继承的变量。
  • 也许我没有正确模拟结果,但是您使用的是什么版本的 bash?我正在使用 bash4 并且在读取最后一行时遇到问题,无论是否有最后一个新行。
  • bash 4.1.5。也许你应该检查换行符的种类(\r、\n 或两者,mac linux 或 win-style)

标签: bash file newline append


【解决方案1】:

一种适用于所有 shell 的方法如下:

#!/bin/sh

willexit=0
while [ $willexit == 0 ] ; do
read ROW || willexit=1
...
done < file

只要read 遇到EOF,直接while read 就会退出,因此不会处理最后一行。通过检查while外的返回值,我们可以处理最后一行。不过,应该在读取之后添加一个额外的 $ROW 空性测试,因为否则最后一行以换行符结尾的文件会生成一个空行的虚假执行,所以让它

#!/bin/sh

willexit=0
while [ $willexit == 0 ] ; do
read ROW || willexit=1
if [ -n "$ROW"] ; then
  ...
fi
done < file

【讨论】:

  • 我会将 if 更改为 if [ -n "$ROW" ] || [ "$willexit" == 0 ] 以便仍然可以处理空行(还要注意变量周围的引号)。
  • 关于引号的好点,但您的if 添加意味着“在第一个空行退出”而不是“跳过空行”。如果你想处理空行,只需去掉 if(代码的第一个版本)
【解决方案2】:
#!/bin/bash

while read ROW
 ...
done < <(cat file ; echo)

【讨论】:

  • 这确实创建了一个子shell,尽管它是cat 一个而不是while 一个(因此对于可能不希望while 的OP 来说可能没问题位于子外壳中。(此外,它需要&lt;() 管道,这并非在所有外壳中都可用。)
【解决方案3】:

执行此操作的 POSIX 方法是通过命名管道。

#!/bin/sh

[ -p mypipe ] || mkfifo mypipe

(cat num7; echo) > mypipe & 

while read line; do 
  echo "-->$line<--"
  export CNT=$((cnt+1))
done < mypipe

rm mypipe

echo "CNT is '$cnt'"

输入

$ cat infile
1
2
3
4
5$

输出

$ (cat infile;echo) > mypipe & while read line; do echo "-->$line<--"; export CNT=$((cnt+1)); done < mypipe; echo "CNT is '$cnt'"
[1] 22260
-->1<--
-->2<--
-->3<--
-->4<--
-->5<--
CNT is '5'
[1]+  Done                    ( cat num7; echo ) > mypipe

【讨论】:

  • 这是否仍然会导致在 while 循环中设置的 env vars 在它之后看不到,我认为这是 OP 不想要子 shell 的原因?
  • @Oblomov 发布后,我意识到我的答案没有意义,因为来自 parent->subshel​​l 的 env vars 应该始终有效。我意识到他在谈论相反的方式。已更新答案以使其与 POSIX 解决方案一起使用。
【解决方案4】:

来自answer to a similar question

while IFS= read -r LINE || [ -n "${LINE}" ]; do
   ...
done <file

IFS= 部分防止read 去除前导和尾随空格(请参阅this answer)。

如果您需要根据文件是否有尾随换行符做出不同的反应(例如,警告用户),您必须对 while 条件进行一些更改。

【讨论】:

    猜你喜欢
    • 2023-03-22
    • 1970-01-01
    • 1970-01-01
    • 2010-09-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-05-28
    相关资源
    最近更新 更多