- 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]
除此之外,您应该能够将read
和echo
包装在while
循环中,尽管存在一些棘手的问题。(例如,准确复制不以换行符结尾的非空文件。
但是,从技术上讲,echo
并不比cat
更像sh
的一部分;就像cat
一样,它是一个可能不存在的实用程序(在非Posix系统上)。实际上,没有echo
的环境与没有cat
的环境一样的可能性;如果您有 sh
,则有合理的期望找到标准命令行实用程序。
笔记
-
最小 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 'foo barnnbye' | > 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
。见上文。 - 输入流中解释 \;这是集合中唯一兼容 Posix 的选项。
-r
避免在不用说,以上只是理论上的兴趣,或者根据OP作为智力测验。在实践中,shell 脚本应该只协调外部实用程序的工作,并且存在与 Posix 兼容的实用程序(如
cat
、dd
、head
和tail
)应该足以满足任何文件复制需求。 -
(这基本上与@rici的答案相同,但有一个无法单独显示sh
文件的具体示例。
仅使用 sh
无法复制cat
。这是因为sh
不提供任何将字节从一个文件移动到另一个不涉及 shell 参数的方法,并且 shell 参数不能包含 NULL 字节。
下面是一个简单的示例:
printf 'foo barn' > 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"