運用環境 シェル(その11)

【連載】

にわか管理者のためのLinux運用入門

【第191回】運用環境 シェル(その11)

[2019/08/13 07:00]後藤大地 ブックマーク ブックマーク

サーバ/ストレージ

展開とホワイトスペース

前回はパラメータ展開、コマンド置換、算術展開について取り上げた。これら展開処理が行われた結果には複数の空白(ホワイトスペース)が含まれていることがあると思うが、結果が「”(ダブルクォーテーション)」で囲まれている場合と囲まれていない場合とで、そのホワイトスペース周りの挙動が異なってくる。

シェルはパラメータ展開、コマンド置換、算術展開などを実施したあと、その展開後の結果をパースする。ダブルクォーテーションで囲まれていれば全体を文字列として認識してホワイトスペースについての処理は行われないが、ダブルクォーテーションで囲まれていないのであれば、シェルは結果をパースして、次のように処理を行う。

  • 変数IFSに設定されている文字(デフォルトはスペース、タブ、改行、が設定されている)については、展開結果の先頭と末尾にあるものは削除する
  • IFSに含まれている文字が区切り文字として認識され、それ以外の文字はIFSに含まれている文字で分割される


IFSは区切り文字を指定する特別な変数で、デフォルトでは空白、タブ、改行が設定されている。IFSはシェルの展開処理やパース処理における区切り文字を指定するものであり、動作に影響を与えるものとなっている。

ダブルクォーテーションのあり/なしで動作が変わる様子は、次のサンプルを見てもらうのがわかりやすいだろう。

# cal 9 2020
      9月 2020         
日 月 火 水 木 金 土  
       1  2  3  4  5  
 6  7  8  9 10 11 12  
13 14 15 16 17 18 19  
20 21 22 23 24 25 26  
27 28 29 30           

# echo "$(cal 9 2020)"
      9月 2020         
日 月 火 水 木 金 土  
       1  2  3  4  5  
 6  7  8  9 10 11 12  
13 14 15 16 17 18 19  
20 21 22 23 24 25 26  
27 28 29 30           

# echo $(cal 9 2020)
9月 2020 日 月 火 水 木 金 土 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
#

「cal」はカレンダーを出力するコマンドだ。このコマンドをコマンド置換で実行した結果を、ダブルクォーテーションで囲っておくと、calコマンドを単体で実行したときと同じ結果を得ることができる。出力結果がそのまま保持されるからだ。

しかし、ダブルクォーテーションを指定しないと、カレンダー出力における空白と改行が区切り文字として認識され、それを除いて個別に分割したものを空白で区切ったものがデータとして整理される。結果として、1行にまとまったデータが出力されていることがわかると思う。

ちなみに、IFSを明示的に変更するような利用シーンがあるのかと言われると、「なくもないが、別にIFSを変更してシェルで処理するべきとも言えない」といったところではないかと思う。例えば、次のようにIFSを変更すれば、シェルのホワイトスペースを使ったパース機能で「:(コロン)」区切りのデータを切り分けることができる。

# tail /etc/passwd
nobody:*:65534:65534:Unprivileged user:/nonexistent:/usr/sbin/nologin
daichi:*:501:20:Daichi GOTO:/Users/daichi:/usr/local/bin/fish
_tss:*:601:601:TrouSerS user:/var/empty:/usr/sbin/nologin
cups:*:193:193:Cups Owner:/nonexistent:/usr/sbin/nologin
git_daemon:*:964:964:git daemon:/nonexistent:/usr/sbin/nologin
messagebus:*:556:556:D-BUS Daemon User:/nonexistent:/usr/sbin/nologin
avahi:*:558:558:Avahi Daemon User:/nonexistent:/usr/sbin/nologin
tests:*:977:65534:Unprivileged user for tests:/nonexistent:/usr/sbin/nologin
polkitd:*:565:565:Polkit Daemon User:/var/empty:/usr/sbin/nologin
sasaki:*:1001:1001:Yoshifumi SASAKI:/Users/sasaki:/bin/sh
# T=$IFS
# IFS=:
# tail /etc/passwd |
> while read user passwd id others
> do
>    echo $user $id
> done
nobody 65534
daichi 501
_tss 601
cups 193
git_daemon 964
messagebus 556
avahi 558
tests 977
polkitd 565
sasaki 1001
# IFS=$T
#

しかし、正直なところこの書き方はわかりにくい。同じことをするならawkやsedを使ったほうがわかりやすいと思うし、無理にシェルで処理する必要はないように見える。

シェルパターン

IFSを使ったホワイトスペース処理が行われた後はパス名展開が行われるのだが、その前にパターンについて説明しておく必要がある。シェルはパス名やcase制御構文、パラメータ展開などでパターンを指定することができる。シェルで使用できるパターンは次の通りだ。

