Во всех известных мне оболочках rm [A-Z]*
удаляются все файлы, которые начинаются с заглавной буквы, но с помощью bash это удаляет все файлы, начинающиеся с буквы.
Так как эта проблема существует в Linux и Solaris с bash-3 и bash-4, она не может быть ошибкой, вызванной ошибочным сопоставлением шаблонов в libc или неверно настроенным определением локали.
Предполагается ли это странное и рискованное поведение или это просто ошибка, которая существует неуклонно в течение многих лет?
locale
выводит? Я не могу воспроизвести это (touch foo; echo [A-Z]*
выводит буквальный шаблон, а не "foo", в другом пустом каталоге).# echo [A-Z]* ; export LC_COLLATE=C ; echo [A-Z]*
A b B z ZABZОтветы:
LC_COLLATE
является переменной, которая определяет порядок сопоставления, используемый при сортировке результатов раскрытия имени пути, и определяет поведение выражений диапазона, классов эквивалентности и последовательностей сортировки в раскрытии имени пути и сопоставлении с образцом.Учтите следующее:
Обратите внимание, что при
echo [a-z]
вызове команды ожидаемым результатом будут все файлы с символами нижнего регистра. Кроме того, сecho [A-Z]
, файлы с заглавными буквами ожидается.Стандартные сопоставления с локалями, например,
en_US
имеют следующий порядок:a
иz
(в[a-z]
) находятся ВСЕ заглавные буквы, кромеZ
.A
иZ
(в[A-Z]
) находятся ВСЕ строчные буквы, кромеa
.Видеть:
Если вы измените
LC_COLLATE
переменную наC
это выглядит как ожидалось:Так что это не ошибка , это проблема сопоставления .
Вместо выражений диапазона вы можете использовать классы символов, определенные в POSIX , такие как
upper
илиlower
. Они также работают с различнымиLC_COLLATE
конфигурациями и даже с акцентированными символами :источник
tr
поэтому я проверил это в первую очередь.LC_COLLATE
что также задокументировано в руководстве.[A-Z]
inbash
сопоставляет все элементы сортировки (символы, но call также являются последовательностью символов, какDsz
в венгерских локалях), которые сортируют послеA
и сортируют доZ
. В вашем регионе,c
вероятно, сортирует между B и C.Так
c
илиz
будет соответствовать[A-Z]
, но неẐ
илиa
.В локали C, порядок будет:
Так
[A-Z]
будет соответствоватьA
,B
,C
,Z
, но неÇ
и до сих пор неẐ
.Если вы хотите сопоставить буквы верхнего регистра (в любом скрипте), вы можете использовать
[[:upper:]]
вместо этого. Там нет встроенного способа,bash
чтобы соответствовать только заглавные буквы в латинском скрипте (за исключением перечисления их по отдельности).Если вы хотите , чтобы соответствовать
A
наZ
английском языке буквами без диакритики, вы можете использовать[A-Z]
или ,[[:upper:]]
но вC
локали (предполагается , что данные не кодируются в наборах символов , таких как BIG5 или GB18030 , который имеет несколько символов , чья кодировка содержит кодировку этих букв) или список их индивидуально ([ABCDEFGHIJKLMNOPQRSTUVWXYZ]
).Обратите внимание, что есть некоторые различия между оболочками.
For
zsh
,bash -O globasciiranges
(странно названная опция, введенная в bash-4.3),schily-sh
иyash
,[A-Z]
совпадает с символами, чья кодовая точка находится между той изA
и той изZ
, так что будет эквивалентно поведениюbash
в локали Си.Для пепла, mksh и древних оболочек, таких же, как
zsh
указано выше, но ограничено однобайтовыми символами. То есть, например, в локали UTF-8[É-Ź]
не будет совпадатьÓ
, но так как[<c3><89>-<c5><b9>]
это будет соответствовать байтовым значениям от 0x89 до 0xc5!ksh93
ведет себя так же,bash
за исключением того, что он обрабатывает как особые случаи, концы которых начинаются с строчных или заглавных букв. В этом случае он сопоставляется только с элементами упорядочения, которые сортируются между этими концами, но которые (или их первый символ для многосимвольных элементов упорядочения) также являются строчными (или прописными соответственно). Так[A-Z]
было бы соответствовать наÉ
, но неe
такe
же рода междуA
и ,Z
но не в верхнем регистре , какA
иZ
.Для
fnmatch()
шаблонов (как вfind -name '[A-Z]'
) или системных регулярных выражений (как вgrep '[A-Z]'
) это зависит от системы и локали. Например, в системе GNU здесь[A-Z]
не совпадаетx
вen_GB.UTF-8
локали, но вth_TH.UTF-8
одной. Мне неясно, какую информацию он использует для определения этого, но, очевидно, он основан на справочной таблице, полученной из данных локали LC_COLLATE ).POSIX разрешает все варианты поведения, поскольку POSIX оставляет поведение диапазонов, не заданных в локалях, отличных от локали C. Теперь мы можем спорить о преимуществах каждого подхода.
bash
Этот подход имеет большой смысл, так как[C-G]
мы хотим, чтобы символы между нимиC
иG
. И использование порядка сортировки пользователя для определения того, что находится между ними, является наиболее логичным подходом.Теперь проблема в том, что это разрушает ожидания многих людей, особенно тех, кто привык к традиционному поведению до Юникода, даже до интернационализации. В то время как от обычного пользователя, это , возможно , ощущение того, что
[C-I]
включает в себяh
какh
буква междуC
иI
и что[A-g]
не включает в себяZ
, это другое дело для людей , имеющих дело с ASCII только в течение десятилетий.Это
bash
поведение также отличается от[A-Z]
сопоставления диапазонов в других инструментах GNU, таких как регулярные выражения GNU (как вgrep
/sed
...) илиfnmatch()
как вfind -name
.Это также означает, что то, что
[A-Z]
совпадает, зависит от среды, от ОС и от версии ОС. Тот факт, что[A-Z]
соответствует А, но не Ź, также неоптимален.Для
zsh
/yash
мы используем другой порядок сортировки. Вместо того, чтобы полагаться на представление пользователя о порядке символов, мы используем значения кода символа. Преимущество этого заключается в том, что его легко понять, но с практической точки зрения немногие за пределами ASCII не очень полезны.[A-Z]
соответствует 26 заглавным буквам английского языка США,[0-9]
соответствует десятичным цифрам. В Unicode есть кодовые точки, которые следуют порядку некоторых алфавитов, но они не обобщены и не могут быть обобщены, так как в любом случае разные люди, использующие один и тот же сценарий, не обязательно соглашаются с порядком букв.Для традиционных оболочек и mksh, dash, он не работает (теперь, когда большинство людей используют многобайтовые символы), но прежде всего потому, что у них пока нет многобайтовой поддержки. Добавление многобайтовой поддержки для таких оболочек, как
bash
иzsh
было огромным усилием, все еще продолжается.yash
(японская оболочка) изначально была разработана с многобайтовой поддержкой с самого начала.Подход ksh93 имеет то преимущество, что он согласуется с регулярными выражениями системы или fnmatch () (или, по крайней мере, кажется, по крайней мере, в системах GNU). Там это не нарушает ожидание некоторых людей, поскольку
[A-Z]
не включает строчные буквы,[A-Z]
включаетÉ
(и Á, но не Ź). Это не соответствуетsort
или вообщеstrcoll()
порядок.источник
mksh
(оба получены из pdksh).posh -c $'case Ó in [É-Ź]) echo yes; esac'
ничего не возвращаетsort
потому чтоbash
глобусы основаны на порядке сортировки символов. В настоящее время у меня нет доступа к такой старой версииbash
, но я могу проверить позже. Было ли это иначе?\xFF
есть байт 0xFF, а не символ U + 00FF (ÿ
сам кодируется как 0xC3 0xBF).\xFF
само по себе не образует действительный символ, поэтому я не могу понять, почему он должен соответствовать[É-Ź]
.Он предназначен и задокументирован в
bash
документации, в разделе сопоставления с образцом . Выражение диапазона[X-Y]
будет включать любые символы между последовательностью сортировки и набором символов текущей локалиX
иY
с ее использованием:Вы можете видеть,
b
отсортированный междуA
иZ
вen_US.utf8
локали.У вас есть несколько способов предотвратить такое поведение:
или включить
globasciiranges
(с bash 4.3 и выше):источник
Я наблюдал такое поведение на новом экземпляре Amazon EC2. Поскольку ОП не предлагал MCVE , я выложу один:
Таким образом, отсутствие моего
LC_*
набора приводит к выпуску bash 4.1.2 (1) в Linux, что приводит к явно странному поведению. Я могу надежно переключать нечетное поведение, устанавливая и отменяя соответствующие переменные локали. Неудивительно, что это поведение выглядит последовательным при экспорте:В то время как я вижу, что bash ведет себя так, как ответил Шезел Стефан "Shellshock" , я думаю, что документация bash по сопоставлению с образцом содержит ошибки:
Я прочитал это предложение (выделение мое) как «если соответствующие переменные локали не установлены, то по умолчанию bash будет использовать локаль C». Баш, похоже, не делает этого. Вместо этого, по-видимому, по умолчанию используется локаль, в которой символы отсортированы в порядке словаря с диакритическим свертыванием:
Я думаю, что для bash было бы хорошо документировать, как он будет себя вести, когда
LC_*
(в частности,LC_CTYPE
иLC_COLLATE
) не определены. Но пока я поделюсь с вами некоторой мудростью :а также
Обновление На основе комментария @ G-Man давайте посмотрим глубже на происходящее:
Ах, ха! Это объясняет сопоставление, замеченное ранее. Давайте удалим все переменные локали:
Там мы идем. Теперь bash работает согласованно с документацией по этой системе Linux. Если какие - либо из локализаций переменных устанавливается (
LANGUAGE
,LANG
,LC_COLLATE
,LC_CTYPE
,LC_ALL
и т.д.) , то Bash использует те в соответствии с ее руководством. В противном случае Bash возвращается к C.В FAQ по Wooledge bash есть следующее:
Таким образом, очевидную проблему, как в работе, так и в документации, можно объяснить, посмотрев на общую сумму всех движущих переменных локали.
источник
C
локали, это ошибка.env | grep LANG
илиecho "$LANG"
.LANG
. С этим намеком все объясняется.Локаль может изменить какие символы соответствуют
[A-Z]
. использованиеустранить влияние. (Я использовал подоболочку для локализации изменений).
источник
export LC_ALL=C
первым.Как уже было сказано, это проблема «порядка упорядочения».
Диапазон az может содержать заглавные буквы в некоторых локалях:
Правильное решение, начиная с bash 4.3, заключается в установке опции
globasciiranges
:заставить bash действовать так, как если бы
LC_COLLATE=C
он был установлен в диапазонах глобализации .источник
Кажется, я нашел правильный ответ на свой вопрос:
Bash глючит, так как не управляет собственной локалью. Таким образом, установка LC_ * в процессе bash не влияет на этот процесс оболочки.
Если вы установите LC_COLLATE = C и затем запустите другой bash, глобализация будет работать, как и ожидалось, в новом процессе bash.
источник
export
это неправильно.