zsh/bash中含制表符的文件读写字符串自不等问题求解
嘿,我太懂你这种摸不着头脑的感觉了!之前在bash里跟制表符打交道时也踩过一模一样的坑,咱们一步步拆解问题,搞定它~
其实核心是bash在文件读写时的默认行为搞的鬼:要么是写入时悄悄加了换行符,要么是读取时对制表符做了不必要的转义/处理,导致两个看起来完全一样的字符串,在字节层面其实有差异,bash自然判定不相等。
别光靠[[ $foo == $bar ]]这种表面判断,得深入到字节层面看差异:
用hexdump看十六进制细节
制表符的十六进制是09,换行符是0a,把两个字符串转成十六进制对比,一眼就能看出问题:# 查看原字符串的十六进制 echo -n "$original" | hexdump -C # 查看从文件读取的字符串的十六进制 echo -n "$from_file" | hexdump -C要是输出里的字节序列完全一致,那才是真的相等。
用od命令看字符编码
另一个实用工具是od,能同时显示字符和对应的ASCII码:od -t x1 -c <<< "$original" od -t x1 -c <<< "$from_file"对比两行输出,有没有多出来的换行,或者制表符被替换的情况一目了然。
用diff对比临时文件
把两个字符串分别写入临时文件,用diff工具对比:echo -n "$original" > temp1.txt echo -n "$from_file" > temp2.txt diff temp1.txt temp2.txt要是diff没输出,说明两个字符串完全一致;有输出的话,会明确告诉你哪里不一样。
知道了问题根源,咱们调整读写方式,确保字符串完全一致:
写入用printf代替echo
echo默认会在末尾加换行符,部分版本还会转义制表符(比如echo "a\tb"可能输出a\tb而不是a b),用printf更靠谱:# 假设原字符串是$original,带制表符 printf "%s" "$original" > output.txt%s会按原样输出字符串,不会加额外换行,也不会动你的制表符。读取时用read -r或直接cat赋值
用read的时候一定要加-r,不然bash会解析反斜杠,破坏制表符;或者直接用cat赋值,完全读取文件内容:# 方法1:保留原始字符的读取方式 read -r from_file < output.txt # 方法2:直接读取整个文件内容 from_file=$(cat output.txt)注意:如果文件末尾有换行符,
$(cat)会自动去掉它,所以写入时别加多余换行,保持和原字符串一致就行。
咱们跑一遍完整流程验证下:
# 定义带制表符的原字符串(可以直接按Ctrl+V再按Tab输入真实制表符,或者用\转义) original="hello$(printf '\t')world" # 写入文件,不带额外换行 printf "%s" "$original" > test.txt # 读取文件内容 from_file=$(cat test.txt) # 对比十六进制 echo "原字符串字节细节:" echo -n "$original" | hexdump -C echo "读取的字符串字节细节:" echo -n "$from_file" | hexdump -C # 最后判断相等性 if [[ "$original" == "$from_file" ]]; then echo "搞定!两个字符串完全相等啦~" else echo "还有问题?再看上面的字节对比找差异哦!" fi
内容的提问来源于stack exchange,提问作者SimpleSam5