パターン 内容
* 任意の文字列に一致
? 任意の1文字に一致
[文字列] 指定された文字列のうちのどれか1文字に一致。]を含める場合には[の直後に]を指定する
[!文字列] 指定された文字列のうちのどの文字にも一致しない
[^文字列] [!文字列]と同じ

シェルのパターンはとてもシンプルなものだ。正規表現ほど複雑な指定をすることはできない。以下に、シェルパターンの簡単な使用例を示しておく。

# ls
commands    tables      typescript.txt  typescript.yd
Makefile    typescript.html typescript.xml
# ls -l
total 48
drwxr-xr-x  2 daichi  staff   512  8月  7 18:04 commands
-rw-r--r--  1 daichi  staff   565  8月  7 17:04 Makefile
drwxr-xr-x  2 daichi  staff   512  8月  7 17:56 tables
-rw-r--r--  1 daichi  staff  9604  8月  7 18:07 typescript.html
-rw-r--r--  1 daichi  staff  7425  8月  7 18:07 typescript.txt
-rw-r--r--  1 daichi  staff  7071  8月  7 18:07 typescript.xml
-rw-r--r--  1 daichi  staff  7512  8月  7 18:07 typescript.yd
# echo typescript.*
typescript.html typescript.txt typescript.xml typescript.yd
# echo .*
. .. .build.pid
# echo ./ty*
./typescript.html ./typescript.txt ./typescript.xml ./typescript.yd
#

シェルパターンはコマンドが処理する正規表現と混乱しやすい。どの段階ではシェルがパターン展開を行い、どの段階でコマンドが正規表現を処理しているのか、その辺りをちゃんと区別しておくことが大切になる。

パス名展開

先述の通り、IFSに基づくホワイトスペース処理が行われた後にはパス名の展開が実施される。パス名の展開は、パターンの一致する文字列に展開することで行われるのだが、次のルールがある。

  • パターン展開ではパターン部分はスラッシュを含む文字列には展開されない
  • パターンの最初の文字がピリオドになっていない限り、先頭がピリオドで始まるパス名には展開されない


パス名展開はディレクトリやファイル名ごとに発生し、スラッシュを超えてパターンが一致することはないし、明示的に指定しない限り隠しファイルや隠しディレクトリに展開されることもない。

展開順序

ここまで展開処理について理解すると、それぞれの展開がどのような優先順位で行われるかがわかるはずだ。

  1. チルダ展開、パラメータ展開、コマンド展開、算術展開
  2. 上記展開処理で発生した文字列に対してIFS変数を利用したフィールド分割
  3. パス名展開
  4. クォートを削除


シェルの操作はそれほど難しいものではない。ただし、実際には上記のように処理の順番が厳密に決まっており、シェルはそのルールに従って処理を行っていることを覚えておいてほしい。

シェルの仕組みとして説明すべきことは、これでほとんど取り上げたことになる。慣れれば考えることなくシェルを使うようになると思うが、時にはこうやってシェルがどのように処理を進めているのかに思いをはせてみるとよいだろう。特にシェルスクリプトやワンライナーの作成などで行き詰まったときは、こうして基礎に戻って見直すとすらっと問題がとけることもある。

参考資料

※ 本記事は掲載時点の情報であり、最新のものとは異なる場合がございます。予めご了承ください。

一覧はこちら

連載目次

もっと知りたい!こちらもオススメ

なぜ今、統合システムなのか? 押さえておくべき「3つのインパクト」

なぜ今、統合システムなのか? 押さえておくべき「3つのインパクト」

ガートナー ジャパンは10月31日~11月2日、都内で「Gartner Symposium/ITxpo 2017」を開催。11月1日には同社 主席アナリストの青山浩子氏が登壇し「CIOが理解すべき統合システムの3大インパクト」と題する講演を行った。本稿では、講演の内容をダイジェストでお届けする。

関連リンク

この記事に興味を持ったら"いいね!"を Click
Facebook で IT Search+ の人気記事をお届けします

会員登録(無料)

注目の特集/連載
[解説動画] Googleアナリティクス分析&活用講座 - Webサイト改善の正しい考え方
知りたい! カナコさん 皆で話そうAIのコト
教えてカナコさん! これならわかるAI入門
対話システムをつくろう! Python超入門
Kubernetes入門
AWSで作るクラウドネイティブアプリケーションの基本
ソフトウェア開発自動化入門
PowerShell Core入門
徹底研究! ハイブリッドクラウド
マイナビニュース スペシャルセミナー 講演レポート/当日講演資料 まとめ
セキュリティアワード特設ページ

一覧はこちら

今注目のIT用語の意味を事典でチェック!

一覧はこちら

ページの先頭に戻る