这是一个关于 Bash 和 Linux 的小问题。我有一个包含特殊字符的文件,例如\n
、\r
等等。我正在尝试打印文件而不使用这些特殊字符。例如,如果文件内容是,hi \t hello
那么它将按原样打印(而不是hi hello
)。使用cat file
并不能完成工作。尝试查看 man of cat,我发现我可以使用 -A 选项。它打印:abc^Mabc
。但什么是^M
?我怎样才能让它按表格打印\<char>
?
答案1
如果您的文件中包含字面转义的字符,例如,如果“你好\n”有倒斜杠和“t”字母表示应该有一个制表符,而斜杠和“n”应该是换行符,那么您必须解释此表示法并将它们转换为真正的字符。
[正如 Paul Pedant 指出的那样,之前使用 echo 的解决方案不起作用]
经过一些测试,得出了在 Bash 中取消引用字面转义字符的简单方法:
x="$(cat filename)"
echo -en "$x"
为了验证其操作,我创建了此文件(cat > filename
然后键入各行,然后Ctrl-D
Enter
在最后一行中的五个空格后以及其他位置Ctrl-D
Enter
以关闭文件):
This line starts with 4 leading spaces, a tab here:\t. Has a real newline (RN) here:
Newline code here:\n, six spaces and a dot: . RN:
Return code here:\r, because the return code the first three words are hidden in the terminal.RN:
This line starts with two spaces, then has four and a tab here: \tRN:
This final line ends with five spaces without an ending newline:
在此文件上运行上述命令会在我的终端中显示一些奇怪的输出,因为返回代码会导致覆盖部分行并且不显示选项卡的实际宽度。
要检查输出中的实际字符,请将其传递给八进制转储器od
:
x="$(cat filename)"; echo -en "$x" | od -cx
其中 -c 显示不可打印的转义字符, -x 显示其十六进制代码(TAB=09、RETURN=0D、NEWLINE=0A、SPACE=20 等),结果是:
0000000 T h i s l i n e s t
2020 2020 6854 7369 6c20 6e69 2065 7473
0000020 a r t s w i t h 4 l e a d
7261 7374 7720 7469 2068 2034 656c 6461
0000040 i n g s p a c e s , a t a
6e69 2067 7073 6361 7365 202c 2061 6174
0000060 b h e r e : \t . H a s a
2062 6568 6572 093a 202e 6148 2073 2061
0000100 r e a l n e w l i n e ( R N
6572 6c61 6e20 7765 696c 656e 2820 4e52
0000120 ) h e r e : \n N e w l i n e
2029 6568 6572 0a3a 654e 6c77 6e69 2065
0000140 c o d e h e r e : \n , s i x
6f63 6564 6820 7265 3a65 2c0a 7320 7869
0000160 s p a c e s a n d a d o
7320 6170 6563 2073 6e61 2064 2061 6f64
0000200 t : . R N : \n R e
3a74 2020 2020 2020 202e 4e52 0a3a 6552
0000220 t u r n c o d e h e r e : \r
7574 6e72 6320 646f 2065 6568 6572 0d3a
0000240 , b e c a u s e t h e r e
202c 6562 6163 7375 2065 6874 2065 6572
0000260 t u r n c o d e t h e f i
7574 6e72 6320 646f 2065 6874 2065 6966
0000300 r s t t h r e e w o r d s
7372 2074 6874 6572 2065 6f77 6472 2073
0000320 a r e h i d d e n i n t h
7261 2065 6968 6464 6e65 6920 206e 6874
0000340 e t e r m i n a l . R N : \n
2065 6574 6d72 6e69 6c61 522e 3a4e 200a
0000360 T h i s l i n e s t a r t
5420 6968 2073 696c 656e 7320 6174 7472
0000400 s w i t h t w o s p a c e
2073 6977 6874 7420 6f77 7320 6170 6563
0000420 s , t h e n h a s f o u r
2c73 7420 6568 206e 6168 2073 6f66 7275
0000440 a n d a t a b h e r e :
6120 646e 6120 7420 6261 6820 7265 3a65
0000460 \t R N : \n T h i s f i
2020 2020 5209 3a4e 540a 6968 2073 6966
0000500 n a l l i n e e n d s w i
616e 206c 696c 656e 6520 646e 2073 6977
0000520 t h f i v e s p a c e s w
6874 6620 7669 2065 7073 6361 7365 7720
0000540 i t h o u t a n e n d i n g
7469 6f68 7475 6120 206e 6e65 6964 676e
0000560 n e w l i n e :
6e20 7765 696c 656e 203a 2020 2020
0000576
不会丢失任何内容,并且代码会转换为所需的单个字符。
问题:这个简单的操作只能转换小文件,因为 Bash 变量的最大长度为 32k 左右。为了扩展这一点,我们必须创建一个循环来读取文件大小合适的块并转换它们。对于此部分块读取,选择的实用程序是dd
。这是使用此方法的完整转换器脚本,块大小为 16k (16384):
#!/bin/bash
# unescape.sh: converts escaped chars (\t) to actual chars (TAB)
# 2020.11.19 Fjor
#-------------------------------
[ -z "$2" ] \
&& echo "Use: $0 inputfile outputfile to unescape chars (\t -> TAB)" \
&& exit
IFILE="$1"
OFILE="$2"
#-- exists input file?
[ ! -r "$IFILE" ] \
&& echo "$0: Can't read inputfile $IFILE" \
&& exit
#-- don't destroy existing output file
[ -f "$OFILE" ] \
&& read -p "$0: Output file $OFILE exists, overwrite(s/n)? " \
&& [ x"$REPLY" != xs ] \
&& exit \
|| rm -f $OFILE
let START=0
let STEP=16384
let SIZE="$(stat -c %s $IFILE)"
let NSTEPS=SIZE/STEP+1
echo -n "Converting..."
for ((n=0 ; n<$NSTEPS ; n++)) ; do
echo -ne "\r$START bytes converted..."
#-- ibs input block size, skip N blocks, copy count blocks
x="$(dd if=$IFILE ibs=$STEP skip=$n count=1 2>/dev/null)"
echo -ne "$x" >> $OFILE
let START+=STEP
done
echo -e "\rConversion complete ($SIZE bytes)."
#-- end --#
让我们另存为unescape.sh
,chmod u+x unescape.sh
并调用它:
./unescape filename.txt outresult.txt
如果文件有 NULL 字符,将出现警告,并且 NULL 将被忽略。