Comportamiento extraño de tr usando rangos

10

Tengo un servidor en particular que exhibe un comportamiento extraño cuando uso tr. Aquí hay un ejemplo de un servidor que funciona:

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

Eso tiene mucho sentido para mí.

Esto, sin embargo, es del servidor 'especial':

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

Como puede ver, la eliminación de todos los caracteres en minúscula falla. PERO, ha eliminado la letra 'o'

La parte interesante son los siguientes dos ejemplos, que no tienen ningún sentido para mí:

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

(de nuevo, la 'o' se elimina en el último ejemplo)

¿Alguien tiene alguna idea de lo que está pasando aquí? No puedo reproducir en ninguna otra caja de Linux que estoy usando.

Chris
fuente
55
Relacionado tangencialmente: los trrangos se escriben sin encerrar [...]. Así tr -d '[a-z]'matará a-z, y también personajes [y ]. Use tr -d a-zpara matar solo letras a-z.
Satō Katsura el

Respuestas:

24

tienes un archivo nombrado oen el directorio actual

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

Shell expandirá la [a-z]cadena si se encuentra una coincidencia.

Esto se llama expansión de nombre de ruta, de acuerdo con man bash

Expansión del nombre de ruta
Después de la división de palabras, a menos que se haya establecido la opción -f, bash escanea cada palabra para los caracteres * ,? Y [. ... (...)

bash realizará la expansión.

[...] Coincide con cualquiera de los caracteres incluidos.

Archemar
fuente
@Chris Puede verificar la expansión del shell utilizando, por ejemplo echo: touch o ; echo tr -d [a-z]da esto:tr -d o
pabouk
8

Qué está pasando

El shell (bash) ve el argumento [a-z]. Ese es un patrón comodín (un globo ), que coincide con cualquier letra minúscula¹. Por lo tanto, el shell busca un nombre de archivo que coincida con este patrón. Hay tres casos:

  • Ningún archivo en el directorio actual tiene un nombre que sea una letra minúscula. Luego, el shell deja el patrón comodín sin cambios y trve los argumentos -dy [a-z]. Esto es lo que sucede en la mayoría de sus máquinas.
  • Un solo archivo en el directorio actual tiene un nombre que es una letra minúscula. Luego, el shell expande el patrón a este nombre de archivo y trve los argumentos -dy el nombre del archivo. Esto sucede en el servidor, y se llama al archivo correspondiente oya que podemos ver que treliminó la letra o.
  • Dos o más archivos en el directorio actual tienen un nombre que es una letra minúscula. Luego, el shell expande el patrón a la lista de nombres de archivos coincidentes y trve tres o más argumentos: -dy los nombres de los archivos. Como trespera un único argumento después -d, se quejará.

Lo que deberías haber hecho

Si hay caracteres especiales en el argumento de un comando, debe escapar de ellos. Ponga el argumento entre comillas simples '…'(esta es la forma más simple, hay otras). Dentro de las comillas simples, todos los personajes se representan a sí mismos, excepto la comilla simple. Si hay una comilla simple dentro del argumento, reemplácela por'\'' .

tr -d '[a-z]'

Sin embargo, tenga en cuenta que esto probablemente no sea lo que quería decir. Esto le indica trque elimine las letras minúsculas y los corchetes. Es equivalente a tr -d ']a-z[', tr '[]a-z', etc. Para eliminar letras minúsculas, el uso

tr -d a-z

El argumento para tres un conjunto de caracteres. Pone corchetes alrededor de un conjunto de caracteres en una expresión regular o patrón comodín para indicar que es un conjunto de caracteres. Pero trfunciona en un solo personaje a la vez. Sus argumentos de línea de comando son lo que pondría dentro de los corchetes .

Necesita paréntesis para indicar las clases de caracteres . En una expresión regular, utiliza corchetes dentro de corchetes para indicar una clase de caracteres, por ejemplo, [[:lower:]]*coincide con cualquier número de letras minúsculas, [[:lower:]_]*coincide con cualquier número de letras minúsculas y guiones bajos. En el argumento de tr, necesita el conjunto sin sus corchetes circundantes, por lo que tr -d '[:lower:]'elimina letras minúsculas, tr -d '[:lower:]_'elimina letras minúsculas y guiones bajos, etc.

¹ En algunos entornos locales puede coincidir con otros caracteres .

Gilles 'SO- deja de ser malvado'
fuente
1
Tenga en cuenta que en Solaris 10 (y otros sistemas Unix basados antigua SysV), sí es necesario tr -d '[a-z]'con /usr/bin/tr. Con /usr/xpg4/bin/tr, tr -d a-zfunciona pero tr -d '[a-z]'no elimina [ni ].
Stéphane Chazelas
1
/usr/xpg4/bin/tr -d '[a-z]'no se elimina [ni ]aparentemente se corrigió en Solaris 11.
Stéphane Chazelas