web-dev-qa-db-ja.com

範囲を使用したtrの奇妙な動作

Trを使用するときに奇妙な動作をする特定のサーバーがあります。以下は、稼働中のサーバーの例です。

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

それは私には完全に理にかなっています。

ただし、これは「特別な」サーバーからのものです。

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

ご覧のように、すべての小文字の削除は失敗します。しかし、それは文字「o」を削除しました

興味深いのは、次の2つの例ですが、これらはまったく意味がありません。

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

(ここでも、最後の例では「o」が削除されています)

ここで何が行われているのか誰か誰か知っていますか?私が使用している他のLinuxボックスでは再現できません。

10
Chris

現在のディレクトリにoという名前のファイルがあります

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

一致が見つかった場合、シェルは[a-z]文字列を展開します。

man bashによれば、これはパス名展開と呼ばれます

パス名展開
Word分割後、-fオプションが設定されていない場合、bashは各Wordで*、?、および[の文字をスキャンします。 ...(...)

bashは拡張を実行します。

[...]囲まれた文字のいずれかに一致します。

24
Archemar

何が起こっている

シェル(bash)は引数[a-z]を参照します。これはワイルドカードパターン(a glob )で、すべての小文字に一致します¹。したがって、シェルはこのパターンに一致するファイル名を探します。 3つのケースがあります。

  • 現在のディレクトリのファイルには、1つの小文字の名前はありません。次に、シェルはワイルドカードパターンを変更せずに残し、trは引数-dおよび[a-z]を参照します。これがほとんどのマシンで起こります。
  • 現在のディレクトリ内の1つのファイルには、1つの小文字の名前が付けられています。次に、シェルはパターンをこのファイル名に展開し、trは引数-dとファイル名を参照します。これはサーバーで発生し、oが文字trを削除したことがわかるため、一致するファイルはoと呼ばれます。
  • 現在のディレクトリにある2つ以上のファイルに、1つの小文字の名前が付けられています。次に、シェルはパターンを一致するファイル名のリストに展開し、trは3つ以上の引数-dとファイル名を参照します。 tr-dの後に単一の引数を期待するため、文句を言うでしょう。

すべきこと

コマンドの引数に特殊文字がある場合は、それらをエスケープする必要があります。引数を一重引用符'…'で囲みます(これが最も簡単な方法です。他にもあります)。一重引用符内では、一重引用符自体を除いて、すべての文字が自分自身を表します。引数内に単一引用符がある場合は、 置換文字列を'\'' に置き換えます。

tr -d '[a-z]'

ただし、これはおそらくあなたが意図したものではないことに注意してください!これは、小文字と角かっこを削除するようにtrに指示します。 tr -d ']a-z['tr '[]a-z'などと同等です。小文字を削除するには、次を使用します。

tr -d a-z

trの引数は文字セットです。正規表現またはワイルドカードパターンで文字セットを角括弧で囲んで、文字セットであることを示します。ただし、trは一度に1つの文字に対してのみ機能します。そのコマンドライン引数は、大括弧内に入力するものです

文字クラスを示すには括弧が必要です。正規表現では、大括弧内の大括弧を使用して、文字クラスを示します。 [[:lower:]]*は任意の数の小文字と一致し、[[:lower:]_]*は任意の数の小文字とアンダースコアと一致します。 trの引数では、セットを括弧で囲む必要がないため、tr -d '[:lower:]'は小文字を削除し、tr -d '[:lower:]_'は小文字とアンダースコアを削除します。

¹ 一部のロケールでは、他の文字と一致する場合があります