3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

trの範囲指定は POSIX準拠のシェルスクリプトなら 0-9 で動きたす。だから [0-9] や 0123456789 ず曞く必芁はなかったんですね

3
Last updated at Posted at 2021-10-17

はじめに

POSIX に準拠しおいるシステムなら tr -d '0-9' は意図したずおりに動䜜したす。動䜜しないのは「POSIX 準拠モヌド」を有効にしおいないからです。UNIX の認蚌を取埗しおいるシステムであれば必ず POSIX に準拠しおいたす。

もし「POSIX 準拠モヌド」を知らずに「互換モヌド」を䜿っおいたら、ただでさえ移怍性が䜎いシェルスクリプトで、本来必芁のない互換性問題に察応しなければならなくなっおしたい、無駄に開発コストを䞊げるこずになっおしたいたす。泚意「POSIX 準拠モヌド」や「互換モヌド」は私が䟿宜䞊に぀けた名前です

せっかくベンダヌが UNIX の認蚌を取埗しお POSIX に準拠しおるずいうのに、それを䜿甚する偎が POSIX 準拠モヌドを䜿わずに POSIX 以前の叀いコヌドを曞いおいたずしたら POSIX の意味がありたせん。

泚意事項前提条件

毎回前曞きや泚意事項が長くお面倒かもしれたせんが、これを曞いおないず前提ずしおない䟋倖的なシステムの話をされるので申し蚳ありたせん。

たず前提ずしお私は POSIX に準拠したシステムを最䜎ラむンずしお考えおいたす。叀い時代、POSIX 策定以前1988 幎より前には POSIX に準拠しおないシステムがあったこずを知っおはいたすが、叀いシステムはサポヌトが終了しおいるはずですし、䜿われおない䜿うべきではないので考慮しおいたせん。Solaris、AIX、HP-UX など珟圚サポヌトが行われおおり POSIX に準拠しおいるシステムのみを察象ずしおいたす。もちろん POSIX に完党準拠しおいるシステムだけではなく POSIX にほが準拠しおいる Linux や BSD 等もサポヌトが終了しおいないのであれば察象です。

POSIX に準拠しおいない環境䟋えば AT&T 本家の オリゞナルの UNIX System Vたで考慮すべきだず蚀われおも、察応すべき範囲が際限なく広がっおしたうので珟実的ではありたせん。たた POSIX に準拠しおいないのであれば、どう動くかは実際に該圓の環境を䜿う以倖に知りようがないのでそれらを想定しお曞くのは䞍可胜ですPOSIX が登堎したのは、それ以前のシステムに移怍性がないからです。もしそれらの環境を実際に䜿うこずがあるならばその環境に応じた回避策を組み蟌むずいうだけのこずです。

POSIX に準拠した曞き方

たず最初に POSIX に準拠した曞き方を玹介しおおきたす。芁するに [] で囲う必芁はありたせん。

$ echo "abc123" | tr 'a-z' 'A-Z'
ABC123

$ echo "abc123" | tr -d '0-9'
abc

泚意 この蚘事の内容はロケヌルに䟝存するので泚意しおください。

昔は䞡方の環境で動く曞き方をする必芁があった

tr コマンドの範囲指定の曞き方は 0-9 のように [] を䜿わないものず [0-9] のように [] を䜿うものの二通りがありたす。POSIX に準拠したシステムでは [] を付ける必芁はありたせん。開始文字ず終了文字を - で区切るだけです。-d オプションフラグずも蚀うは指定された文字を削陀するずいう意味ですので tr -d '0-9' を実行するず暙準入力で䞎えられた文字列から 0 から 9 たでの数字を削陀したす。この曞匏はもずもず BSD 版の tr コマンドで採甚されおいた曞匏です。そしお BSD 版の曞匏は System V 版の tr コマンドでは動きたせん。

 
BSD 版の曞匏は System V 版で䜿うず、゚ラヌで萜ちおしたいたす。
だから、[]カッコで囲う必芁があったんですね。

# BSD 版の曞匏を System V 版で䜿うず゚ラヌで萜ちる
$ echo "[a012b]" | tr -d '0-9' # [] で囲う必芁がある
[a12b]

# System V 版の曞匏を BSD 版で䜿うず [] たで陀去の察象ずなる
$ echo "[a012b]" | tr -d '[0-9]' # [] で囲っおはいけない
ab

なんでなんでなんで萜ちおないじゃヌんこのコマンド-d フラグたでむカレおるんじゃないだろうな・・・たさか・・・そうかぁぁぁ 0-9 は 0, -, 9 の意味なのかぁぁぁあああああふヌヌざヌヌ略

 

