Почему соответствует строчным буквам в bash?

Во всех оболочках, о которых я знаю, rm [AZ]* удаляет все файлы, начинающиеся с буквы верхнего регистра, но с bash это удаляет все файлы, начинающиеся с буквы.

Поскольку эта проблема существует в Linux и Solaris с bash-3 и bash-4, это не может быть ошибкой, вызванной ошибкой шаблона шаблона в libc или определением локали с ошибкой.

Является ли это странным и рискованным поведением или это просто ошибка, которая существует незафиксированной с многих лет?

7 Solutions collect form web for “Почему соответствует строчным буквам в bash?”

Обратите внимание, что при использовании выражений диапазона, таких как [az], могут быть указаны буквы другого случая, в зависимости от настройки LC_COLLATE.

LC_COLLATE – это переменная, которая определяет порядок сортировки, используемый при сортировке результатов расширения пути, и определяет поведение выражений диапазона, классов эквивалентности и группирующих последовательностей в расширении пути и сопоставлении шаблонов.


Рассмотрим следующее:

 $ touch a A b B c C x X y Y z Z $ ls a A b B c C x X y Y z Z $ echo [az] # Note the missing uppercase "Z" a A b B c C x X y Y z $ echo [AZ] # Note the missing lowercase "a" A b B c C x X y Y z Z 

Обратите внимание, когда вызывается команда echo [az] , ожидаемым выходом будут все файлы с строчными символами. Кроме того, с помощью echo [AZ] ожидаются файлы с прописными буквами.


Стандартные сопоставления с локалями, такими как en_US имеют следующий порядок:

 aAbBcC...xXyYzZ 
  • Между a и z[az] ) есть ВСЕ прописные буквы, за исключением Z
  • Между A и Z[AZ] ) – ВСЕ строчные буквы, за исключением a .

Видеть:

  aAbBcC[...]xXyYzZ | | from a to z aAbBcC[...]xXyYzZ | | from A to Z 

Если вы измените переменную LC_COLLATE на C она выглядит как ожидаемая:

 $ export LC_COLLATE=C $ echo [az] abcxyz $ echo [AZ] ABCXYZ 

Таким образом, это не ошибка , это проблема сортировки .


Вместо выражений диапазона вы можете использовать классы символов POSIX, такие как upper или lower . Они также работают с различными конфигурациями LC_COLLATE и даже с акцентированными символами :

 $ echo [[:lower:]] abcxyz à è é $ echo [[:upper:]] ABCXYZ 

[AZ] в bash соответствует всем символам, которые сортируются после A и сортируются до Z В вашем регионе, c вероятно, сортируется между B и C.

 $ printf '%s\n' A a á b B c C Ç z Z Ẑ | sort a A á b B c C Ç z Z Ẑ 

Таким образом, c или z будут сопоставляться [AZ] , но не или a .

 $ printf '%s\n' A a á b B c C Ç z Z Ẑ | pipe> bash -c 'while IFS= read -rx; do case $x in [AZ]) echo "$x"; esac; done' A á b B c C Ç z Z 

В локали C заказ будет выглядеть так:

 $ printf '%s\n' A a á b B c C Ç z Z Ẑ | LC_COLLATE=C sort A B C Z a b c z Ç á Ẑ 

Таким образом, [AZ] будет соответствовать A , B , C , Z , но не Ç и все же не .

Если вы хотите совместить буквы верхнего регистра (в любом скрипте), вы можете использовать [[:upper:]] . В bash нет встроенного способа, чтобы соответствовать только заглавным буквам в латинском скрипте (кроме перечисления их отдельно).

Если вы хотите Z буквы A и Z английском языке без диакритики, вы можете использовать [AZ] или [[:upper:]] но в локали C

Обратите внимание, что между оболочками существует некоторая разница.

Для zsh , bash -O globasciiranges (странно названный вариант, введенный в bash-4.3), schily-sh и yash , [AZ] совпадает с символами, чья кодовая точка находится между символами A и Z , поэтому была бы эквивалентна поведение bash в локали C.

Для золы, mksh и древних оболочек, таких же, как zsh выше, но ограниченных однобайтовыми кодировками. Например, в локали UTF-8, [É-Ź] не будет совпадать с Ó , но поскольку это [<c3><89>-<c5><b9>] , это будет соответствовать байтам 0x89 0xc5!

Для ksh93 (по крайней мере, на GNU / Linux из того, что я могу сказать по латинскому скрипту). É соответствует [AZ] но не e ни é . Мне непонятно, какую информацию он использует для определения этого, но он, кажется, берет его из информации о сопоставлении локали системы и, похоже, работает так же, как соответствие регулярного выражения систем или fnmatch () (что похоже на системы GNU, основано на таблица поиска, полученная из локальных данных LC_COLLATE ).

Все действия допускаются POSIX, поскольку POSIX оставляет поведение диапазонов неопределенным в локалях, отличных от локали C. Теперь мы можем спорить о преимуществах каждого подхода.

