(
set -e -- $(ls -l <file>) # <-- parsing 'ls' output generally is not a good move
pos=$(($5 - 1)) # file's size from `ls -l`, minus 1 to point to last byte
asciicode=$(od -j "$pos" -t u1 -A n "$9") # 'od' can seek with '-j' option
invcode=$(printf '%02x' $((asciicode ^ 0xff))) # 8-bit value read by 'od' xor-ed
# and made a 0-padded 2-digits hex value
temp="$(mktemp)" # temporary helper file
trap 'rm -f "$temp"' EXIT # dispose of it in due time
touch -r "$9" "$temp" # copy original file's timestamp
printf "\\x${invcode}" | dd of="$9" obs="$pos" seek=1 # put computed 8-bit value in place
touch -r "$temp" "$9" # restore file's timestamp
)
不是 sed,但这似乎在 Perl 中有效:
它按行读取,但这并不重要,因为它是 8 位干净的。
eof
在最后一行为真,并\z
在字符串的原始末尾匹配($
也将在可选的最终换行符之前匹配,因此严格来说不是最后一个字节)。替换只是对匹配字符串的异或操作。我自己在python中做了:
不是最优雅的解决方案,但我相信你们有一些巧妙的 sed 技巧并且可以做得更好。
这是完整版,您可以使用 ./invert.py (filename) 运行
类似
sed
并且awk
通常不适合查找文件或在字节级别处理文件内容的工具。它们是面向行的,需要正则表达式或行号作为地址,并且没有(内置)方法来检索文件的元数据,例如文件的大小或时间戳。也可以通过命令行工具实现您的预期结果,但据我所知,无论如何您都需要执行“粘合”操作。
这是我的尝试,只是为了好玩:(单行仅用于演示,根据您的意愿)
替换
<file>
为您的文件名。如您所见,并不是真正的单线。我已经在某种程度上符合 POSIX,但即使没有它也不会更短。
另请注意,它不处理文件的时间戳。要使用命令行工具做到这一点,它会变成这样:(这次为了可读性和解释而分解)
touch -r
在临时文件上来回使用,因为这应该是保持纳秒精度的最便携方式。还需要注意执行解析
ls
输出的危险操作的必要性,但我想不出另一个 POSIX 工具来检索文件的大小。当然,在这种情况下,它可以以更安全的方式完成(使脚本更加复杂),但这种必要性可能再次暗示我们正在将标准工具扩展到超出其预期任务的范围。