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

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


3
Используйте редактор, который выделяет таких персонажей. Подсветка синтаксиса тоже очень помогает. Никогда не вставляйте прямо из Интернета в терминал, всегда проходите через вышеупомянутый редактор.
Чороба

2
Возможно, вы захотите найти проблемную команду в своем списке истории, а затем направить вывод через программу шестнадцатеричного отображения. Чтобы вам не приходилось разбираться в длинном листинге, либо повторите команду, чтобы поместить ее в конец списка истории, и запустите history 2|xxd(потому что historyсама команда всегда является последней в списке), либо введите history|grep "CommandWithProblem"|xxd. Вы можете использовать любую другую программу шестнадцатеричного отображения вместо xxd, но по умолчанию это формат, который мне нравится.
AFH

@ Габриэль Гленн, пожалуйста, пометьте лучший / самый полезный / какой бы то ни было ответ как « принятый », используя галочку - вместо того, чтобы комментировать каждый ответ, который помог. информация
Attie

1
@ Attie, да, я просто подожду 24 часа, прежде чем принять лучшие ответы, как указано в: meta.stackexchange.com/questions/5234/…
Габриэль Гленн,

1
Лично я бы использовал set -x. Это покажет вам команду и как она разделена. Это не обязательно говорит «плохой персонаж здесь», но показывает, что bash не разделяется на этого персонажа.
Патрик

Ответы:


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приведенном выше примере, поэтому что-то заменило не-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}

Это решение работает: 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которое также упоминается в ответе Атти.
Микаэль Кьер

@ 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 и другие оболочки, такие как zsh, могут открывать текущую командную строку в редакторе. По умолчанию ярлыка для Баша 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$
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.