如何查看bash命令字符串的逐字符?


15

我今天早上在一个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和“]”之间的空格并用空格替换它使命令再次起作用。

我的假设是正确的:一个未知的空白角色已经进入命令,但问题是:

  1. 如何在终端中显示这些字符以便我可以调试命令?更重要的是:
  2. 我怎样才能防止这种情况再次发生?

顺便说一下,我运行Ubuntu 18.04 /法语,我粘贴命令的脚本是在USB驱动器中,也可能在Windows上编辑过。


谢谢你的非常好的答案。坏字符是c2 a0非破坏空间UTF-8字符。这个问题如何使用sed删除特殊“M-BM-的性格有关于性格有趣的事实。

奇怪的是剧本没有这个角色。所以我不知道它来自哪里。


3
使用突出显示此类字符的编辑器。语法高亮也有很大帮助。切勿直接从网络粘贴到终端,请务必浏览上述编辑器。
choroba

2
Yo可能想在历史列表中找到问题命令,然后通过十六进制显示程序管道输出。因此,您不需要浏览长列表,重新运行命令将其放在历史列表的底部并运行history 2|xxd(因为history命令本身始终是列表中的最后一个),或者键入history|grep "CommandWithProblem"|xxd。您可以使用任何其他十六进制显示程序而不是xxd,但默认为我喜欢的格式。
AFH

@Gabriel Glenn,请使用勾选标记最好/最有帮助/任何答案为“已接受 ” - 而不是评论答案所帮助的每一个。info
Attie

1
@Attie,是的我会,我通常会在接受最佳答案之前等待24小时,如下所示:meta.stackexchange.com/questions/5234 / ...
Gabriel Glenn

1
就个人而言,我会用set -x。这将显示命令及其拆分方式。它不一定会说“这里的坏人物”,但它会告诉你bash并没有分裂那个角色。
帕特里克

Answers:


11

一种选择是查看您尝试使用十六进制查看器或编辑器的字符。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

你可以在这里看到spaceclose-square-bracespace是正确的- ,0x20,。0x5D0x20

这些值是ASCII码,以十六进制显示。超出范围的任何值0x20- 0x7E就ASCII而言,不是可打印字符,并且很可能不能与命令行界面一起使用。

注意:我复制了您在上面示例中使用的第一个“ 损坏 ”行hexdump,因此在原始源和渲染问题之间使用ASCII空格替换了 ASCII空格。


要重复此操作,请执行以下步骤:

  1. 输入hexdump -Cv <<"EOF"并按Enter
  2. 粘贴您要使用的文本
  3. 键入EOF自己的一行,然后按Enter

终端和命令行界面不能很好地处理特殊字符 - 正如您所发现的那样。如果您对格式化文档不是很小心,那么使用“ 智能引号 ”,em-dashes,Microsoft Word(和其他人)也会遇到问题,列表继续...

发现差异:(顶部是“ 智能引号 ”,底部是“ 直引号 ”)

智能报价与直线报价的例子

$ 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系列- ,0xE20x800x9CU+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

这些方法正在用相关文本替换组件。为避免这种情况,请使用以下方法之一。注意使用单引号(')和引用的heredoc"EOF")。

$ echo '${USER}'
${USER}

$ echo '`whoami`'
`whoami`

$ echo '$(whoami)'
$(whoami)

$ cat <<"EOF"
> ${USER}
> EOF
${USER}

此解决方案有效:echo "[ -f /etc/openvpn.ovpn ]" | hd 返回[...] c2 a0 [...]。我们可以看到c2 a0 UT-8角色不间断的空间
加布里埃尔格伦

18

你可以使用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会告诉你隐形和奇怪的角色。


7
此解决方案有效:echo "[ -f /etc/openvpn.ovpn ]" | cat -A返回[ -f /etc/openvpn/client.ovpnM-BM- ]$。我们可以看到M-BM- UT-8角色不间断的空间
加布里埃尔格伦

@GabrielGlenn很高兴这对你有所帮助。
Kiwy

9

echo "<your command>" | hd应该管用。查找退格(0x08)或代码> = 80的字符。echo "<your command>" | wc -b并检查计数是否与您看到的相符也是一个好主意。

从其名称中带有“Office”的任何内容生成的文件中复制内容是危险的,因为此类软件通常可以自由替换字符:在法语中,请注意用“guillemets”替换的双引号,用英语替换为其引用的简单引号打开/关闭等价物。我发现的最难的是文件名中间的0宽度不间断空间(服务器停机3天......)。


2
值得一提的hd是,hexdump在Attie的回答中也提到了它的简称。
MikaelKjær18年

@MikaelKjær - 在Ubuntu上,hd相当于hexdump -C
AFH

1
@xenoid:我说'在Windows上编辑',不是用Office Writer编辑的,我们并不疯狂;)。如果它是编辑的,那就是Notepad ++。
加布里埃尔格伦

1
此解决方案有效:echo "[ -f /etc/openvpn.ovpn ]" | hd 返回[...] c2 a0 [...]。我们可以看到c2 a0 UT-8角色不间断的空间
加布里埃尔格伦

2

Bash和其他shell如zsh可以在编辑器中打开当前命令行。对于bash默认的快捷键是C-x C-eCtrlX 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$
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.