Strano comportamento di tr utilizzando gli intervalli


10

Ho un server particolare che mostra comportamenti strani quando si usa tr. Ecco un esempio da un server funzionante:

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

Questo ha perfettamente senso per me.

Questo, tuttavia, proviene dal server "speciale":

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

Come puoi vedere, l'eliminazione di tutti i caratteri minuscoli non riesce. MA, ha cancellato la lettera 'o'

La parte interessante sono i seguenti due esempi, che per me non hanno alcun senso:

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

(di nuovo, la 'o' viene eliminata nell'ultimo esempio)

Qualcuno ha idea di cosa sta succedendo qui? Non riesco a riprodurre su nessun altro box Linux che sto usando.


5
Tangenzialmente correlati: gli trintervalli sono scritti senza racchiudere [...]. Così tr -d '[a-z]'ucciderà a-z, e anche personaggi [e ]. Usa tr -d a-zper uccidere solo lettere a-z.
Satō Katsura,

Risposte:


24

hai un file chiamato onella directory corrente

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

la shell espande la [a-z]stringa se viene trovata una corrispondenza.

Questo si chiama espansione del nome percorso, secondo man bash

Espansione del nome percorso
Dopo la divisione delle parole, a meno che non sia stata impostata l'opzione -f, bash esegue la scansione di ogni parola per i caratteri *,? E [. ... (...)

bash eseguirà l'espansione.

[...] Corrisponde a uno qualsiasi dei caratteri racchiusi.


@Chris Puoi controllare l'espansione della shell usando ad esempio echo: touch o ; echo tr -d [a-z]dà questo:tr -d o
pabouk

8

Che cosa sta succedendo

La shell (bash) vede l'argomento [a-z]. Questo è un modello jolly (un glob ), che corrisponde a qualsiasi lettera minuscola¹. Pertanto la shell cerca un nome file che corrisponda a questo modello. Esistono tre casi:

  • Nessun file nella directory corrente ha un nome che è una singola lettera minuscola. Quindi la shell lascia invariato il modello jolly e trvede gli argomenti -de [a-z]. Questo è ciò che accade sulla maggior parte delle tue macchine.
  • Un singolo file nella directory corrente ha un nome che è una singola lettera minuscola. Quindi la shell espande il modello con questo nome di file e trvede gli argomenti -de il nome del file. Questo accade sul server e viene chiamato il file corrispondente opoiché possiamo vedere che ha treliminato la lettera o.
  • Due o più file nella directory corrente hanno un nome che è una singola lettera minuscola. Quindi la shell espande il modello all'elenco dei nomi file corrispondenti e trvede tre o più argomenti: -de i nomi file. Poiché si traspetta un solo argomento dopo -d, si lamenterà.

Cosa avresti dovuto fare

Se nell'argomento di un comando sono presenti caratteri speciali, è necessario evitarli. Inserisci l'argomento tra virgolette singole '…'(questo è il modo più semplice, ce ne sono altri). All'interno delle virgolette singole, tutti i personaggi si distinguono da soli tranne la virgoletta singola stessa. Se nell'argomento è presente una virgoletta singola, sostituirla con'\'' .

tr -d '[a-z]'

Tuttavia nota che probabilmente non è ancora quello che volevi dire! Questo dice trdi cancellare lettere minuscole e parentesi quadre. È equivalente a tr -d ']a-z[', tr '[]a-z'ecc. Per eliminare lettere minuscole, utilizzare

tr -d a-z

L'argomento trè un set di caratteri. Metti le parentesi attorno a un set di caratteri in un'espressione regolare o in un jolly per indicare che si tratta di un set di caratteri. Ma trfunziona su un singolo personaggio alla volta. I suoi argomenti della riga di comando sono quelli che inseriresti tra parentesi .

Sono necessarie parentesi per indicare le classi di caratteri . In un'espressione regolare, si usano parentesi tra parentesi per indicare una classe di caratteri, ad es. [[:lower:]]*Corrisponde a qualsiasi numero di lettere minuscole, [[:lower:]_]*corrisponde a qualsiasi numero di lettere minuscole e caratteri di sottolineatura. Nell'argomento di tr, è necessario l'insieme senza le parentesi quadre circostanti, quindi tr -d '[:lower:]'elimina le lettere minuscole, tr -d '[:lower:]_'elimina lettere minuscole e caratteri di sottolineatura, ecc.

¹ In alcune versioni locali potrebbe corrispondere ad altri caratteri .


1
Si noti che su Solaris 10 (e altri Unix basati antica SysV), si ha bisogno tr -d '[a-z]'di /usr/bin/tr. Con /usr/xpg4/bin/tr, tr -d a-zfunziona ma tr -d '[a-z]'non cancella [].
Stéphane Chazelas,

1
/usr/xpg4/bin/tr -d '[a-z]'non cancellato []apparentemente corretto in Solaris 11.
Stéphane Chazelas,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.