подход bash имеет большой смысл, как с [CG] , мы хотим, чтобы символы находились между C и G И использование порядка сортировки пользователя для определения того, что является промежуточным, является наиболее логичным подходом.

Теперь проблема в том, что она превзошла ожидания многих людей, особенно тех людей, которые привыкли к традиционному поведению до Юникода, даже до интернационализации дней. Хотя от обычного пользователя это может означать, что [CI] включает h поскольку h буква находится между C и I и что [Ag] не включает Z , это другое дело для людей, которые имели дело с ASCII только на протяжении десятилетий.

Это поведение bash также отличается от соответствия диапазона [AZ] в других инструментах GNU, таких как регулярные выражения GNU (как в grep / sed …) или fnmatch() как в find -name .

Это также означает, что соответствие [AZ] зависит от среды, с ОС и версией ОС. Тот факт, что [AZ] соответствует Á, но не Ź, также субоптимален.

Для zsh / yash мы используем другой порядок сортировки. Вместо того, чтобы полагаться на пользовательское понятие символьного порядка, мы используем значения кода символьной точки. Это полезно для понимания, но с практической точки зрения немногие, вне ASCII, это не очень полезно. [AZ] соответствует 26 букв в верхнем регистре US-english, [0-9] соответствует десятичным разрядам. В Юникоде есть кодовые точки, которые следуют порядку некоторых алфавитов, но это не является обобщенным и не может быть обобщено, так как в любом случае разные люди, использующие один и тот же скрипт, не обязательно соглашаются на порядок букв.

Для традиционных оболочек и mksh, тире, он сломан (теперь большинство людей используют многобайтовые символы), но прежде всего потому, что у них еще нет поддержки нескольких байтов. Добавление многобайтовой поддержки к оболочкам, таким как bash и zsh , было огромным усилием и продолжается. yash (японская оболочка) изначально была разработана с поддержкой нескольких байтов с самого начала.

Подход ksh93 имеет преимущество в соответствии с регулярными выражениями системы или fnmatch () (или, по крайней мере, по крайней мере, по-видимому, в системах GNU). Там он не нарушает ожиданий некоторых людей, поскольку [AZ] не включает строчные буквы, [AZ] включает É (и Á, но не Ź). Это не соответствует strcoll() sort или вообще strcoll() .

Он предназначен и задокументирован в документации по bash , в разделе соответствия шаблонов . Выражение диапазона [XY] будет содержать любые символы между X и Y используя последовательность сортировки текущего языка и набор символов:

 LC_ALL=en_US.utf8 bash -c 'case b in [AZ]) echo yes; esac' yes 

Вы можете видеть, b отсортировано между A и Z в en_US.utf8 locale.

У вас есть выбор, чтобы предотвратить такое поведение:

 # Setting LC_ALL or LC_COLLATE to C LC_ALL=C bash -c 'echo [AZ]*' # Or using POSIX character class LC_ALL=C bash -c 'echo [[:upper:]]*' 

или включить globasciiranges (с bash 4.3 и выше):

 bash -O globasciiranges -c 'echo [AZ]*' 

Я наблюдал это поведение на новом экземпляре Amazon EC2. Поскольку ОП не предлагал MCVE , я отправлю одно:

 $ cd $(mktemp -d) $ touch foo $ echo [AZ]* # prepare for a surprise! foo $ echo $BASH_VERSION 4.1.2(1)-release $ uname -a Linux spinup-tmp12 3.14.27-25.47.amzn1.x86_64 #1 SMP Wed Dec 17 18:36:15 UTC 2014 x86_64 x86_64 x86_64 GNU/Linux $ env | grep LC_ # no locale, let's set one $ LC_ALL=C $ echo [AZ]* [AZ]* $ unset LC_ALL # ok, good. what if we go back to no locale? $ echo [AZ]* foo 

Таким образом, не имея моего LC_* set приводит bash 4.1.2 (1) -release в Linux, чтобы произвести явно странное поведение. Я могу надежно переключить нечетное поведение, установив и отключив соответствующие языковые переменные. Неудивительно, что это поведение кажется последовательным благодаря экспорту:

 $ export LC_ALL=C $ bash $ echo [AZ]* [AZ]* $ exit $ echo $SHLVL 1 $ unset LC_ALL $ bash $ echo [AZ]* foo 

В то время как я вижу, как бах ведет себя как Стефан «Shellshock», Chazelas ответил , я думаю, что документация bash по сопоставлению с образцами глючит:

Например, в локали C по умолчанию «[a-dx-z]» эквивалентен «[abcdxyz]»

Я прочитал это предложение (выделение мое) как «если соответствующие переменные локали не установлены, тогда bash по умолчанию будет соответствовать языку C». Кажется, Баш делает это. Вместо этого он, по-видимому, не соответствует языку, где символы сортируются в порядке словаря с диакритической складкой:

 $ echo [AE]* [AE]* $ echo [AF]* foo $ touch "évocateur" $ echo [AF]* foo évocateur 

