それマグで!

知識はカップより、マグでゆっくり頂きます。 takuya_1stのブログ

習慣に早くから配慮した者は、 おそらく人生の実りも大きい。

bashの似てて紛らわしいもの() / {} について

サブシェルとグループの違い

  • サブシェル ()
  • グループ {}

サブシェルの大きな特徴は、サブシェルであるということ

サブシェルは、ほぼfork みたいなものだと思ったらいいと思います。

() / { } の違い

  • () はサブシェルを起動し実行する。
  • {} は現在のシェルで実行する。

現在のシェルの中でソースコードを実行するのか、フォークしてフォーク先でソースコードを実行するのかが一番大きな違い。この特徴があるので環境変数の保護や、作業ディレクトリの保護など変数にまつわることを隠蔽できるのが特徴になる。

目次

サブシェルとしての実験

サブシェルとしての特徴を見ていく実験をしておく。

サブシェルが、プロセスを生やしていることを調べる。

コマンド

( sleep 20; )

実行中のプロセス

takuya@Desktop$ pstree  62776
-+= 62776 takuya -bash
 \-+= 63989 takuya -bash
   \--- 63990 takuya sleep 20

コレでわかるとおりです。

bash から bash が枝が生えて、枝bash からsleep が生えています。

グループの場合のプロセスツリー

グループで起動した、この場合のプロセスツリーはどうでしょうか

コマンド

 { sleep 20 ; }

実行プロセス

-+= 64457 takuya -bash
 \--= 64717 takuya sleep 20

グループでは、sleepを直接起動しています。これは

> sleep 20 ;

としているときと全く同じです。

サブシェル時の変数

サブシェルを使う場合、変数は受け継がれる。ただし変更は、戻されない。

これは起動時に変数を引継ぐが、起動後は別のプロセスになるので、変数への更新は反映されない。

takuya@Desktop$ a=1 ; ( echo $a; a=1234; echo $a;   ) ; echo $a   ;
1       # 元シェル $a
1234 # サブシェル内 $a
1      #  元シェル $a

リダイレクトについて

リダイレクトをマトメられる特徴についても見ておく。

() サブシェルと { } グループの 特徴として リダイレクトをマトメられる。

サブシェルはサブシェルとして一つのグループになっているので、リダイレクトはマトメられる

echo 1234 > out.txt
echo 5678 > out.txt
echo 9012 > out.txt

などとするときに面倒だし、() / {} で、マトメて一括リダイレクト指定して、楽に書くことが出来る。

( ) でのリダイレクト

(
echo 1234
echo 5678
echo 9012
) > out.txt

{} でのリダイレクト

またはグループを使うと。

{
echo 1234
echo 5678
echo 9012
} > out.txt

リダイレクトをマトメられるのでちょっと便利に使えるよね。

グループでよくハマること:Syntax Error

グループではまって面食らって「良くわからん」と使うのを断念しちゃう原因にシンタックスエラーがある。

{ } の場合:シンタックスエラー

takuya@Desktop$ { echo 1234; }
1234
takuya@Desktop$ {echo 1234; }
-bash: 予期しないトークン `}' 周辺に構文エラーがあります

開始の{ の直後に文字をつ続けてしまうと、制御構造として解釈されない

ところが、サブシェルの場合はシンタックスエラーにならない (& | ;なとと同等の優先度であるためだと思う。

( ) の場合:シンタックスOK

takuya@Desktop$ (echo 1234; )
1234

この辺も知らないと戸惑う所。

{} / () は関数の定義に使える

理由は良くわからんが、どっちも関数定義に使える。

{} で関数の定義

takuya@Desktop$ func_a () ( echo 1 ;  ); func_a ;
1

( ) で関数の定義!?

takuya@Desktop$ func_a () { echo 1 ;  }; func_a ;
1

気持ち悪さがある。ただ サブシェルが変数をサブシェル内にとどめてくれるので、とても楽にローカル変数を扱う、関数を作ることが出来る

つまり、作業ディレクトリ(wd) を覚えておく、元いた場所に戻すなど、ああいう面倒な作業が一切いらなくなる。

{ } での場合の変数の状況

takuya@Desktop$ pwd ; func_a () { cd ~; pwd  ;    }; func_a; pwd ;
/Users/takuya/Desktop
/Users/takuya
/Users/takuya

( ) での場合の変数の状況

takuya@Desktop$ pwd ; func_a () (  cd ~; pwd  ;    ); func_a; pwd ;
/Users/takuya/Desktop
/Users/takuya
/Users/takuya/Desktop

サブシェルを使う点において同じになるので、少し知ってると便利かもしれない。

まとめ { }( ) の違いはサブシェルを経由するかどうか

サブシェルを経由して、プロセスを生やすことを除き、殆どすべて同じであると思う。

サブシェルの特徴として次のとおりです。

サブシェルとグループに共通する特徴

  • 名前をつけて関数に出来る
  • リダイレクトも纏めてできる。
  • 環境変数は引継がれる。
  • 引継がれる主な変数
    • set / shopt
    • export 済み
    • trap
    • cd / pushd / popd

個人的には () は fork だと思うことにしてる。

今回の一番の驚愕ポイント!!

サブシェルを関数に出来たこと。

myFunc () ( echo aaaaa ) ;  myFunc;

関連記事インデックス

bashの使い方のまとめ記事のインデックス - それマグで!

参考資料

[改訂新版] シェルスクリプト基本リファレンス  ??#!/bin/shで、ここまでできる (WEB+DB PRESS plus)

[改訂新版] シェルスクリプト基本リファレンス  ??#!/bin/shで、ここまでできる (WEB+DB PRESS plus)