为什么 cat 会改变二进制文件的内容?
Why does cat change the contents of binary files?
显然,运行 cat
对二进制文件和回显内容似乎不起作用。这是我制作的简单脚本:
#!/bin/sh
CONTENTS=$(cat "")
mv "" ""
echo "$CONTENTS" > ""
出于某种原因,当我这样做时,这似乎搞砸了:
script first.pptx second.pptx
在 运行 之后,新的 first.pptx
文件可以正常打开,但是新的 second.pptx
文件可能是无效格式或其他格式。
为什么会发生这种情况,我该如何解决?
构造 $(cat "")
将在替换值之前去除文件中所有尾随的换行符(所以这就是 CONTENTS
最终的结果)。
echo "$CONTENTS"
将在第一个 NUL 字符处截断内容,并附加一个换行符。
因此,如果文件没有恰好以一个换行符结尾,则内容会略有变化。如果它有任何 NUL 字符,它将被截断。
这里证明 cat
不会 改变值的内容,并且你试图实现的脚本虽然没有实用价值,但实际上 可以写成bash:
#!/bin/bash
declare -a arr=( )
{
while IFS= read -r -d '' s; do
arr+=( "$s" )
done
arr+=( "$s" )
} < <(cat "") ## aside: this would be more efficiently just <"" without the cat
mv "" ""
{
printf '%s[=10=]' "${arr[@]:0:${#arr[@]}-1}"
printf '%s' "${arr[@]:${#arr[@]}-1}"
} >""
现在,这是如何工作的?
arr
是一个shell数组;每个元素都是一个 C 字符串。
while IFS= read -r -d '' s
将输入文件中的 NUL-delimited 字符串增量读取到 s
。只有 returns 为真,而这些字符串为 NUL-terminated;当一个字符串没有 NUL 终止符时,变量 s
仍然被填充,但是 read
命令 returns 为假。 [有关 while read
成语的更多信息,请参阅 BashFAQ #001。
- ...因此,数组的最后一个元素包含 在 最终 NUL 之后的内容。
printf '%s[=18=]' ...
在格式字符串(在 ...
区域)之后发出每个参数,后跟一个 NUL 定界符。 ${#arr[@]}
扩展为数组 arr
中的条目数,并且 ${arrayname[@]:SEEK:COUNT}
在跳过第一个 [=25= 之后扩展为数组 arrayname
中的 COUNT
项] 项目;因此,${arr[@]:0:${#arr[@]}-1}
扩展到数组 arr
中除最后一项以外的所有项,打印这些每个后跟一个 NUL。
- 最终的
printf
在最后的 NUL 之后发出尾随的内容——从最后一个数组条目开始。
如果 运行 这样,您将观察到输入文件的 md5sums 交换,即使它们是包含 NUL 值的二进制文件。因此,你的问题前提是错误的:cat
不改变二进制文件的内容。
尝试使用临时文件执行相同的操作。
cat < $file1 > $tempFile
cat < $file2 > $file1
cat < $tempFile > $file2
rm $tempFile
显然,运行 cat
对二进制文件和回显内容似乎不起作用。这是我制作的简单脚本:
#!/bin/sh
CONTENTS=$(cat "")
mv "" ""
echo "$CONTENTS" > ""
出于某种原因,当我这样做时,这似乎搞砸了:
script first.pptx second.pptx
在 运行 之后,新的 first.pptx
文件可以正常打开,但是新的 second.pptx
文件可能是无效格式或其他格式。
为什么会发生这种情况,我该如何解决?
构造 $(cat "")
将在替换值之前去除文件中所有尾随的换行符(所以这就是 CONTENTS
最终的结果)。
echo "$CONTENTS"
将在第一个 NUL 字符处截断内容,并附加一个换行符。
因此,如果文件没有恰好以一个换行符结尾,则内容会略有变化。如果它有任何 NUL 字符,它将被截断。
这里证明 cat
不会 改变值的内容,并且你试图实现的脚本虽然没有实用价值,但实际上 可以写成bash:
#!/bin/bash
declare -a arr=( )
{
while IFS= read -r -d '' s; do
arr+=( "$s" )
done
arr+=( "$s" )
} < <(cat "") ## aside: this would be more efficiently just <"" without the cat
mv "" ""
{
printf '%s[=10=]' "${arr[@]:0:${#arr[@]}-1}"
printf '%s' "${arr[@]:${#arr[@]}-1}"
} >""
现在,这是如何工作的?
arr
是一个shell数组;每个元素都是一个 C 字符串。while IFS= read -r -d '' s
将输入文件中的 NUL-delimited 字符串增量读取到s
。只有 returns 为真,而这些字符串为 NUL-terminated;当一个字符串没有 NUL 终止符时,变量s
仍然被填充,但是read
命令 returns 为假。 [有关while read
成语的更多信息,请参阅 BashFAQ #001。- ...因此,数组的最后一个元素包含 在 最终 NUL 之后的内容。
printf '%s[=18=]' ...
在格式字符串(在...
区域)之后发出每个参数,后跟一个 NUL 定界符。${#arr[@]}
扩展为数组arr
中的条目数,并且${arrayname[@]:SEEK:COUNT}
在跳过第一个 [=25= 之后扩展为数组arrayname
中的COUNT
项] 项目;因此,${arr[@]:0:${#arr[@]}-1}
扩展到数组arr
中除最后一项以外的所有项,打印这些每个后跟一个 NUL。- 最终的
printf
在最后的 NUL 之后发出尾随的内容——从最后一个数组条目开始。
如果 运行 这样,您将观察到输入文件的 md5sums 交换,即使它们是包含 NUL 值的二进制文件。因此,你的问题前提是错误的:cat
不改变二进制文件的内容。
尝试使用临时文件执行相同的操作。
cat < $file1 > $tempFile
cat < $file2 > $file1
cat < $tempFile > $file2
rm $tempFile