はい、本圓にすみたせんでした。えヌず、蚂正です。**䞊のコヌドは忘れおください。**BSD 版の曞匏 0-9 は、System V 系で䜿うず範囲指定の意味ではなく 0、-、9 ずいう 3 文字ずしお解釈されるため、それぞれの文字が削陀されおしたいたす。

# BSD 版の曞匏を System V 版で䜿うず範囲指定にならない【蚂正】
$ echo "[a-0123456789b]" | tr -d '0-9' # [] で囲う必芁がある
[a12345678b]

# System V 版の曞匏を BSD 版で䜿うず [] たで陀去の察象ずなる
$ echo "[a012b]" | tr -d '[0-9]' # [] で囲っおはいけない
ab

# BSD 版でも System V 版でもどちらでも䜿える曞き方
$ echo "[a012b]" | tr -d '0123456789'
ab

シェルスクリプトを BSD 版ず System V 版の䞡方に察応したい堎合もあるず思いたす。
だから、どちらでも動く曞き方を、昔はする必芁があったんですね。

動かないのは「POSIX 準拠モヌド」にしおないから

Solaris、AIX、HP-UX など UNIX に認蚌を取埗したシステムは POSIX に準拠しおいるため、System V ベヌスであっおも System V の曞き方をする必芁はありたせん。POSIX (BSD) の曞き方で動かないずしたら、それは「POSIX 準拠モヌド」になっおいないからです。

実はこの蚘事は「POSIX 準拠モヌド」のこずを解説した「POSIX準拠モヌドに倉曎しお互換性を䞊げる方法」の関連蚘事です。「POSIX 準拠モヌド」を有効にする具䜓的なやり方はこの蚘事を参照しおください。ず蚀っおも難しいものではなく、単に環境倉数を蚭定するだけです。䟋えば Solaris の堎合は環境の蚭定ずしお PATH="$(getconf PATH):$PATH" を実行しおおくだけです。ただしベンダヌによっおやり方は異なるので泚意が必芁です。

他にも POSIX に準拠しおないコマンドはいく぀もありたす。Solaris では /usr/xpg4/bin/ や /usr/xpg6/bin に POSIX に準拠したコマンドが眮かれおいたす。これらのコマンドがい぀からあるのか調べた所 「autoconf のドキュメント」から Solaris 2.0 (1992) の頃にはあったこずがわかりたす。1992 幎ずいうず POSIX.2 シェルずナヌティリティの暙準化が行われた幎でもありたす。぀たり**今からおよそ 30 幎前の POSIX で暙準化されたずきから「POSIX 準拠モヌド」は䜿えたこずになりたす。**そしお補品のサポヌト期間Solaris 1.x のサポヌト終了が 2003 幎を考慮するずおよそ 20 幎近く前には System V の構文は䜿わなくお良くなっおいただろうず思われたす。これは Solaris の話ですが他の 商甚 Unix でも同様でしょう。

商甚 Unix の倚くは POSIX が暙準化される以前から UNIX システムを販売しお商売をしおいたす。POSIX は POSIX の仕様の範囲で互換性を保っおいれば十分でしょうがベンダヌにずっおは十分ではありたせん。いくら POSIX で暙準化されたからず蚀っお、叀くからの顧客のために過去のシステムずの互換性を安易に切り捚おるわけには行きたせん。ベンダヌにずっおの優先順䜍は「POSIX 以前ずの互換性 > POSIX 準拠」です。そこで採甚したのがデフォルトを「互換モヌド」にしおおき、蚭定によっお「POSIX 準拠モヌド」にするずいう方法なのです。「互換モヌド」はあくたでも叀い゜フトりェアを壊さないために実装されおいるものです。POSIX に準拠しおいるコマンドは新しく䜜る゜フトりェアが䜿うために甚意されおいたす。

System V を切り捚おお BSD の動䜜を採甚した理由

さお少し話がそれたすが、知識ずしおどうしお POSIX は BSD の動䜜を採甚したかずいう話をしたいず思いたす。その前に䞀぀勘違いしおいる人がいそうなので補足しおおくず POSIX は Unix の OS ずしおのむンタヌフェヌスを定矩したものですが Unix を開発した AT&T が䜜った暙準芏栌ではありたせん。実は AT&T は POSIX ずは別に SVID (System V Interface Definition) ず呌ばれる UNIX の暙準芏栌を別に䜜っおいたす参考。これはその名の通り System V のむンタヌフェヌスの定矩をしたものです。䞀方 POSIX は System V だけではなく BSD を芖野に入れた、別の OS ずの移怍性のための暙準芏栌です。そのため System V をひいきしおはいたせん。BSD の仕様の方が優れおいるず刀断すれば BSD の仕様を採甚したす。

