两个重要的陷阱
到目前为止,其他答案都忽略了这一点:
从命令展开中删除尾随换行符
NUL字符删除
从命令展开中删除尾随换行符
这是一个问题:
value="$(cat config.txt)"
类型解决方案,但不是基于读的解决方案。
命令展开会移除尾随的换行:
S="$(printf "a\n")"
printf "$S" | od -tx1
输出:
0000000 61
0000001
这打破了从文件中读取的简单方法:
FILE="$(mktemp)"
printf "a\n\n" > "$FILE"
S="$(<"$FILE")"
printf "$S" | od -tx1
rm "$FILE"
POSIX解决方案:在命令展开部分附加一个额外的字符,然后将其删除:
S="$(cat $FILE; printf a)"
S="${S%a}"
printf "$S" | od -tx1
输出:
0000000 61 0a 0a
0000003
几乎POSIX的解决方案:ASCII编码。见下文。
NUL字符删除
在变量中存储NUL字符没有合理的Bash方法。
这会影响扩展和读取解决方案,我不知道有什么好的解决方案。
例子:
printf "a\0b" | od -tx1
S="$(printf "a\0b")"
printf "$S" | od -tx1
输出:
0000000 61 00 62
0000003
0000000 61 62
0000002
哈,我们的NUL不见了!
解决方法:
ASCII编码。见下文。
使用bash扩展$""字面值:
S =美元“b \ 0”
打印“$S”| od -tx1
只适用于字面量,所以对从文件中读取没有用处。
解决陷阱的方法
在变量中存储文件的uuencode base64编码版本,并在每次使用前解码:
FILE="$(mktemp)"
printf "a\0\n" > "$FILE"
S="$(uuencode -m "$FILE" /dev/stdout)"
uudecode -o /dev/stdout <(printf "$S") | od -tx1
rm "$FILE"
输出:
0000000 61 00 0a
0000003
uuencode和udecode是POSIX 7,但在Ubuntu 12.04默认不是(sharutils包)…我没有看到POSIX 7替代bash进程<()替换扩展除了写入另一个文件…
当然,这又慢又不方便,所以我想真正的答案是:如果输入文件可能包含NUL字符,就不要使用Bash。