是否有可能用 POSIX sh(1) 复制 cat(1)


  • http://pubs.opengroup.org/onlinepubs/9699919799/utilities/sh.html

POSIX sh(1)能够进行各种文件描述符操作(相当于open(2)close(2)dup(2))以及从STDIN read一行。

所以我的印象是我们可以用符合 POSIX 的 shell 脚本替换cat(1),但我还没有提出实际的实现。真的有可能吗,或者,sh(1)中可能缺少cat(1)的什么功能?(暂时忘记GNU扩展)

不要问我为什么要这样做。也许是一个智力测验?

cat可以将任何文件复制到stdout;该文件不需要是文本文件。例如,它可能包括NUL s,并且NUL不能用sh字符串表示。因此,这肯定是cat的一个特征,即使不是不可能,也很难实现。[注1]

除此之外,您应该能够将readecho包装在while循环中,尽管存在一些棘手的问题。(例如,准确复制不以换行符结尾的非空文件。

但是,从技术上讲,echo并不比cat更像sh的一部分;就像cat一样,它是一个可能不存在的实用程序(在非Posix系统上)。实际上,没有echo的环境与没有cat的环境一样的可能性;如果您有 sh ,则有合理的期望找到标准命令行实用程序。


笔记

  1. 最小 Posix 兼容read接受的唯一选项是 -r .但是,如果我们有 read 的 bash 实现,我们可以逐个字符复制文件,即使NUL字符实际上永远不会出现在 shell 变量中:

    while IFS= read -d '' -rn1 char; do
      if [ -z "$char" ]; then printf ''; else printf '%s' "$char"; fi
    done < "$1" > "$2"
    

    例:

    $ printf 'foobarnnbye' |
    > while IFS= read -d '' -rn1 char; do
    >   if [ -z "$char" ]; then printf ''; else printf '%s' "$char"; fi
    > done |
    > hd
    00000000  66 6f 6f 00 62 61 72 0a  0a 62 79 65              |foo.bar..bye|
    0000000c
    

    在该调用中read的完整选项集经过精心设计,以解决 bash 实现中的各种特性:

    • IFS=避免从结果中删除尾随空格字符。
    • -n1会导致读取一个字符,直到分隔符。直观地说,-N1会更自然,因为-N1忽略了分隔符。但是,read也会从输入中删除NUL字符。由于目的是在$char中存储零个字符,如果下一个字符是NUL,我们可以通过使用-n1并将分隔符设置为 NUL 来避免这个问题,这是有效的,因为分隔符检查是在剥离NUL之前完成的。
    • -d ''将行分隔符设置为 NUL 。见上文。
    • -r避免在
    • 输入流中解释 \;这是集合中唯一兼容 Posix 的选项。
       

    不用说,以上只是理论上的兴趣,或者根据OP作为智力测验。在实践中,shell 脚本应该只协调外部实用程序的工作,并且存在与 Posix 兼容的实用程序(如 catddheadtail)应该足以满足任何文件复制需求。

(这基本上与@rici的答案相同,但有一个无法单独显示sh文件的具体示例。

仅使用 sh无法复制cat。这是因为sh不提供任何将字节从一个文件移动到另一个不涉及 shell 参数的方法,并且 shell 参数不能包含 NULL 字节。

下面是一个简单的示例:

printf 'foobarn' > tmp.txt  # Create a file containing a null byte
IFS= read -r line < tmp.txt    # Real that line into a variable.
echo "$line"                   # Only outputs "foo"

最新更新