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. Вопрос о том, как удалить специальный символ 'M-BM-' с помощью sed, имеет интересный факт об этом персонаже.

Странно то, что сценарий свободен от этого персонажа. Так что я не знаю, откуда это взялось.

4 ответа4

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 покажет вам невидимых и странных персонажей.

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

Здесь вы можете видеть, что space , close-square-brace , space правильные - 0x20 , 0x5D , 0x20 .

Эти значения представляют собой коды ASCII, отображаемые в шестнадцатеричном формате. Любое значение за пределами диапазона 0x20 - 0x7E не является « печатаемым символом » в отношении ASCII и, скорее всего, не будет хорошо работать с интерфейсами командной строки.

Примечание. Я скопировал вашу первую « ломаную » строку для использования в примере с hexdump , поэтому что-то заменило пространство not-an-ASCII пространством ASCII между вашим исходным источником и вашим заданным вопросом.


Чтобы повторить это, выполните следующие действия:

  1. Введите hexdump -Cv <<"EOF" и нажмите Enter
  2. Вставьте текст, который вы хотели бы использовать
  3. Введите EOF в отдельной строке и нажмите Enter

Терминалы и интерфейсы командной строки плохо обрабатывают специальные символы - как вы обнаружили. Если вы не очень осторожны с форматированием документов, у вас также будут проблемы с 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 - 0xE2 , 0x80 , 0x9C или U+201C - которые терминал не будет обрабатывать, как вы могли ожидать.

Предложение Киви о 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}
9

echo "<your command>" | hd должен работать. Ищите возврат (0x08) или символы с кодами> = 80. echo "<your command>" | wc -b и проверка того, что число соответствует тому, что вы видите, также является хорошей идеей.

Копирование файлов из файлов, созданных чем-либо с "Office" в его названии, опасно, потому что такое программное обеспечение часто позволяет себе сменить символы: на французском языке ищите двойные кавычки, замененные "guillemets", на английском языке - простые кавычки, заменяемые открыть / закрыть эквиваленты. Самым сложным из всех, что я обнаружил, был неразрывный пробел шириной 0 в середине имени файла (3 дня простоя сервера ...).

2

Bash и другие оболочки, такие как zsh, могут открывать текущую командную строку в редакторе. Ярлык по умолчанию для bash - это C-x C-e (Ctrl X Ctrl E), и он открывается в первом доступном из $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$

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .