シェルスクリプトの基礎 - 変数

提供:MochiuWiki : SUSE, EC, PCB
ナビゲーションに移動 検索に移動

概要



変数の定義と参照

変数へ値を代入する時は$を付けず、変数を参照するときは$を付ける。

 #!/bin/sh
 
 name=Michael        # 変数の定義
 echo Hello $name    # 変数の参照
 echo "Hello $name"  # ダブルクォーテーションは変数を展開する
 echo 'Hello $name'  # シングルクォーテーションは変数を展開しない


# 出力
Hello Michael
Hello Michael
Hello $name


未定義の変数を参照すると、空文字列と同様に扱われる。(エラーにはならない)

 #!/bin/sh
 
 echo "Hello $name !"


# 出力
Hello  !


変数の内容の一部を取得する場合は、${変数名:開始位置:長さ}と指定する。
なお、先頭の開始位置は0である。

指定した文字列までを削除する場合、${変数名#文字列}と指定する。
文字列を後ろから削除する場合、${変数名%文字列}と指定する。
また、文字列の指定には、ワイルドカード*が使用できる。

以下の例では、(1)環境変数LANGの0文字目から5文字分のみ取得、(2).(ドット)文字までを削除、(3).(ドット)文字以降を削除を行っている。
なお、操作を指定せず${変数名}とする場合、$変数名と同義になる。

 #! /bin/bash
 
 echo "$LANG"		# 環境変数LANGの内容を表示
 echo "${LANG:0:5}"	# (1) LANGの0文字目から5文字分のみ表示
 echo "${LANG#*.}"	# (2) .文字までを削除
 echo "${LANG%.*}"	# (3) .文字以降を削除


# 出力
ja_JP.utf8     # 環境変数LANGの内容
ja_JP          # ${LANG:0:5}の結果
utf8           # ${LANG#*.}の結果
ja_JP          # ${LANG%.*}の結果


下表に、主な変数の操作を示す。

指定方法 操作内容
${変数名:開始位置:長さ} 開始位置から指定した長さ分の文字列を切り出す
${変数名#パターン}
${変数名##パターン}
パターン部分(前方一致)を削除する。
#の場合は最短一致、##の場合は最長一致となる。
${変数名%文字列}
${変数名%%文字列}
パターン部分(後方一致)を削除する。
%の場合は最短一致、%%の場合は最長一致となる。
${変数名:-文字列} 変数の内容を取得する。
変数がセットされていない場合、指定した文字列を返す。
${変数名:=文字列} 変数の内容を取得する。
変数がセットされていない場合、指定した文字列を変数に代入して返す。
${#変数名} 変数の文字数を取得する。(配列の場合は要素数)


下表に、その他の文字列の操作を示す。

指定方法 操作内容
${@:2} 2つ目以降の引数を取得する。
${変数名:-文字列} "変数名"が未定義またはNULLの場合、"文字列"が代入される。
${変数名:=文字列} "変数名"が未定義またはNULLの場合、"文字列"が代入される。
${変数名:?文字列} "変数名"が未定義またはNULLの場合、"文字列"とエラーを出力して終了する。
${変数名:+文字列} "変数名"が未定義またはNULLの場合はNULLを代入して、それ以外の場合は"文字列"を代入する。
${変数名%文字列} 最短の接尾語を取り除く。
例えば、拡張子名を削除する時に使用する。
${変数名%%文字列} 最長の接尾語を取り除く。
${変数名#文字列} 最短の接頭語を取り除く。
例えば、ディレクトリのパス名から文字列$HOMEを削除する時に使用する。
${変数名##文字列} 最長の接頭語を取り除く。
例えば、ディレクトリのパス名から任意の文字列を削除する時に使用する。
$'文字列' Bashのみ使用可能。
例えば、echo 'a\te';と記述する場合はa\teと出力されるが、
echo $'a\te'と記述する場合はa eと出力される。
\UhhhhhhhhとUnicode(ISO/IEC 10646 )がある。


Shell Parameter Expansion(:)の有無により、変数が定義されているかどうか、および、値がNULLかどうかで動作が変わる。

指定方法 変数が定義されており値がNULLではない場合 変数が定義されており値がNULLの場合 変数が未定義の場合
${変数名:-文字列} 既存の値を返す。 "文字列"を返す。 "文字列"を返す。
${変数名-文字列} 既存の値を返す。 NULLを返す。 "文字列"を返す。
${変数名:=文字列} 既存の値を返す。 "文字列"を代入する。 "文字列"を代入する。
${変数名=文字列} 既存の値を返す。 NULLを返す。 "文字列"を代入する。
${変数名:?文字列} 既存の値を返す。 エラーを出力して終了する。 エラーを出力して終了する。
${変数名?文字列} 既存の値を返す。 NULLを返す。 エラーを出力して終了する。
${変数名:+文字列} "文字列"を返す。 NULLを返す。 NULLを返す。
${変数名+文字列} "文字列"を返す。 "文字列"を返す。 NULLを返す。


以下に、接尾語および接頭語を示す。

 #!/usr/bin/env bash
 
 hoge="${HOME}/hoge/bake/file.c"
 
 echo 'original'
 echo $hoge
 echo ''
 
 echo '${parameter%word}'
 echo ${hoge%.c}.o
 echo ''
 
 echo '${parameter%%word}'
 echo ${hoge%%file.c}new_file.o
 echo ''
 
 echo '${parameter#word}'
 echo ${hoge#$HOME}
 echo ''
 
 echo '${parameter##word}'
 echo ${hoge##*/}


# 出力
original
/Users/yasui/hoge/bake/file.c

${parameter%word}
/Users/yasui/hoge/bake/file.o

${parameter%%word}
/Users/yasui/hoge/bake/new_file.o

${parameter#word}
/hoge/bake/file.c

${parameter##word}
file.c



定数

以下のように実行すると、指定した変数や関数に対する代入、unsetができなくなる。

 readonly var      # 変数varをreadonlyにする
 readonly -a arr   # 配列arrをreadonlyにする
 readonly -f func  # 関数funcをreadonlyにする


以下のように、変数の定義と同時にreadonlyを指定することも可能である。

 readonly USERNAME="Mike"


以下の例では、変数MAX_SIZEを100で初期化した後、200という値を代入しようとしている。
変数MAX_SIZEは定数化されているので、代入しようとした場合はエラーになる。

 #!/bin/bash
 
 readonly MAX_SIZE=100
 MAX_SIZE=200


# 出力
./sample.sh: 行 4: MAX_SIZE: 読み取り専用の変数です



変数の値の確認

test[ ... ]で使用できる演算子には、以下のようなものがある。

  • -z 文字列
    文字列の長さが0の場合に真
  • -n 文字列
    文字列の長さが0でない場合に真


変数名にダブルクォーテーションを付けて値を展開して、上記の演算子でその文字列の長さをチェックすることにより、
変数に値が代入されている(または、代入されていない)ことを確認することができる。

変数に値が代入されていることを確認する

以下の例では、変数NAMEの値がセットされているか調べている。

 #!/bin/bash
 
 if [ -n "$NAME" ]; then
    echo $NAME
 fi


変数に値が代入されていないことを確認する

以下の例では、変数NAMEの値がセットされていないか調べている。

 #!/bin/bash
 
 if [ -z "$NAME" ]; then
    echo 'NAME is not set'
 fi


# 出力
NAME is not set



数値の演算

加算 / 減算

変数の値に対して、加算および減算を行うには以下のように記述する。

 #!/bin/bash
 
 val=100
 let val=$val+200
 echo $val

または

 #!/bin/bash
 
 val=100
 val=$(($val+200))
 echo $val


# 出力
300


 #! /bin/bash
 
 VALUE1="100"
 VALUE2="200"
 
 expr "$VALE1" "+" "$VALUE2"
 # または
 expr $VALUE1 + $VALUE2
 # または
 VALUE3=`expr "$str1" "+" "$str2"`
 echo "$VALUE3"


# 出力
300
300
300


整数の演算では、exprコマンドまたはletコマンドを使用するが、
実数を演算では、bcコマンドを使用する。
また、bcコマンドも制御構文を使用したスクリプト処理が可能である。

 #! /bin/bash
 
 VALUE1=10.5
 VALUE2=3.2
 
 echo $VALUE1 + $VALUE2 | bc
 
 ANS=`echo $VALUE1*$VALUE2 | bc`
 echo $ANS


下表に、letコマンドで使用できる演算子を示す。(四則演算は省略)

演算子 説明
<変数名>++
<変数名>--
変数を評価した後でインクリメントまたはデクリメントする。
++<変数名>
--<変数名>
変数をインクリメントしてから評価する、変数をデクリメントしてから評価する
** 累乗
% 剰余
~ ビット単位の否定
<< 左ビットシフト
>> 右ビットシフト
& ビット単位のAND
^ ビット単位のXOR
| ビット単位のOR
<=
>=
<
>
大小の比較(TRUEは1、FALSEは0)
==
!=
等価または等価ではない(TRUEは1、FALSEは0)
! 論理否定
&& 論理的AND
|| 論理的OR
<式1> ? <式2> : <式3> 三項演算(<式1>がTRUEならば<式2>、<式1>がFALSEなら<式3>を評価する)


この他に、以下のような代入演算子を使用できる。

  • =
  • *=
  • /=
  • %=
  • +=
  • -=
  • <<=
  • >>=
  • &=
  • ^=
  • |=


ランダムな数値の取得

Bashのシェル変数$RANDOMを参照すると、0~32767の範囲のランダムな整数を取得することができる。

 #!/bin/bash
 
 echo $RANDOM


# 出力
27591


文字列の連結

複数の変数を連結するには、以下のように続けて記述する。

 #!/bin/sh
 
 STR1="Shell script"
 STR2="is intersting!"
 
 STR=$STR1$STR2
 
 echo $STR


また、以下のように変数を{}で括る。

 #!/bin/sh
 
 STR1="Shell script"
 STR="${STR1} is intersting!!"
 
 echo $STR



文字列の長さの取得

変数に代入されている文字列の長さを取得するには、以下の2つの方法がある。

 echo ${#変数名}
 # または
 echo "#変数名"
 
 expr length "変数名"
 # または
 expr length "文字列"



コマンドの実行結果を変数に代入する

lsgrepfind等のコマンドの実行結果を変数に代入するには、以下のように記述する。

変数=$(コマンド)
   または
変数=`コマンド`


以下の例では、lsコマンドの実行結果を変数FILESに代入して、echoで出力している。

 #!/bin/sh
 
 FILES=$(ls)
 echo "$FILES"

または

 #!/bin/sh
 
 FILES=`ls`
 echo "$FILES"



環境変数

環境変数とは、システム全体の動作に関連する値が設定されている(もしくはユーザが任意で設定する)変数であり、
環境変数として設定されている変数名とその値はenvコマンドで確認できる。

特殊変数名 自動的に設定される値
$? 直前に実行されたコマンドの終了ステータスが設定される変数。
シェルスクリプトでは、exitコマンドに与えた引数が終了ステータスとなる。

例えば、exit 2と記述する場合、そのシェルスクリプトの終了ステータスは2となる。
また、関数のreturnの場合も同様に、指定した引数がその関数の終了ステータスとなる。
$! バックグラウンドで実行されたコマンドのプロセスIDが設定される変数。
sleep 100 &のように&を付加してバックグラウンドで実行する時、
特殊変数$!には、sleepコマンドのPID(プロセスID)がセットされる。
$- setコマンドで設定されたフラグまたはシェルの起動時に指定されたフラグの一覧が設定される変数。
$$ コマンド自身のPID(プロセスID)が設定される変数。

例えば、シェルスクリプト内で作成するファイル名に変数$$を指定するというものがある。
例 : tempfile_$$.txt

同一シェルスクリプトを同時実行する場合、シェルスクリプト実行ごとに作成するファイル名を変更できるため、
各シェルスクリプトが同時に同一ファイルに出力することを防止できる。
$LINENO この変数を使用している行の行番号が設定される変数。
${PIPESTATUS[@]} パイプで連結した各コマンドの終了ステータスが設定される配列。
この変数を使用することで、パイプの先頭や途中にあるコマンドの終了ステータスを参照することができる。
ただし、bashのみ使用できる。
$PS1 プロンプトに使用される文字列が設定されている環境変数。
この変数の値を変更することで、プロンプトの表示形式を変更することができる。
$PS2 コマンドが複数行にわたった場合のプロンプトに使用される文字列が設定されている変数。
特殊変数PS1と同様に値を変更することにより、プロンプトの表示形式を変更することができる。
$PATH コマンドが格納されているディレクトリのパスが設定されている変数。
コマンド実行時は、この特殊変数PATHに設定されているディレクトリからコマンドが自動的に検索される。
$TZ タイムゾーンが設定されている変数。
変更することは無いと考えるが、例えば、古いUNIX環境において昨日の日付を取得する等、一時的に変更する場合がある。
$PWD カレントディレクトリのパスが設定されている変数。
PS1='[${PWD}]$ 'のように特殊変数PS1に設定すると、プロンプトにカレントディレクトリが表示されるようになる。
$IFS デリミタ(区切り文字)として使用される値が設定されている変数。
初期値において、タブ、スペース、改行が設定されている。

繰り返し文の値リストの区切り、readコマンドで取得する値の区切り等に使用される。


以下の例では、環境変数SRC_ROOTが設定されているかを確認して、設定されていない場合はメッセージを出力して終了している。
echoの出力を>&2とリダイレクトすると、標準エラー出力へ出力できる。

 #!/bin/bash
 
 if [ -z $SRC_ROOT ]; then
    echo 'Please set the "SRC_ROOT" environment variable and try again.' >&2
    exit -1
 fi
 
 echo 'Program continues...'
 
 # 出力
 # 設定されている場合
 Program continues...
 # 設定されていない場合
 Please set the "SRC_ROOT" environment variable and try again.


以下の例では、root権限でシェルを実行してるかどうかを判別している。

 #!/usr/bin/env sh
 
 if [ "$UID" -eq "$ROOT_UID" ]; then
    # ...略
 fi


以下の例では、使用中のターミナルの種類を判別している。

 # ターミナルの種類を判別
 if [[ $TERM == *"gnome"* ]]; then
    # gnome-terminalの場合
    # 何らかの処理
 elif [[ $COLORTERM == "konsole" ]] || [[ $TERM == "xterm-256color" && -n "$KONSOLE_VERSION" ]]; then
    # Konsoleの場合
    # 何らかの処理
 else
    # その他のターミナルの場合
    # 何らかの処理
 fi




変数展開機能

変数展開の仕組みを利用すると、変数に格納された文字列を置換、変数の存在を確認、変数の初期値の指定等ができる。

パターン照合演算子

下表に示すように、変数展開の構文を使用すると、変数に格納された文字列の一部を置換した文字列を作成することができる。
また、パターン部分において、ワイルドカード(*、?、[a-z]等)が使用できる。

構文 意味
${変数/パターン/置換文字列} パターンに一致する部分を置換文字列に置き換える(1つだけ)
${変数//パターン/置換文字列} パターンに一致する部分を置換文字列に置き換える(すべて)
${変数#パターン} 先頭から最短一致でパターンに一致する部分を取り除く
${変数##パターン} 先頭から最長一致でパターンに一致する部分を取り除く
${変数%パターン} 末尾から最短一致でパターンに一致する部分を取り除く
${変数%%パターン} 末尾から最長一致でパターンに一致する部分を取り除く


  • 最初に一致する文字列のみ置換する。
 # 実行
 X=aaabbbccc
 Y=${X/b/B}
 echo $Y
 
 # 出力
 aaaBbbccc


  • パターンに一致する文字列を全て置換する。
 # 実行
 X=aaabbbccc
 Y=${X//b/B}
 echo $Y
 
 # 出力
 aaaBBBccc


sedコマンドと同様の結果を、シェルスクリプトの機能だけで実現できていることがわかる。

 X=aaabbbccc
 Y=$(echo $X | sed -e 's/bbb/BBB/g')


  • フルパスからbasenameを取り出す。
    以下の例では、/aaa/bbb/cccという絶対パスから、basenameのcccを抽出している。
    */というパターンで、先頭から最長一致させて、/aaa/bbb/を取り除いている。
 # 実行
 X=/aaa/bbb/ccc
 Y=${X##*/}
 echo $Y
 
 # 出力
 ccc


また、basenameを取得する場合、一般的に、basenameコマンドを使用した方が簡単である。

 basename $X


  • フルパスからdirnameを取り出す。
    以下の例では、/aaa/bbb/cccという絶対パスから、ディレクトリ名の/aaa/bbbを抽出している。
    /*というパターンで、末尾から最短一致させて、/cccを取り除いている。
 # 実行
 X=/aaa/bbb/ccc
 Y=${X%/*}
 echo $Y
 
 # 出力
 /aaa/bbb


また、ディレクトリ名を取得する場合、一般的に、dirnameコマンドを使用した方が簡単である。

 dirname $X


  • '#'以降のコメントを削除する。
    #*というパターンで、末尾から最長一致させて、変数LINEの#以降の部分を削除している。
 # 実行
 LINE="aaa bbb # This is a comment"
 LINE=${LINE%%#*}
 echo $LINE
 
 # 出力
 aaa bbb


初期値

構文 意味
${変数:-word} 変数が未定義の時、wordを返す。
${変数:=word} 変数が未定義の時、変数にwordを代入して返す。
${変数:?}
${変数:?word}
変数が未定義の時、エラーを表示する。
${変数:+word} 変数が定義されている時、wordを返す。
  • ${変数:-word}
    例えば、X=${COUNT:-0}と記述すると、変数Xの値は変数COUNTの値が無ければ0を返す。
    つまり、変数COUNTの初期値を0とみなして参照する。

    この構文は、コマンドライン引数が省略された場合、初期値を設定するために使用できる。
    例えば、FILENAME=${1:-input.txt}とすると、変数FILENAMEの値は、第1パラメータで指定された値または初期値のinput.txtとなる。


  • ${変数:=word}
    例えば、X=${COUNT:=0}と記述すると、変数COUNTの値が無ければ、変数Xおよび変数COUNTに0を代入する。
    ただし、変数に代入するため、コマンドライン引数や関数の引数の$1を参照する場合には、この方法は使用できない。


  • ${変数:?word}
    例えば、以下のように記述すると、変数COUNTの値が無ければエラーを出力して終了する。
 # 実行
 echo ${COUNT:?}
 
 # 出力
 -bash: count: パラメータが null または設定されていません


?の後ろに表示するメッセージを指定することもできる。

 # 実行
 echo ${COUNT:?パラメータが設定されていません}
 
 # 出力
 -bash: count: パラメータが設定されていません


  • ${変数:+word}
    変数に値が代入されている場合、代わりにwordの値を返す。
    例えば、${COUNT:+1}は、変数COUNTが定義されていたら1と評価される。
 # 実行
 COUNT=9999
 echo ${COUNT:+1}
 
 # 出力
 1


 # 実行
 COUNT=9999
 unset COUNT
 echo ${COUNT:+1}
 
 # 出力
 何も表示されない


位置指定で部分文字列を抽出する

構文 意味
${変数:offset} offsetで指定した位置から末尾までの文字列を抽出する。
${変数:offset:length} offsetで指定した位置からlength分の文字列を抽出する。


 # 実行
 HOGE=ABCDEFGHIJ
 echo ${HOGE:3}
 
 # 出力
 DEFGHIJ


 # 実行
 HOGE=ABCDEFGHIJ
 echo ${HOGE:3:4}
 
 # 出力
 DEFG


offsetやlength に負の値を指定して、末尾から文字列を抽出することもできる。
ただし、offsetの前に1つ以上のスペースが必要である。(スペースを入れないと、${変数:-word}という形で初期値が指定されたとみなされる)

 # 実行
 HOGE=ABCDEFGHIJ
 echo ${HOGE: -3}
 
 # 出力
 HIJ


 # 実行
 HOGE=ABCDEFGHIJ
 echo ${HOGE: -5:-2}
 
 # 出力
 FGH