Странное поведение tr с использованием диапазонов

10

У меня есть один конкретный сервер, который демонстрирует странное поведение при использовании tr. Вот пример с рабочего сервера:

-bash-3.2$ echo "abcdefghijklmnopqrstuvwxyz1234567890"|tr -d [a-z]
1234567890
-bash-3.2$

Это имеет смысл для меня.

Это, однако, с «специального» сервера:

[root@host~]# echo "abcdefghijklmnopqrstuvwxyz1234567890"|tr -d [a-z]
abcdefghijklmnpqrstuvwxyz1234567890

Как видите, удалить все строчные буквы не удается. НО, он удалил букву «о»

Интересная часть - следующие два примера, которые не имеют никакого смысла для меня:

[root@host~]# echo "abcdefghijklmnopqrstuvwxyz1234567890"|tr -d [a-n]
opqrstuvwxyz1234567890
[root@host~]# echo "abcdefghijklmnopqrstuvwxyz1234567890"|tr -d [a-o]
abcdefghijklmnpqrstuvwxyz1234567890
[root@host~]#

(опять же, 'o' удаляется в последнем примере)

Кто-нибудь знает, что здесь происходит? Я не могу воспроизвести на любой другой Linux-коробке, которую я использую.

Крис
источник
5
Тангенциально связанные: trдиапазоны записываются без огибающей [...]. Так tr -d '[a-z]'что убьют a-z, а также персонажей [и ]. Используйте, tr -d a-zчтобы убить только буквы a-z.
Satō Katsura

Ответы:

24

у вас есть файл с именем oв текущем каталоге

foo> ls
foo> echo "abcdefghijklmnopqrstuvwxyz1234567890"|tr -d [a-z]
1234567890
foo> touch o
foo> echo "abcdefghijklmnopqrstuvwxyz1234567890"|tr -d [a-z]
abcdefghijklmnpqrstuvwxyz1234567890

shell расширит [a-z]строку, если совпадение найдено.

Это называется расширением пути, согласно man bash

Расширение имени пути
После разделения слов, если не установлена ​​опция -f, bash сканирует каждое слово на наличие символов * ,? И [. ... (...)

Bash выполнит расширение.

[...] Соответствует любому из вложенных символов.

Archemar
источник
@ Крис Вы можете проверить расширение командного интерпретатора , используя, например echo: touch o ; echo tr -d [a-z]дает это:tr -d o
pabouk
8

Что случилось

Оболочка (bash) видит аргумент [a-z]. Это шаблон подстановочного знака ( глобус ), который соответствует любой строчной букве¹. Поэтому оболочка ищет имя файла, которое соответствует этому шаблону. Есть три случая:

  • Ни один файл в текущем каталоге не имеет имени, состоящего из одной строчной буквы. Затем оболочка оставляет шаблон подстановки без изменений и trвидит аргументы -dи [a-z]. Это то, что происходит на большинстве ваших машин.
  • Один файл в текущем каталоге имеет имя, которое представляет собой одну строчную букву. Затем оболочка расширяет шаблон до этого имени файла и trвидит аргументы -dи имя файла. Это происходит на сервере, и соответствующий файл вызывается, oпоскольку мы видим, что trудалили букву o.
  • Два или более файлов в текущем каталоге имеют имя, которое представляет собой одну строчную букву. Затем оболочка расширяет шаблон до списка совпадающих имен файлов и trвидит три или более аргумента: -dи имена файлов. Так как trожидает один аргумент после -d, он будет жаловаться.

Что ты должен был сделать

Если в аргументе команды есть специальные символы, вы должны их избежать. Поместите аргумент в одинарные кавычки '…'(это самый простой способ, есть и другие). Внутри одинарных кавычек все символы обозначают самих себя, кроме самой одинарной. Если внутри аргумента есть одиночная кавычка, замените ее на'\'' .

tr -d '[a-z]'

Однако обратите внимание, что это, вероятно, все еще не то, что вы имели в виду! Это говорит trоб удалении строчных букв и квадратных скобок. Это эквивалентно tr -d ']a-z[', tr '[]a-z'и т.д. Для удаления строчных букв, использование

tr -d a-z

Аргументом trявляется набор символов. Вы ставите скобки вокруг набора символов в регулярном выражении или шаблоне подстановки, чтобы указать, что это набор символов. Но trработает на одного персонажа одновременно. Его аргументы командной строки - это то, что вы должны поместить в скобки .

Вам нужны скобки для обозначения классов символов . В регулярном выражении вы используете квадратные скобки для обозначения класса символов, например, [[:lower:]]*соответствует любому количеству строчных букв, [[:lower:]_]*соответствует любому количеству строчных букв и подчеркиваний. В качестве аргумента trвам нужен набор без окружающих его скобок, поэтому tr -d '[:lower:]'удаляются строчные буквы, tr -d '[:lower:]_'удаляются строчные буквы и подчеркивания и т. Д.

Some В некоторых регионах он может совпадать с другими символами .

Жиль "ТАК - перестань быть злым"
источник
1
Обратите внимание , что на Solaris 10 (и других на основе древнего SysV юниксы), вам нужно tr -d '[a-z]'с /usr/bin/tr. С /usr/xpg4/bin/tr, tr -d a-zработает, но tr -d '[a-z]'не удаляет [ни ].
Стефан Шазелас
1
/usr/xpg4/bin/tr -d '[a-z]'не удалял и [не ]был, по-видимому, исправлен в Solaris 11.
Стефан Шазелас