今天早上我在 bash 终端上发现了这种奇怪的现象:
user@home:/home/user$ [ -f /etc/openvpn/client.conf ] && echo true
bash: [: missing «]»
user@home:/home/user$ [ -f /etc/openvpn/client.conf ] && echo true
true
- 第一个命令是粘贴来自用 gedit 编辑的脚本。
- 第二个是直接输入在终端。
经过一番挖掘,我发现删除第 30 个字符(client.conf 和“]”之间的空格)并将其替换为空格使得命令再次起作用。
我的假设是正确的:命令中出现了未知的空白字符,但问题是:
- 我怎样才能在终端中显示这些字符以便调试命令?更重要的是:
- 我怎样才能防止这种情况再次发生?
顺便说一句,我运行的是 Ubuntu 18.04/法语,我粘贴命令的脚本位于 USB 驱动器中,可能也在 Windows 上进行了编辑。
非常感谢您的回答。坏角色是c2 a0不间断空格 UTF-8 字符。问题如何使用 sed 删除特殊的“M-BM-”字符有关于该角色的有趣事实。
奇怪的是剧本里没有这个角色。所以我不知道它是从哪里来的。
答案1
一种选择是使用十六进制查看器或编辑器查看您尝试使用的字符。hexdump
如果您仅限于终端,这是一个不错的选择。
$ hexdump -Cv <<"EOF"
> [ -f /etc/openvpn/client.conf ] && echo true
> EOF
00000000 5b 20 2d 66 20 2f 65 74 63 2f 6f 70 65 6e 76 70 |[ -f /etc/openvp|
00000010 6e 2f 63 6c 69 65 6e 74 2e 63 6f 6e 66 20 5d 20 |n/client.conf ] |
00000020 26 26 20 65 63 68 6f 20 74 72 75 65 0a |&& echo true.|
0000002d
您可以在这里看到,,space
是close-square-brace
正确space
的 - 0x20
,,0x5D
。0x20
这些值是 ASCII 代码,显示为十六进制. 任何超出范围的值0x20
-0x7E
都不是“可打印字符“就 ASCII 而言,很可能无法与命令行界面很好地兼容。
笔记:我抄袭了你的第一个“破碎的“行用于hexdump
上述示例,因此某些内容已取代了非 ASCII 空格在原始来源和呈现的问题之间留有一个 ASCII 空格。
要重复此操作,请执行以下步骤:
- 键入
hexdump -Cv <<"EOF"
并按下Enter - 粘贴您想要使用的文本
- 输入
EOF
一行,然后按Enter
终端和命令行界面不能很好地处理特殊字符 - 正如您所发现的。如果您在格式化文档时不够小心,您也会遇到使用“智能引号“,破折号,列表还在继续......
找出不同之处:(最上面是“智能引号“,底部是”直引号“)
$ hexdump -Cv <<"EOF"
> “quoted string”
> EOF
00000000 e2 80 9c 71 75 6f 74 65 64 20 73 74 72 69 6e 67 |...quoted string|
00000010 e2 80 9d 0a |....|
00000014
此处,开引号不是简单的 ASCII 引号 ( "
),而是 Unicode /UTF-8系列- 0xE2
,,,或0x80
0x9C
U+201C
- 终端将不会像您预期的那样处理。
Kiwy 的建议cat -A
也可以起到这样的作用:
$ cat -A <<"EOF"
> “quoted string”
> EOF
M-bM-^@M-^\quoted stringM-bM-^@M-^]$
笔记:使用 时echo "..." | hd
,bash 可能会替换您要检查的字符串的某些部分。在尝试检查脚本的组件时,这一点尤其令人担忧。
例如尝试:
$ echo "${USER}"
attie
$ echo "`whoami`"
attie
$ echo "$(whoami)"
attie
$ cat <<EOF
> ${USER}
> EOF
attie
这些方法用相关文本替换组件。为避免这种情况,请使用以下方法之一。请注意使用单引号 ( '
) 和“引述此文“("EOF"
)。
$ echo '${USER}'
${USER}
$ echo '`whoami`'
`whoami`
$ echo '$(whoami)'
$(whoami)
$ cat <<"EOF"
> ${USER}
> EOF
${USER}
答案2
您可以使用手册中的选项 cat
:-A
-A, --show-all
equivalent to -vET
-E, --show-ends
display $ at end of each line
-T, --show-tabs
display TAB characters as ^I
-v, --show-nonprinting
use ^ and M- notation, except for LFD and TAB
因此cat -A yourscrip.sh
将会向您展示隐形和奇怪的人物。
答案3
echo "<your command>" | hd
应该可以工作。查找退格键 (0x08) 或代码 >=80 的字符。echo "<your command>" | wc -b
检查计数是否与您看到的内容相匹配也是一个好主意。
复制任何名称中带有“Office”的软件生成的文件都是很危险的,因为此类软件通常会擅自替换字符:在法语中,双引号会被替换为“guillemets”,在英语中,普通引号会被替换为相应的开/关字符。我发现最难的是在文件名中间出现一个宽度为 0 的不间断空格(服务器停机 3 天......)。
答案4
Bash 和其他 shell(如 zsh)可以在编辑器中打开当前命令行。bash 的默认快捷键是C-x C-e
( CtrlX CtrlE),它会在 $VISUAL
、$EDITOR
和 emacs 的第一个可用窗口中打开。实际上,这对于调试和修改复杂命令非常有用。根据您的看法,zsh 在这方面比 bash 更友好:当编辑器退出时,bash 会立即运行命令,而 zsh 会等待您按下Enter(为您提供更多机会编辑命令)。
在编辑器中打开命令后,您可以配置编辑器以不同方式显示非 ASCII 字符。
例如,使用 Vim,使用以下设置:
set encoding=latin1
set isprint=
set display+=uhex
或者,采用其他答案的方法:
bash-4.4$ f() { cat -A "$@"; false; } # exit false to prevent bash from running the command
bash-4.4$ VISUAL=f
bash-4.4$ [ -f /etc/openvpn/client.conf ] && echo true # C-x C-e here
[ -f /etc/openvpn/client.confM-BM- ] && echo true$