使用bash编写Unix过滤器

3

如果一个Unix/Linux命令从标准输入接受其输入数据并在标准输出上生成其输出(结果),则称之为过滤器。

最简单的过滤器是cat。它只是将stdin复制到stdout而没有任何修改。

如何在bash中实现cat?(不考虑命令行参数的情况)

我想到了以下方法:

#! /bin/bash

while IFS="" read -r line
do
  echo -E "$line"
done

在大多数情况下,这似乎是可行的,即使文本文件包含一些二进制字节,只要它们不是空字节。但是,如果最后一行不以换行符结尾,则会缺少输出结果中的该行。

如何解决这个问题?

我几乎可以确定这个问题以前已经被回答过了,但是我的搜索技巧似乎不够好。

显然,我不想在bash中重新实现cat:无论如何都行不通,因为存在空字节问题。但是,我想扩展基本循环以对文本文件的某些行进行自定义处理。然而,我们已经看到过没有最终换行符的文本文件,因此我希望能够处理这种情况。


1
你最好使用管道连接现有的过滤器(如 sedawk),而不是尝试在 bash 中实现类似的东西。 - larsks
可能是在bash中从txt文件读取行的重复问题。 - bfontaine
1
它对于包含空字节的二进制文件不起作用;一个shell变量无法存储空字节。 - chepner
@chepner:你是对的,我注意到了。对于我的使用情况来说,这并不是个问题,因为我知道不会有空字节。我会编辑问题以澄清这一点。 - Uwe Geuder
@larsks 是的,我非常确定可以使用 awk 解决问题。然而,我需要进行的自定义处理相当复杂,使用 bash 脚本更容易维护。 - Uwe Geuder
你可以使用 while IFS= read -r line || [[ -n $line ]]; do printf '%s\n' "$line" done 来处理没有换行符的最后一行。 - anubhava
1个回答

5

假设您不需要处理任意二进制文件(因为 shell 无法在变量中存储空字节),您可以通过检查循环结束后line是否为空来处理没有以换行符终止的文件。

while IFS= read -r line; do
    printf '%s\n' "$line"
done
if [ -n "$line" ]; then
    printf '%s' "$line"
fi

在循环中,我们输出了read去掉的换行符。在最后一个if语句中,如果while循环读取的最后一行已经以一个换行符结束,则不输出一个新的换行符,因为此时$line将为空。请保留html标记。

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