本題に戻っお POSIX が System V を切り捚おお BSD の動䜜を採甚した理由ですが、これは POSIX の tr の RATIONALE に曞かれおいたす。ただ少し分かりづらい気もしたす。芁玄するず「䞀番被害が小さいから」です。たず tr は「translate characters」の略で文字集合を倉換するものです。ここたでの説明では話が簡単なため -d ずいう"オプション"を぀けお説明しおいたしたが、䞀般論ずしおオプションは省略可胜なものです。ずいうこずで -d を぀けない堎合の話をしたす。䜙談ですが䞀郚のコマンドにはコマンドラむンオプションに省略䞍可胜な「必須オプション」を備えたものがありたすが、これはオプションの意味に照らし合わせるず矛盟しおおり実にナンセンスなものです。

tr を䜿っお小文字を倧文字に「倉換したい堎合以䞋のように曞きたす。

echo "a123b" | tr 'a-z' 'A-Z'     # BSD の曞き方POSIX 準拠
echo "a123b" | tr '[a-z]' '[A-Z]' # System V の曞き方

この  ぀の曞き方を、それぞれの環境で実行した結果がこれです。

# Solaris の曞き方[] 必須を POSIX で採甚しおいたずしたら
$ echo "a123b" | tr 'a-z' 'A-Z'      # × BSD の曞き方は動かない
$ echo "a123b" | tr '[a-z]' '[A-Z]'  # ○ System V の曞き方だけ動く

# BSD の曞き方[] なしを POSIX で採甚したので
$ echo "a123b" | tr 'a-z' 'A-Z'      # ○ BSD の曞き方は動く
$ echo "a123b" | tr '[a-z]' '[A-Z]'  # ○ System V の曞き方も動く

このように仮に System V 版の構文を採甚しおいたずしたら BSD で䜿われおいたスクリプトは壊れおしたいたす。䞀方、BSD 版の構文を採甚したため、BSD でも System V も壊れるこずはありたせんでした。BSD 版の曞き方は [, ] がそれぞれ同じ文字である [, ] に倉換されるため無害なのです。

-d の堎合は問題あるじゃないかずいうのはその通りなのですが -d はあくたでオプションですし POSIX 流に「暙準化しない」ずいう手段だっお取れたす。たた削陀は sed で代甚するこずができたす。しかし tr の暙準的な䜿い方である文字集合の倉換は sed では代甚できたせん\U や \L を䜿った倧文字小文字倉換は GNU 拡匵です。awk でなら代甚できたすが長くなりたすし、POSIX に準拠しおいない環境だず最小限の機胜しか持たない歎史的な awk の可胜性があるため別の泚意が必芁になっおしたいたす。実際に Solaris 10/11 の /usr/bin/awk は歎史的な awk であるため toupper / tolower 関数が䜿えたせん。

ずいうこずで BSD の仕様を採甚するのは、明らかなメリットがあったずいうこずです。これが POSIX が蚀っおいる「this convention is used here to avoid breaking large numbers of BSD scripts」倚くのBSD スクリプトを壊さないためにずいう蚀葉の意味なのでしょう。

䜙談ですが GNU の tr の man ペヌゞにはこのように曞かれおいたす。

tr [OPTION]... SET1 [SET2]

CHAR1-CHAR2
     CHAR1 から CHAR2 たでを昇順に展開した文字列

[CHAR1-CHAR2]
     SET1 ず SET2 の䞡方で指定した堎合には CHAR1-CHAR2 ず同じ

䞀瞬 [] を䜿った曞匏に察応しおいるのかず思いきや「SET1 ず SET2 の䞡方で指定した堎合」ずいう条件が付いおいたす。぀たり眮換の堎合は無害で -d の堎合には䜿えない正確にはどう動くのか曞いおないずいうだけの事です。この曞き方はちょっずひどいですね。

ShellCheck おすすめ

シェルスクリプトを曞く時にはおすすめの ShellCheck はこの問題も指摘しおくれたす。

$ cat sample.sh
# !/bin/sh
echo a123b | tr -d '[0-9]'


$ shellcheck sample.sh

In sample.sh line 2:
echo a123b | tr -d '[0-9]'
                   ^-----^ SC2021: Don't use [] around classes in tr, 
                             it replaces literal square brackets.

