Comportamento estranho de tr usando intervalos

10

Eu tenho um servidor em particular que está exibindo um comportamento estranho ao usar o tr. Aqui está um exemplo de um servidor em funcionamento:

-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, apagou a letra 'o'

A parte interessante são os dois exemplos seguintes, 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 ideia do que está acontecendo aqui? Não consigo reproduzir em nenhuma outra caixa do Linux que estou usando.

    
por Chris 03.07.2018 / 12:23

2 respostas

24

você tem um arquivo chamado o no diretório atual

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

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

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

Pathname Expansion
After word splitting, unless the -f option has been set, bash scans each word for the characters *, ?, and [. ... (...)

o bash executará a expansão.

[...] Matches any one of the enclosed characters.

    
por 03.07.2018 / 12:27
8

O que está acontecendo

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

  • Nenhum arquivo no diretório atual tem um nome que seja uma única letra minúscula. Em seguida, o shell deixa o padrão de curinga inalterado e tr vê os argumentos -d e [a-z] . Isto é 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 tr vê os argumentos -d e o nome do arquivo. Isso acontece no servidor, e o arquivo correspondente é chamado de o , pois podemos ver que tr excluiu a letra 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 tr vê três ou mais argumentos: -d e os nomes dos arquivos. Como tr espera um único argumento depois de -d , ele irá reclamar.

O que você deveria ter feito

Se houver caracteres especiais no argumento de um comando, você deve escapar deles. Coloque o argumento entre aspas simples '…' (esta é a maneira mais simples, existem outras). Dentro de aspas simples, todos os caracteres representam a si próprios, exceto a simples citação. Se houver uma única citação dentro do argumento, substitua por '\'' .

tr -d '[a-z]'

No entanto, note que isso provavelmente ainda não é o que você quis dizer! Isso diz a tr para 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 tr funciona em um único caractere de cada vez. Seus argumentos de linha de comando são o que você colocaria dentro dos colchetes .

Você precisa de colchetes para indicar classes de caracteres . Em uma expressão regular, você usa colchetes entre colchetes para indicar uma classe de caractere, 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 seus colchetes ao redor, então 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 .

    
por 03.07.2018 / 22:25