Comportamento estranho de tr usando intervalos

10

Eu tenho um servidor específico que está exibindo um comportamento estranho ao usar tr. Aqui está um exemplo de um servidor de trabalho:

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

Isso faz todo o sentido para mim.

Isso, no entanto, é do servidor 'especial':

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

Como você pode ver, a exclusão de todos os caracteres minúsculos falha. MAS, ele excluiu a letra 'o'

A parte interessante são os dois exemplos a seguir, que não fazem sentido para mim:

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

(novamente, o 'o' é excluído no último exemplo)

Alguém tem alguma idéia do que está acontecendo aqui? Não consigo reproduzir em nenhuma outra caixa Linux que estou usando.

Chris
fonte
5
Relacionado tangencialmente: os trintervalos são gravados sem o anexo [...]. Então tr -d '[a-z]'vai matar a-z, e também personagens [e ]. Use tr -d a-zpara matar apenas letras a-z.
Satō Katsura

Respostas:

24

você tem um arquivo nomeado ono diretório atual

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

shell expandirá a [a-z]string se uma correspondência for encontrada.

Isso é chamado de expansão do nome do caminho, de acordo com man bash

Expansão do nome do caminho
Após a divisão da palavra, a menos que a opção -f tenha sido definida, o bash verifica cada palavra em busca dos caracteres *,? E [. ... (...)

o bash executará a expansão.

[...] Corresponde a qualquer um dos caracteres incluídos.

Archemar
fonte
@ Chris Você pode verificar a expansão do shell usando, por exemplo echo: touch o ; echo tr -d [a-z]dá a este:tr -d o
pabouk
8

O que está acontecendo

O shell (bash) vê o argumento [a-z]. Esse é um padrão curinga ( glob ), que corresponde a qualquer letra minúscula¹. Portanto, o shell procura um nome de arquivo que corresponda a esse padrão. Existem três casos:

  • Nenhum arquivo no diretório atual tem um nome que é uma única letra minúscula. Em seguida, o shell deixa o padrão curinga inalterado e trvê os argumentos -de [a-z]. É o que acontece na maioria das suas máquinas.
  • Um único arquivo no diretório atual tem um nome que é uma única letra minúscula. Em seguida, o shell expande o padrão para esse nome de arquivo e trvê os argumentos -de o nome do arquivo. Isso acontece no servidor e o arquivo correspondente é chamado, opois podemos ver que tra letra foi excluída o.
  • Dois ou mais arquivos no diretório atual têm um nome que é uma única letra minúscula. Em seguida, o shell expande o padrão para a lista de nomes de arquivos correspondentes e trvê três ou mais argumentos: -de os nomes dos arquivos. Como trespera um único argumento depois -d, ele irá reclamar.

O que você deveria ter feito

Se houver caracteres especiais no argumento de um comando, você deverá escapar deles. Coloque o argumento entre aspas simples '…'(esta é a maneira mais simples, existem outras). Dentro de aspas simples, todos os caracteres se representam, exceto a própria aspas simples. Se houver uma aspas simples dentro do argumento, substitua-o por'\'' .

tr -d '[a-z]'

No entanto, note que isso provavelmente ainda não é o que você quis dizer! Isso indica trpara excluir letras minúsculas e colchetes. É equivalente a tr -d ']a-z[', tr '[]a-z'etc. Para excluir letras minúsculas, use

tr -d a-z

O argumento para tré um conjunto de caracteres. Você coloca colchetes em torno de um conjunto de caracteres em uma expressão regular ou padrão curinga para indicar que é um conjunto de caracteres. Mas trfunciona em um único personagem de cada vez. Seus argumentos de linha de comando são o que você colocaria dentro dos colchetes .

Você precisa de colchetes para indicar as classes de caracteres . Em uma expressão regular, você usa colchetes entre colchetes para indicar uma classe de caracteres, por exemplo, [[:lower:]]*corresponde a qualquer número de letras minúsculas, [[:lower:]_]*corresponde a qualquer número de letras minúsculas e sublinhados. No argumento de tr, você precisa do conjunto sem os colchetes, portanto, tr -d '[:lower:]'exclui letras minúsculas, tr -d '[:lower:]_'exclui letras minúsculas e sublinhados, etc.

¹ Em alguns locais, pode corresponder a outros caracteres .

Gilles 'SO- parar de ser mau'
fonte
1
Note-se que no Solaris 10 (e outros Unices base antiga SysV), você precisa fazer tr -d '[a-z]'com /usr/bin/tr. Com /usr/xpg4/bin/tr, tr -d a-zfunciona , mas tr -d '[a-z]'não exclui [nem ].
Stéphane Chazelas
1
/usr/xpg4/bin/tr -d '[a-z]'não foi []
excluído