Как я могу проверить дословные символы командной строки bash?

1541
Gabriel Glenn

У меня было такое странное поведение этим утром в терминале 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, имеет интересный факт об этом персонаже.

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

15
Используйте редактор, который выделяет таких персонажей. Подсветка синтаксиса тоже очень помогает. Никогда не вставляйте прямо из Интернета в терминал, всегда проходите через вышеупомянутый редактор. choroba 5 лет назад 3
Возможно, вы захотите найти проблемную команду в своем списке истории, а затем направить вывод через программу шестнадцатеричного отображения. Чтобы вам не приходилось разбираться в длинном листинге, либо еще раз введите команду, чтобы поместить ее в конец списка истории, и запустите `history 2 | xxd` (поскольку сама команда` history` всегда является последней в list), или введите `history | grep" CommandWithProblem "| xxd`. Вы можете использовать любую другую программу шестнадцатеричного отображения вместо `xxd`, но по умолчанию это формат, который мне нравится. AFH 5 лет назад 2
@ Габриэль Гленн, пожалуйста, пометьте лучший / самый полезный / какой бы то ни было ответ как «_accepted_», используя галочку, а не комментируйте каждый, который помог ответ. [Информация] (https://meta.stackexchange.com/a/5235/364309) Attie 5 лет назад 0
@ Attie, да, буду, обычно я жду 24 часа, прежде чем принять лучшие ответы, как предложено в: https://meta.stackexchange.com/questions/5234/how-does-accepting-an-answer-work Gabriel Glenn 5 лет назад 1
Лично я бы использовал `set -x`. Это покажет вам команду и как она разделена. Это не обязательно говорит «плохой персонаж здесь», но показывает, что bash не разделяется на этого персонажа. Patrick 5 лет назад 1
@ Патрик Хорошее предложение! Пожалуйста, добавьте это как ответ. wjandrea 5 лет назад 0

4 ответа на вопрос

18
Kiwy

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

Это решение работает: `echo" [-f /etc/openvpn.ovpn] "| cat -A` возвращает `[-f /etc/openvpn/client.ovpnM-BM-] $`. Мы можем видеть символ * M-BM- * UT-8 _ неразрывный пробел_ Gabriel Glenn 5 лет назад 7
@GabrielGlenn рад, что это помогло тебе. Kiwy 5 лет назад 0
11
Attie

Один из вариантов - посмотреть на символы, которые вы пытаетесь использовать, с помощью шестнадцатеричной программы просмотра или редактора. 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 "$" attie  $ echo "`whoami`" attie  $ echo "$(whoami)" attie  $ cat <<EOF > $ > EOF attie 

Эти методы заменяют компоненты соответствующим текстом. Чтобы избежать этого, используйте один из следующих подходов. Обратите внимание на использование одинарных кавычек ( ') и « heredoc в кавычках » ( "EOF").

$ echo '$' $  $ echo '`whoami`' `whoami`  $ echo '$(whoami)' $(whoami)  $ cat <<"EOF" > $ > EOF $ 
Это решение работает: `echo" [-f /etc/openvpn.ovpn] "| hd `возвращает` [...] c2 a0 [...] `. Мы можем видеть символ * c2 a0 * UT-8 _ неразрывный пробел_ Gabriel Glenn 5 лет назад 0
9
xenoid

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

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

Стоит отметить, что «hd» - это сокращение от «hexdump», которое также упоминается в ответе Атти. Mikael Kjær 5 лет назад 2
@ MikaelKjær - В Ubuntu `hd` эквивалентно` hexdump -C`. AFH 5 лет назад 0
@xenoid: Я сказал «отредактировано в Windows», не отредактировано с помощью Office Writer, мы не сумасшедшие;). Если это было отредактировано, это было с Notepad ++. Gabriel Glenn 5 лет назад 1
Это решение работает: `echo" [-f /etc/openvpn.ovpn] "| hd `возвращает` [...] c2 a0 [...] `. Мы можем видеть символ * c2 a0 * UT-8 _ неразрывный пробел_ Gabriel Glenn 5 лет назад 1
2
muru

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$ 

Похожие вопросы