Неожиданное поведение с эхом [[: digit:]]

10

Я хотел бы спросить:

Почему echo {1,2,3}расширен до 1 2 3, что является ожидаемым поведением, а echo [[:digit:]]возвращается, [[:digit:]]пока я ожидал, что он будет печатать все цифры от 0до 9?

Абдаллах Талаат
источник
Следите за: unix.stackexchange.com/q/347950/117549
Джефф Шаллер

Ответы:

34

Потому что это две разные вещи. Это {1,2,3}пример расширения скобки . {1,2,3}Конструкция расширяется оболочкой , до того echoдаже не видит. Вы можете увидеть, что произойдет, если вы используете set -x:

$ set -x
$ echo {1,2,3}
+ echo 1 2 3
1 2 3

Как видите, команда echo {1,2,3}расширена до:

echo 1 2 3

Тем не менее, [[:digit:]]это класс символов POSIX . Когда вы передаете его echo, оболочка также сначала обрабатывает его, но на этот раз он обрабатывается как глобус оболочки . это работает так же, как если бы вы запустили, echo *который напечатает все файлы в текущем каталоге. Но [[:digit:]]это оболочка Глобо , которая будет соответствовать любой цифре. Теперь в bash, если глобус оболочки ничего не соответствует, он будет расширен до самого себя:

$ echo /this*matches*no*files
+ echo '/this*matches*no*files'
/this*matches*no*files

Если шарик совпадает с чем-то, это будет напечатано:

$ echo /e*c
+ echo /etc
/etc

В обоих случаях echoпросто печатает все, что говорит оболочка, чтобы распечатать, но во втором случае, так как глобус совпадает с чем-то ( /etc), ему говорят, что это нужно напечатать.

Итак, поскольку у вас нет файлов или каталогов, чье имя состоит ровно из одной цифры (что [[:digit:]]соответствует), глобус расширяется до самого себя, и вы получаете:

$ echo [[:digit:]]
[[:digit:]]

Теперь попробуйте создать файл с именем 5и запустить ту же команду:

$ echo [[:digit:]]
5

И если есть несколько подходящих файлов:

$ touch 1 5       
$ echo [[:digit:]]
1 5

Это (вроде) задокументировано в man bashобъяснении nullglobопций, которые отключают это поведение:

nullglob
    If  set,  bash allows patterns which match no files (see
    Pathname Expansion above) to expand to  a  null  string,
    rather than themselves.

Если вы установите эту опцию:

$ rm 1 5
$ shopt -s nullglob
$ echo [[:digit:]]  ## prints nothing

$ 
Тердон
источник
4
Смотрите также, shopt -s failglobчтобы получить более полезное поведение, подобное поведению современных оболочек типа zshor fish.
Стефан Шазелас
Я согласен со Стефаном, используйте failglob. nullglobможет вызвать непредвиденные проблемы, например, при вставке URL, который имеет ?.
Кевин
1
Конечно, я только упомянул, nullglobчтобы продемонстрировать, что паттерн интерпретируется оболочкой как глобус.
Terdon
14

{1,2,3}это расширение скобки , оно распространяется на слова, перечисленные без учета их значения.

[...]является группой символов, используемой в расширении имени файла (или подстановочный знак, или глобус) аналогично звездочке *и вопросительному знаку ?. Он соответствует любому отдельному символу в списке или символам, которые являются членами именованных групп, например, [:digit:]если они есть в списке. Поведение большинства оболочек по умолчанию - оставить подстановочный знак как есть, если нет файлов, соответствующих ему.

(Обратите внимание, что вы действительно не можете превратить подстановочный знак / шаблон в набор строк, которому он будет соответствовать. Звездочка может соответствовать любой строке любой длины, поэтому расширение любого шаблона, содержащего ее, приведет к бесконечному списку строк.)

Так:

$ bash -c 'echo [[:digit:]]'           # bash leaves it as-is
[[:digit:]]
$ zsh -c 'echo [[:digit:]]'            # zsh by default complains if no match
zsh:1: no matches found: [[:digit:]]
$ touch 1 3 d i g t
$ bash -c 'echo [[:digit:]]'           # now there are two matches
1 3                                    # note that d, i, g and t do NOT match

Но до сих пор:

$ bash -c 'echo {1,2,3}'
1 2 3

Оба они расширяются оболочкой , не имеет значения, является ли команда, которую вы запускаете ls, или, echoили rm. Также обратите внимание, что если один из них указан в кавычках, они не будут расширены:

$ bash -c 'echo "[[:digit:]]"'         # even though matching files still exist
[[:digit:]]
$ bash -c 'echo "{1,2,3}"'
{1,2,3}
ilkkachu
источник
спасибо за ваш ответ, я новичок в Linux, поэтому позвольте мне спросить вас, как эхо связано с файлами 1 3, его функция - выводить свои аргументы в стандартный вывод, не ища файлы, насколько мне известно
AbdAllah Talaat
1
@AbdAllahTalaat, на самом деле это не имеет ничего общего с эхом. Оболочка (например, bash) будет «расширять» [[:digit:]] перед передачей ее echo, поэтому echoникогда не видит [[:digit:]], она видит только 1 3. Вы можете увидеть это в действии, запустив, set -xкоторый напечатает фактические команды, которые выполняются (запустите, set +xчтобы выключить его снова).
Terdon
@AbdAllahTalaat, echoне ищет файлы, это делает оболочка перед запуском echo.
ilkkachu
Тем более, что я думаю, что в DOS / Windows утилиты расширяют подстановочные знаки, а не оболочку. (Возможно я ошибаюсь)
ilkkachu
извините, ребята, я переместил правильный ответ на ответ Тедрона, потому что его комментарий содержал значение, что bash - это то, что не отражается в работе ... его ответ тоже содержал это значение ... вы все помогли мне ... я хотел бы, если бы я мог поставить правильный ответ на все ваши ответы и комментарии
Абдаллах Талаат
4

{1,2,3}(и, например, {1..3}это фигурные расширения . Они интерпретируются оболочкой перед выполнением команды.

[[:digit:]]это токен, соответствующий шаблону , но вы не используете его в расположении с файлами, соответствующими этому шаблону. Если вы используете сопоставление с шаблоном, у которого нет совпадений, оно расширяется до самого себя:

$ echo [[:digit:]]; touch 3; echo [[:digit:]]
[[:digit:]]
3
DopeGhoti
источник
Нет, как правильно указывают другие ответы, шаблон сопоставляется с именами файлов.
Тоби Спейт