POSIX sh(1)
能够进行各种文件描述符操作(相当于open(2)
,close(2)
和dup(2)
,等。)以及read
- 来自STDIN的一行。
所以我得到的印象是我们可以用符合POSIX标准的shell脚本替换cat(1)
,但我还没有想出实际的实现。它是否真的可能,或者cat(1)
可能缺少sh(1)
的哪些功能? (暂时忘掉GNU扩展)
不要问我为什么要那样做。作为智力测验,也许?
答案 0 :(得分:6)
cat
可以将任何文件复制到stdout;该文件不需要是文本文件。例如,它可能包含NUL
个,NUL
无法在sh
字符串中表示。所以这肯定是cat
的一个特征,即使不是不可能,也很难实现。 [注1]
除此之外,您应该能够将read
和echo
包裹在while
循环中,尽管存在一些棘手的问题。 (例如,准确地再现不以换行符结尾的非空文件。)
但是,从技术上讲,echo
不再是sh
而是cat
的一部分;就像cat
一样,它是一个可能不存在的实用程序(在非Posix系统上)。实际上,没有echo
的环境与没有cat
的环境差不多;如果您有sh
,则可以合理地期望找到标准的命令行实用程序。
最小兼容Posix的read
接受的唯一选项是-r
。但是,如果我们使用read
的bash实现,我们可以逐个字符地复制文件,即使NUL
字符实际上永远不会出现在shell变量中:
while IFS= read -d '' -rn1 char; do
if [ -z "$char" ]; then printf '\0'; else printf '%s' "$char"; fi
done < "$1" > "$2"
示例:
$ 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
该调用中read
的完整选项集经过精心设计,可以解决bash实现中的各种特性:
IFS=
可以避免从结果中删除尾随的空白字符。-n1
导致一个字符被读取,直到分隔符。直觉上,-N1
会更自然,因为-N1
会忽略分隔符。但是,read
还会从输入中删除NUL
个字符。由于如果下一个字符是$char
,目的是在NUL
中存储零个字符,我们可以使用-n1
并将分隔符设置为NUL
来避免此问题,因为分隔符检查是在NUL
被剥离之前完成的。-d ''
将行分隔符设置为NUL
。见上文。-r
避免在输入流中解释 \ ;这是套装中唯一与Posix兼容的选项。不言而喻,以上只是理论上的兴趣,或者作为OP的智力测验。在实践中,shell脚本应该只是协调外部实用程序的工作,以及与cat
,dd
,head
和{{1}相关的Posix兼容实用程序的存在应该足以满足任何文件复制需求。
答案 1 :(得分:1)
(这与@ rici的答案基本相同,但有一个文件的具体示例,单独使用sh
无法显示。)
cat
无法复制 sh
。这是因为sh
没有提供任何方法将字节从一个文件移动到另一个不涉及shell参数的文件,而shell参数不能包含NULL字节。
这是一个简单的例子:
printf 'foo\0bar\n' > 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"