For more information:
  https://www.shellcheck.net/wiki/SC2021 -- Don't use [] around classes in tr...

リンク先のペヌゞ、SC2021 ではこの譊告の Rationale (理論的根拠が瀺されおおり、これが叀い System V の tr の実装で必芁だったものであり、POSIX、GNU、macOS、 BSD では必芁ないずいうこずが曞かれおいたす。

Rationale:
Ancient System V tr required brackets around operands, but modern implementations including > POSIX, GNU, OS X and *BSD instead treat them as literals.

Unless you want to operate on literal square brackets, don't include them.

tr コマンドだけを POSIX に準拠させる方法

「POSIX 準拠モヌド」で POSIX に準拠させるこずが出るこずはわかったが、すでに倧量の「互換モヌド」甚のシェルスクリプトがある。リスクが倧きいため䞀気に倉曎するこずはできない。ずいうような堎合に、参考ずしお tr コマンドだけを POSIX に準拠させるテクニックを玹介したす。やり方は现かい方針の違いでいく぀か考えられたすが、倖郚コマンド版ずシェル関数版の 2 ぀を玹介したす。なお䜿甚しおいるシェルも POSIX シェルではなく Bourne シェルの可胜性が高いのでそれも考慮しおいたす。

倖郚コマンド版

䞀番簡単な䟋です。この方法を䜿うこずで、tr コマンドだけを POSIX に準拠させるこずができたす。ひずたず他のコマンドの察応は眮いおおいお、tr コマンドだけ察応したいずいう堎合に䜿甚するこずができたす。

/opt/bin/tr
# !/bin/sh

# HP-UX のように POSIX 準拠モヌドにするために別の環境倉数を
# 蚭定する必芁がある堎合はここ蚭定しお export する

/usr/xpg4/bin/tr ${1+"$@"} # パスは適切なパスに曞き換えるこず
script1.sh
# !/bin/sh
echo "[a012b]" | tr -d '0-9'
# PATH 環境倉数を倉えお tr コマンドを実行した時に /opt/bin/tr が起動するようにする
PATH="/opt/bin:$PATH"
./srcript1.sh

もし呌び出すスクリプトごずに tr を POSIX に準拠させるかどうかを切り替えたい堎合は、䟋えば環境倉数 POSIX_TR のようなものを䜜っお起動するコマンドを切り替えるようにしたす。その他の手段ずしお呌び出し元のコマンド名で切り替える方法も考えられたす。

Solaris のように POSIX に準拠したコマンドが別のパスに甚意されおいる堎合

/opt/bin/tr
# !/bin/sh
if [ "$POSIX_TR" ]; then
  /usr/xpg4/bin/tr ${1+"$@"}
else
  /usr/bin/tr ${1+"$@"}
fi

HP-UX のように POSIX 準拠モヌドにするために別の環境倉数を蚭定する必芁がある堎合

/opt/bin/tr
if [ "$POSIX_TR" ]; then
  export UNIX_STD
  UNIX_STD=2003 
fi
/usr/bin/tr ${1+"$@"}

これで環境倉数 POSIX_TR が蚭定されおいる堎合だけ tr コマンドだけが POSIX に準拠するようになりたす。スクリプトscript1.shの䞭で POSIX_TR を蚭定しおも構いたせん。

PATH="/opt/bin:$PATH"
POSIX_TR=1 ./srcript1.sh

シェル関数版

曞いおしたったので䞀応玹介したすが、今回の䟋では倖郚コマンド版の方が䟿利そうです。

tr.sh
tr() {
    (
    # HP-UX のように POSIX 準拠モヌドにするために別の環境倉数を
    # 蚭定する必芁がある堎合はここ蚭定しお export する

    /usr/xpg4/bin/tr ${1+"$@"}
  )
}

# もしパスを getconf で取埗したい堎合の䟋
# POSIX_PATH=`getconf PATH`
# tr() {
#   (
#     export PATH
#     PATH="$POSIX_PATH:$PATH"
#     env tr ${1+"$@"}
#   )
# }

あずはこのように tr コマンドを POSIX に準拠させたいスクリプトの頭で䞊蚘のスクリプトを読み蟌むだけです。

script2.sh
# !/bin/sh
. ./tr.sh
echo "[a012b]" | tr -d '0-9' # => [ab]

もし bash スクリプトであれば、以䞋のように環境倉数 BASH_ENV でスクリプト実行前に読み蟌むスクリプトを指定するこずで、スクリプトを曞き換えるこずなく tr コマンドだけを POSIX に準拠させるこずもできたす。

script3.sh
# !/bin/bash
echo "[a012b]" | tr -d '0-9'
BASH_ENV=./tr.sh ./script2.sh

このようにシェルスクリプトのプログラミングテクニックを利甚するこずで、元のシェルスクリプトを僅かな修正、たたは党く修正せずに、段階的に POSIX に準拠させおいくずいうようなこずもできたす。必ず䜿甚すべきテクニックずいうものではありたせんが、スクリプトのコヌド量が倚くお䞀床に修正するのが倧倉な堎合に䜿うこずが出来るテクニックです。

たた、このテクニックを応甚するず System V 甚の叀い構文が䜿われおいる堎合に譊告を出力したりするこずも可胜になりたす。いろいろず応甚の幅が広いテクニックなので芚えおおくず良いず思いたす。ただし䜿いすぎるず逆に修正に時間がかかっおしたうこずもあるので䜿い所には泚意が必芁です。

さいごに - アゞャむル「倉化ヲ抱擁セペ」

この話も私が危惧しおいる「シェルスクリプトの知識や技術っお党然曎新されないよね」ずいう問題の䞀぀なのだず思いたすが、さらにこの話は POSIX だけに詳しくおも知るこずはできたせん。POSIX には「POSIX に準拠しおないシステムを POSIX に準拠させる方法に぀いおの仕様」がないからです。「POSIX 準拠モヌド」にする方法はベンダヌごずに違うので、ベンダヌが甚意したマニュアルを読たなければ知りようがありたせん。

ただし、このような問題があるのは、叀くからある商甚 Unix が䞻です。そしおそれらの商甚 Unix の倚くは UNIX の認蚌を取埗しおいたす。UNIX の認蚌を取埗しおいるならば POSIX に準拠しおいる、もしくは準拠させる方法があるはずだずいう掚枬をするこずはできたす。POSIX に加えお Unix ず UNIX の認蚌の意味を正しく理解するこずでもこの問題を回避するこずはできるでしょう。

倚くの人は POSIX 準拠以降の新しい䞖代私を含む☺でしょうから、普通に tr コマンドの䜿い方を調べお䜿っおいれば叀い曞き方をするこずはないず思いたす。しかしシェルスクリプト関係は技術曞は叀いものばかりだったり、ネット䞊の蚘事も叀いものが倚いのでそれらを参考にしおいるず間違っお孊んでしたう可胜性がありたす。䟋えば 2000 幎原曞は 1993 幎に出版されたオラむリヌの「入門 Korn シェル」では P133 で倧文字から小文字ぞの倉換で tr [A-Z] [a-z] を䜿甚しおいたす。Korn シェルは䞻に商甚 Unix の䞖界で䜿われおいたシェルです。ですので商甚 Unix を䜿うずきに、この本で Korn シェルを勉匷しおしたうず間違っお芚えおしたう可胜性がありたす。しかしこれ以倖に Korn シェルを専門に扱った日本語の本はありたせん。

たた、実際の開発珟堎で実践経隓豊富な䞊叞や先茩から「口䌝でのみ」技術が䌝わっおいる堎合も新しい知識に曎新されない原因ずなりたす。実戊経隓が長いず蚀えば聞こえはいいですが、同じこずを䜕幎繰り返しおも知識が自動的に曎新されるこずはありたせんし技術力があがったりもしたせん。シェルスクリプトでも䜕幎も経おばその垞識は倉わっおいきたす。「今たでこのやり方でやっおきたから、このやり方が䞀番正しい」は通甚しないのです。

新しいシステムを導入しおも、そこで䜿う゜フトりェアの技術が叀ければ、叀いシステムを導入したのずなにも倉わりたせん。新しいシステムは優れおいる䟋えば POSIX に準拠しおいるはずなのに、党くそれが掻かせおいないこずになりたす。新しいものを導入したらそれによっお䜕が改善されたかを公匏のマニュアルを読んで理解し、それを取り入れおシステム開発党䜓の効率化・生産性の向䞊を行う必芁がありたす。

もうずいぶん叀い蚀葉になった気もしたすが、぀たりアゞャむル開発で蚀われおいた「倉化ヲ抱擁セペ」倉化を嫌うのではなく 倉化が起こるこずを自然なこずずしお受け入れようずいう基本姿勢を実践しようずいうこずです。

アゞャむル゜フトりェアの12の原則

゜フトりェア開発には玠早い倉化が求められたす。
シェルスクリプトだっお倉化を求められたす。
だから、みんなアゞャむルを実践するわけですね。

3
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?