是否可以用POSIX sh(1)复制cat(1)?

nod*_*kai 1 linux bash shell posix sh

POSIX sh(1)是能够有各种文件描述符操作(相当于open(2),close(2)dup(2),),以及read-ing从STDIN单行.

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

不要问我为什么要那样做.作为智力测验,也许吧?

ric*_*ici 6

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

除此之外,你应该能够紧裹readecho一个内while循环,虽然有一些棘手的问题.(例如,准确地再现不以换行符结尾的非空文件.)

但是,从技术上讲,echo是不多的部分shcat是; 就像cat,它是一个可能不存在的实用程序(在非Posix系统上).在实践中,没有echo环境的环境与没有环境的环境差不多cat; 如果你有sh,你有合理的期望找到标准的命令行实用程序.


笔记

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

    while IFS= read -d '' -rn1 char; do
      if [ -z "$char" ]; then printf '\0'; else printf '%s' "$char"; fi
    done < "$1" > "$2"
    
    Run Code Online (Sandbox Code Playgroud)

    例:

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

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

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

    不言而喻,上述仅仅是理论上的兴趣,或者作为OP的智力测验.在实践中,一个shell脚本应该做的不超过协调的外部公用设施的工作更多,并且符合POSIX标准的实用程序,如存在cat,dd,headtail应足以满足任何文件复印需求.

  • @nodakai:这是一个巨大的差异,这就是为什么我要这样做。你觉得我需要更明确吗? (2认同)