Я думаю, было бы хорошо, если бы bash документировал, как он будет себя вести, когда LC_* (в частности, LC_CTYPE и LC_COLLATE ) не определены. Но в то же время я поделюсь некоторой мудростью :

… вы должны быть очень осторожны с [диапазонами символов], потому что они не будут давать ожидаемые результаты, если они не будут правильно настроены. Пока вы должны избегать их использования и вместо этого использовать классы символов.

а также

Если вы действительно правильны и / или используете скрипты для среды с несколькими локалями, вероятно, лучше всего убедиться, что вы знаете, что представляют собой ваши языковые переменные, когда вы соответствуете файлам, или чтобы быть уверенным, что вы кодируете полностью общий.


Обновление, основанное на комментарии @ G-Man, давайте посмотрим глубже в происходящее:

 $ env | grep LANG LANG=en_US.UTF-8 

Ах, ха! Это объясняет сопоставление, увиденное ранее. Удалим все языковые переменные:

 $ unset LANG LANGUAGE LC_ALL $ env | grep 'LC_|LANG' $ echo [AZ]* [AZ]* 

Мы идем. Теперь bash работает в соответствии с документацией по этой системе Linux. Если какая-либо из переменных языка установлена ​​( LANGUAGE , LANG , LC_COLLATE , LC_CTYPE , LC_ALL и т. Д.), LC_ALL Bash использует их в соответствии с ее руководством. В противном случае bash возвращается к C.

В FAQ Wooledge bash можно сказать следующее:

В последних системах GNU переменные используются в этом порядке. Если LANGUAGE установлен, используйте это, если LANG не установлен на C, и в этом случае LANGUAGE игнорируется. Кроме того, некоторые программы просто не используют LANGUAGE. В противном случае, если установлен LC_ALL, используйте это. В противном случае, если задана конкретная переменная LC_ *, которая охватывает это использование, используйте это. (Например, LC_MESSAGES содержит сообщения об ошибках.) В противном случае используйте LANG.

Таким образом, кажущаяся проблема, как в работе, так и в документации, может быть объяснена, если посмотреть на общую сумму всех переменных во время локали.

Locale может изменить, какие символы соответствуют [AZ] . использование

 (LC_ALL=C; rm [AZ]*) 

для устранения влияния. (Я использовал подоболочку для локализации изменения).

Как уже было сказано, это вопрос «упорядочивания заказов».

Диапазон az может содержать буквы верхнего регистра в некоторых локалях:

  aAbBcC[...]xXyYzZ | | from a to z 

Правильное решение, поскольку bash 4.3 – установить параметр globasciiranges :

 shopt -s globasciiranges 

чтобы сделать bash, как если бы LC_COLLATE=C был установлен в глобальных диапазонах.

Кажется, я нашел правильный ответ на свой вопрос:

Bash не работает, потому что он не управляет своей собственной локалью. Поэтому установка LC_ * в процессе bash не влияет на процесс оболочки.

Если вы установите LC_COLLATE = C, а затем запустите другой bash, globbing будет работать, как ожидалось, в новом процессе bash.

  • Список файлов в иерархии каталога
  • Каталог Untar из большого tarball
  • Найти файл с расширением .csv в каталоге, соответствующем шаблону
  • Использовать все хосты, завершенные zsh для cssh
  • Есть ли LC_COLLATE, который сортирует точку перед тире?
  • скопировать все файлы, не имеющие расширения
  • Способ записи нового имени файла на подстановочный знак?
  • Фильтровать дубликаты имен файлов с помощью tar
  • команда tar - пропустить символические ссылки
  • Чувствительность к регистру в квадратных скобках
  • Может ли Bash Variable Expansion выполняться непосредственно при вводе пользователя?
  • Interesting Posts

    Как исправить ошибку отключения debian jessie?

    CentOS 7.2 (теперь для обновления)

    Найти уникальные файлы между двумя каталогами (рекурсивно)

    CentOS 7 загружается слишком быстро, и сеть не готова при выполнении сценариев cron

    Выключить вывод дисплея без перезагрузки, но не в Xorg

    Linux Mint – внешний привод, заставляющий мою файловую систему выглядеть полностью и давая мне ошибку «без пробелов на устройстве»

    Что делать, если мой USB-накопитель / SD-карта защищена от записи или доступна только для чтения?

    Передача значения в качестве параметра для xargs для использования с помощью eval echo

    SSH с su и удаленной командой с использованием -c и запуска нескольких команд с параметрами

    Как отслеживать разбор и расширение параметров?

    Клавиатура не работает, когда требуется ввести пароль для зашифрованного корня

    Должен ли я добавить «> & 2» в конце команды echo?

    Как повреждение памяти обрабатывается Linux при завершении процесса?

    Перемещение файлов из разных подпапок в подпапки

    Средство просмотра изображений для нескольких изображений

    Linux и Unix - лучшая ОС в мире.