Dziwne zachowanie tr przy użyciu zakresów

10

Mam jeden konkretny serwer, który wykazuje dziwne zachowanie podczas używania tr. Oto przykład z działającego serwera:

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

To ma dla mnie idealny sens.

Wynika to jednak z serwera „specjalnego”:

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

Jak widać, usuwanie wszystkich małych liter kończy się niepowodzeniem. ALE usunęła literę „o”

Interesującą częścią są dwa następujące przykłady, które nie mają dla mnie żadnego sensu:

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

(ponownie „o” zostało usunięte w ostatnim przykładzie)

Czy ktoś ma pojęcie, co się tutaj dzieje? Nie mogę reprodukować na żadnym innym urządzeniu z linuksem, którego używam.

Chris
źródło
5
Powiązane stycznie: trzakresy są zapisywane bez otaczania [...]. Więc tr -d '[a-z]'zabije a-z, a także postacie [i ]. Służy tr -d a-zdo zabijania tylko liter a-z.
Satō Katsura

Odpowiedzi:

24

masz plik o nazwie ow bieżącym katalogu

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

powłoka rozwinie [a-z]ciąg, jeśli zostanie znalezione dopasowanie.

Nazywa się to rozszerzaniem nazw ścieżek man bash

Rozszerzenie
nazwy ścieżki Po podziale słów, chyba że ustawiono opcję -f, bash skanuje każde słowo w poszukiwaniu znaków *,? I [. ... (...)

bash wykona ekspansję.

[...] Dopasowuje dowolny z zamkniętych znaków.

Archemar
źródło
@Chris Możesz sprawdzić ekspansję powłoki, używając na przykład echo: touch o ; echo tr -d [a-z]daje to:tr -d o
pabouk
8

Co się dzieje

Powłoka (bash) widzi argument [a-z]. Jest to wzór wieloznaczny ( glob ), który pasuje do każdej małej litery¹. Dlatego powłoka szuka nazwy pliku pasującej do tego wzorca. Istnieją trzy przypadki:

  • Żaden plik w bieżącym katalogu nie ma nazwy, która jest pojedynczą małą literą. Następnie powłoka pozostawia wzór wieloznaczny bez zmian i trwidzi argumenty -di [a-z]. Tak dzieje się na większości twoich maszyn.
  • Pojedynczy plik w bieżącym katalogu ma nazwę, która jest pojedynczą małą literą. Następnie powłoka rozwija wzorzec do tej nazwy pliku i trwidzi argumenty -doraz nazwę pliku. Dzieje się tak na serwerze i wywoływany jest odpowiedni plik, oponieważ widzimy, że trusunął list o.
  • Dwa lub więcej plików w bieżącym katalogu ma nazwę składającą się z jednej małej litery. Następnie powłoka rozwija wzorzec do listy pasujących nazw plików i trwidzi trzy lub więcej argumentów: -di nazwy plików. Ponieważ troczekuje później jednego argumentu -d, będzie narzekać.

Co powinieneś był zrobić

Jeśli w argumencie polecenia są znaki specjalne, musisz je uciec. Umieść argument w pojedynczych cudzysłowach '…'(jest to najprostszy sposób, istnieją inne). Wewnątrz pojedynczych cudzysłowów wszystkie postacie są sobą, z wyjątkiem samego pojedynczego cudzysłowu. Jeśli wewnątrz argumentu znajduje się pojedynczy cytat, zamień go na'\'' .

tr -d '[a-z]'

Pamiętaj jednak, że prawdopodobnie nie to miałeś na myśli! Mówi trto o usuwaniu małych liter i nawiasów kwadratowych. To odpowiednik tr -d ']a-z[', tr '[]a-z'itp Aby usunąć małe litery, stosowanie

tr -d a-z

Argumentem trjest zestaw znaków. Umieszczasz nawiasy wokół zestawu znaków w wyrażeniu regularnym lub wzorze wieloznacznym, aby wskazać, że jest to zestaw znaków. Ale trdziała na jedną postać na raz. Argumenty wiersza poleceń są tym, co należy umieścić w nawiasach .

Potrzebujesz nawiasów, aby wskazać klasy znaków . W wyrażeniu regularnym używasz nawiasów wewnątrz nawiasów, aby wskazać klasę znaków, np. [[:lower:]]*Pasuje do dowolnej liczby małych liter, [[:lower:]_]*pasuje do dowolnej liczby małych liter i podkreśleń. Jako argument trpotrzebujesz zestawu bez otaczających go nawiasów, więc tr -d '[:lower:]'usuwa małe litery, tr -d '[:lower:]_'usuwa małe litery i podkreślenia itp.

¹ W niektórych lokalizacjach może pasować do innych znaków .

Gilles „SO- przestań być zły”
źródło
1
Należy pamiętać, że w systemie Solaris 10 (i innych Uniksach starożytny SysV based), trzeba zrobić tr -d '[a-z]'z /usr/bin/tr. Dzięki /usr/xpg4/bin/tr, tr -d a-zdziała, ale tr -d '[a-z]'nie usuwa [ani ].
Stéphane Chazelas
1
/usr/xpg4/bin/tr -d '[a-z]'nie []
kasuje się