Seltsames Verhalten von tr unter Verwendung von Bereichen

10

Ich habe einen bestimmten Server, der bei der Verwendung von tr ein seltsames Verhalten zeigt. Hier ist ein Beispiel von einem funktionierenden Server:

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

Das macht für mich vollkommen Sinn.

Dies ist jedoch vom "speziellen" Server:

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

Wie Sie sehen können, schlägt das Löschen aller Kleinbuchstaben fehl. ABER es hat den Buchstaben 'o' gelöscht

Der interessante Teil sind die folgenden zwei Beispiele, die für mich überhaupt keinen Sinn ergeben:

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

(Auch hier wird das 'o' im letzten Beispiel gelöscht.)

Hat jemand eine Idee was hier los ist? Ich kann auf keiner anderen Linux-Box, die ich verwende, reproduzieren.

Chris
quelle
5
Tangential verwandt: trBereiche werden ohne Beilage geschrieben [...]. So tr -d '[a-z]'wird getötet a-z, und auch Charaktere [und ]. Verwenden Sie tr -d a-zdiese Option , um nur Buchstaben zu töten a-z.
Satō Katsura

Antworten:

24

Sie haben eine Datei mit dem Namen oim aktuellen Verzeichnis

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

Die Shell erweitert den [a-z]String, wenn eine Übereinstimmung gefunden wird.

Dies wird laut man bash

Pfadnamenerweiterung
Nach dem Aufteilen von Wörtern durchsucht bash jedes Wort nach den Zeichen * ,? Und [ , sofern nicht die Option -f festgelegt wurde. ... (...)

Bash führt eine Erweiterung durch.

[...] Entspricht einem der beiliegenden Zeichen.

Archemar
quelle
Zum Beispiel unter Verwendung @ Chris Sie können die Expansion des Shell überprüfen echo: touch o ; echo tr -d [a-z]gibt diese:tr -d o
pabouk
8

Was ist los

Die Shell (Bash) sieht das Argument [a-z]. Dies ist ein Platzhaltermuster (ein Glob ), das mit jedem Kleinbuchstaben übereinstimmt¹. Daher sucht die Shell nach einem Dateinamen, der diesem Muster entspricht. Es gibt drei Fälle:

  • Keine Datei im aktuellen Verzeichnis hat einen Namen, der aus einem einzelnen Kleinbuchstaben besteht. Dann lässt die Shell das Platzhaltermuster unverändert und trsieht die Argumente -dund [a-z]. Dies geschieht auf den meisten Ihrer Maschinen.
  • Eine einzelne Datei im aktuellen Verzeichnis hat einen Namen, der aus einem einzelnen Kleinbuchstaben besteht. Dann erweitert die Shell das Muster auf diesen Dateinamen und trsieht die Argumente -dund den Dateinamen. Dies geschieht auf dem Server, und die übereinstimmende Datei wird aufgerufen, oda wir sehen können, dass trder Buchstabe gelöscht wurde o.
  • Zwei oder mehr Dateien im aktuellen Verzeichnis haben einen Namen, der aus einem einzelnen Kleinbuchstaben besteht. Anschließend erweitert die Shell das Muster auf die Liste der übereinstimmenden Dateinamen und trsieht drei oder mehr Argumente: -dund die Dateinamen. Da trnachher ein einziges Argument erwartet wird -d, wird es sich beschweren.

Was du hätte tun sollen

Wenn das Argument eines Befehls Sonderzeichen enthält, müssen Sie diese maskieren. Setzen Sie das Argument in einfache Anführungszeichen '…'(dies ist der einfachste Weg, es gibt andere). In einfachen Anführungszeichen stehen alle Zeichen für sich selbst, mit Ausnahme des einfachen Anführungszeichens. Wenn das Argument ein einfaches Anführungszeichen enthält, ersetzen Sie es durch'\'' .

tr -d '[a-z]'

Beachten Sie jedoch, dass dies wahrscheinlich immer noch nicht das ist, was Sie gemeint haben! Dies weist tran, Kleinbuchstaben und eckige Klammern zu löschen. Es ist äquivalent zu tr -d ']a-z[', tr '[]a-z'usw. Kleinbuchstaben zu löschen, verwenden

tr -d a-z

Das Argument für trist ein Zeichensatz. Sie setzen Klammern um einen Zeichensatz in einem regulären Ausdruck oder einem Platzhaltermuster, um anzuzeigen, dass es sich um einen Zeichensatz handelt. Arbeitet aber jeweils tran einem einzelnen Charakter. Die Befehlszeilenargumente werden in Klammern gesetzt .

Sie benötigen Klammern, um Zeichenklassen anzugeben . In einem regulären Ausdruck verwenden Sie Klammern in Klammern, um eine Zeichenklasse anzugeben, z. B. eine [[:lower:]]*beliebige Anzahl von Kleinbuchstaben, eine [[:lower:]_]*beliebige Anzahl von Kleinbuchstaben und Unterstrichen. Im Argument von trbenötigen Sie die Menge ohne die umgebenden Klammern, also werden tr -d '[:lower:]'Kleinbuchstaben, tr -d '[:lower:]_'Kleinbuchstaben und Unterstriche usw. gelöscht.

¹ In einigen Gebietsschemas kann es mit anderen Zeichen übereinstimmen .

Gilles 'SO - hör auf böse zu sein'
quelle
1
Beachten Sie, dass Sie unter Solaris 10 (und anderen alten SysV-basierten Unices) tr -d '[a-z]'mit benötigen /usr/bin/tr. Mit /usr/xpg4/bin/tr, tr -d a-zfunktioniert aber tr -d '[a-z]'nicht gelöscht [noch ].
Stéphane Chazelas
1
/usr/xpg4/bin/tr -d '[a-z]'weder gelöscht [noch ]anscheinend in Solaris 11 behoben.
Stéphane Chazelas