bashの基礎

参考書籍 : 実践bashによるサイバーセキュリティ対策



・出力
echoコマンドで出力を行うことができる

$ echo "Hello World"
Hello World

組み込みコマンドのprintfで整形出力ができる

$ printf "Hello World\n\n"

 


・変数
bash変数の命名規則 : 先頭がアルファベットか_で後続が英数字
別途定義がなければ、変数は文字列変数となる
変数の頭に$をつけて変数の値を取得できる

$ MYVAR=textforavalue # =の前後にスペースを入れないこと
$ echo $MYVAR # MYVARの中身は文字列
textforavalue

"を使うことで、文字列内の変数の置換ができる

$ firstvar=beginning
$ secondvar="this is just the $firstvar"
$ echo $secondvar
this is just the beginning

$( )構文を用いて、コマンドの出力を変数に格納できる

CMDOUT=$(pwd)

 


・位置パラメータ
bashスクリプト内で、パラメータを取得できる。
渡された最初のパラメータは$1, 次のパラメータはs2, 次はs3... というように特殊な変数に格納される
$0はスクリプト名を表す特別なパラメータで、$#はパラメータ数を表す変数として予約されている
↓のようなechoparams.shを作成する

#!/bin/bash -

echo $#
echo $0
echo $1
echo $2
echo $3

実行すると、パラメータが表示される

$ ./echoparams.sh bash is fun
3
./echoparams.sh
bash
is
fun

 


・入力
readコマンドで、ユーザーからの入力を標準入力から受け取ることができる

$ read MYVAR
$ echo "$MYVAR"

 


・条件
bashから起動するコマンドやプログラムは、成功もしくは失敗を示す値を常に返す
この値は、コマンド実行直後に変数$?で参照できる。0が返却されたら成功もしくは真、それ以外の値は失敗もしくは偽とみなされる

if cd /tmp
then # 成功時
    echo "here is what is in /tmp:"
    ls -l
fi

パイプで結合されたコマンドも扱うことができる。結合されたコマンドの、最後に実行されるコマンドの返却値で条件分岐する

if ls | grep pdf
then # 真
    echo "found one or more pdf files here"
else # 偽
    echo "no pdf files found"
fi

"最後に実行されるコマンドの返却値で条件分岐する"点に注意
次のコマンドの場合、wc(単語数をカウントする)はpdfファイルが0でも、カウントには成功し真になる

$ ls | grep pdf | wc

 
if文は、組み込みコマンド[やtest、[[と組み合わせて使われる
[とtestは同じコマンドだが、[[は少し動作が異なる。基本的には[[を使うべきである。↓が詳しい
test と [ と [[ コマンドの違い - 拡張 POSIX シェルスクリプト Advent Calendar 2013 - ダメ出し Blog
ファイルがファイルシステム上に存在するかの例を示す

if [[ -e $FILENAME ]]
then
    echo $FILENAME exists
fi

if文により可能なファイル評価の一覧を示す
 -d : ディレクトリかどうかを評価する
 -e : ファイルかどうかを評価する
 -r : ファイルが存在しており、かつ読み取り可能かを評価する
 -w : ファイルが存在しており、かつ書き込み可能かを評価する
 -x : ファイルが存在しており、かつ実行可能かを評価する

変数の値の比較の例を示す

if [[ $VAL -lt $MIN ]]
then
    echo "value is too small"
fi

if文により実行可能な数値評価の一覧を示す
 -eq : 数値が等しいかを評価する
 -gt : ある数値(左)が別の数値(右)より大きいかを評価する
 -lt : ある数値(左)が別の数値(右)より小さいかを評価する

<を用いて数値評価を行いたいときは、((で囲む
((内では、変数の値を参照するときに$は不要だが、$1, $2, $#, $?などの特別な変数は$を付けて参照すること

if (( VAL < 12 ))
then
    echo "value $VAL is too small"
fi

※((内では、0以外の値が真となることに注意する。そのため、コマンドの実行結果を((内に含めないほうがわかりやすい

if/then構文以外にも、条件分岐を行うことが可能
コマンドを&&や||で区切って実行できる。
cd $DIR && ls と実行した場合、前のコマンド成功時に後のコマンドが実行される
cd $DIR || echo cd failed と実行した場合、前のコマンド失敗時に後のコマンドが実行される
この構文は、コマンドだけでなく[[の評価も利用できる。

[[ -d $DIR ]] && ls "$DIR" 

というコマンドも実行できる
また、;を使い、一行で複数のコマンドの実行ができる。cd $DIR ; ls のように実行できる。



・ループ
while構文でループ処理が可能
真偽値には、コマンドもしくは{や((を利用できる
do, doneで処理のグルーピングを行う

i=0
while (( i < 1000 ))
do
    echo $i
    let i++
done

letコマンドでインクリメントを実行させ、iが1000未満の間繰り返している。
条件式にコマンドを使う場合は以下のようになる

# pdfファイルが無いディレクトリまで遡る
while ls | grep -q pdf
do
    echo -n "there is a file with pdf in its name here :"
    pwd
    cd ..
done

 
forループもbashで使用できる
単純な数値のループは((を使用する

for (( i=0; i < 100; i++ ))
do
    echo $i
done

シェルスクリプトのパラメータを順に利用するには、任意の名前の変数を利用する

for ARG
do
    echo here is an argument: $ARG
done

このスクリプトの動作は次のようになる

$ ./xxx.sh bash is fun
here is an argument: bash
here is an argument: is
here is an argument: fun

3つめのforの利用方法は、任意の値のリストを利用する方法である。

for VAL in 20 3 dog peach 7 vanilla
do
    echo $VAL
done

値のリストは、コマンドの結果や{..}で生成できる

for VAL in $(ls | grep pdf) {0..5}
do
    echo $VAL
done

 


・関数
次のような構文で関数を定義できる

function myfunc () 
{
    # 関数の処理
    echo "Hello, world!"
}

# 関数呼び出し
myfunc

関数呼び出しは、関数名のみ記述する。

関数の引数は、関数内で$1, $2...で参照できる

function myfunc () 
{
    echo $1
    echo $2
}
myfunc Hello World

 
関数の戻り値は、成功した場合0、エラーが発生したら0以外となる。
値を返却したい場合は、グローバル変数を利用して受け渡しを行う

rtn=""

function myfunc () 
{
    rtn=$(pwd)
}

myfunc 
echo $?
echo $rtn

 


bashにおけるパターンマッチング
アスタリスク(*)は任意の文字数の任意の文字にマッチする
?は単一の文字にマッチする
構文では、内のいずれか一つとマッチする。[^]で[^]内のいずれか以外の一つとマッチする。
[]内にクラスを指定できる。例えば、[:alnum:]など
例えば、

$ ls | grep *[[:punct:]]jpg

はtest.jpgにマッチする
アスタリスク(*)や?をパターンマッチングとして使用しない場合、\でエスケープする
""の内部ではパターンマッチングが行われない



・初めてのスクリプト--OSタイプの検出
OSに固有のコマンドを確認することで、OSタイプを検出するスクリプトの例を示す。

#!/bin/bash -

if type -t wevtutil &> /dev/null    # wevtutilコマンドの存在を確認
then
    OS=MSWin
elif type -t scutil &> /dev/null    # scutilコマンドの存在を確認
then
    OS=macOS
else
    OS=Linux
fi
echo $OS