Memo/Linux/ShellScript

https://dexlab.net:443/pukiwiki/index.php?Memo/Linux/ShellScript
 

bash:シェルスクリプト


jsonに変数を埋め込む

  • printfが使えるので
    --ip-permissions "$(printf '[{"IpProtocol": "tcp", "FromPort": 24224, "ToPort": 24224, "IpRanges": [{"CidrIp": "%s", "Description": "%s"}]}]' "${AWS_EC2_DEVLOG_PUBLIC_CIDR}" "${AWS_EC2_DEVLOG_DESCRIPTION}")"

文字列の分割

csv,tsvで文字列を分割して取り出したいとき

  • 行末に改行が無い場合の対応が必要
  • readで複数変数を使う場合、変数に改行が含まれるので削除
  • readを使う
    while read ip hostn || [ -n "${hostn}" ]
    do
      hostn=$(echo -n "$hostn" | tr -d '\r\n')
      echo "ip: ${ip}, host: ${hostn}"
    done < /etc/hosts
  • setを使う
    while read line || [ -n "${line}" ]
    do
      set ${line}
      path=${0}
      ip=${1}
      hostn=${2}
      echo "ip: ${ip}, host: ${hostn}"
    done < /etc/hosts

入力プロンプト

  • sを付けると入力文字が非表示になるのでパスワードに使える
    read -p "proceed ? (y/n)" answer;echo $answer
    proceed ? (y/n) y
    y

URIの分解


tmpwatchで一定時間以上経過したファイルを削除

  • filemy-tmpwatch
    sudo yum install tmpwatch
    sudo cp my-tmpwatch /etc/cron.daily/
    sudo chmod +x /etc/cron.daily/my-tmpwatch

コマンドを探す(which, hash, type)

  • whichはコマンド
  • hash, typeはbash組み込みコマンド
    • typeが良さそう
  • typeを使ってdigコマンドが無かったらエラーとする
    if ! $(type dig > /dev/null 2>&1); then
      echo -e "ERROR\tdig is not found." 1>&2
      exit 1
    fi

0埋め

  • printfを使う。C言語と同じ構文のようだ
    for i in {1..10}; do name=$(printf file%02d.log $i); echo $name; done
    file01.log
    file02.log
    ...
    file10.log

文法(syntax)チェック

  • bash, shも "-n"でチェックできる
    bash -n example.sh

バッググラウトプロセスの終了待ちと終了ステータス

  • 「$!」でバックグラウンドプロセスのPIDを取得できる
  • 「wait PID」 でプロセスの終了まで待ち、「$?」で終了ステータスを取得
  • 子プロセスから「$PPID」親PIDを取得できるが、バックグラウンドだと全て「1」になる。
  • サンプル:待ち時間と終了コードを同じにしている。filewaiting-for-parallel-tasks.zip
    ./parent.sh
    
    parent.sh::PID: 6307
    run: 1, wait: 4, pid: 6310
    run: 2, wait: 9, pid: 6311
    run: 3, wait: 3, pid: 6312
    wait child pid: 6310, exit: 4
    wait child pid: 6311, exit: 9
    wait child pid: 6312, exit: 3

パスからファイル名/拡張子等の切り出し

tmp_path="/etc/httpd/conf/httpd.conf"
tmp_file=${tmp_path##*/} # httpd.conf
echo $tmp_file # httpd.conf
echo ${tmp_file%.*} # httpd
echo ${tmp_path%/*} # /etc/httpd/conf
echo ${tmp_path##*.} # conf

デーモンを作る


select: 選択式メニュー

選択式メニューを表示し、数字を入力するタイプ。


expect: 対話的なコマンド(ssh, telnet, ftp等)を自動実行

  • Man page of EXPECT
    • expect "...", send "..." のように'"'を使う。"'"は動作しなかった
  • 例:YAMAHA RT1xxxルータにtelnetでログインしてNATテーブル数を取得
    #!/bin/bash
    get_nat_table_count(){
    	host=$1
    	password=$2
    	timeout=${3:-5}
    	expect -c "
    set timeout $timeout
    spawn telnet $host
    expect \"Password:\" ; send \"$password\n\"
    expect \">\" ; send \"console character ascii\n\"
    expect \">\" ; send \"console lines infinity\n\"
    expect \">\" ; send \"show nat descriptor address\n\"
    expect \">\" ; send \"exit\n\"
    expect eof" | grep used | awk 'BEGIN {sum=0} {sum+=$6} END {print sum}'
    }
    
    HOST=192.168.1.1
    PASSWORD=****
    get_nat_table_count "$HOST" "$PASSWORD"

タイムアウト処理

  • 例:mysqladmin pingが成功するまで、最大10分待機
    wait_connection() {
    	local db_host=${1:-"127.0.0.1"}
    	local db_user='root'
    	local max_wait_sec=600
    	local wait_sec=10
    	local mysqladmin=/usr/bin/mysqladmin
    	local start_sec=$SECONDS
    	local end_sec=$SECONDS
    
    	if [ -f "$mysqladmin" ]; then
    		while [ $(( $end_sec - $start_sec )) -le $max_wait_sec ]; do
    			$mysqladmin ping -u $db_user -h $db_host > /dev/null 2>&1
    			if [ $? == 0 ]; then
    				break
    			fi
    			sleep $wait_sec
    			end_sec=$SECONDS
    		done
    	else
    		sleep $max_wait_sec
    	fi
    	return 0
    }

浮動小数点演算

  • デフォルトでは整数しか扱えない
  • bcで浮動小数点を含む演算をできる。scale=2;で小数点2桁まで計算
    echo "1.0 + 0.1 + 0.15" | bc
    1.25

パイプで複数繋げたコマンドの終了ステータスを取得

  • $?はパイプの一番最後の終了ステータスしか取得できない
    true | false | (exit 3) | (exit 8)
    echo $?
    8
  • ${PIPESTATUS[@]} を使うとパイプで繋いだコマンドの終了ステータスを取得できる
    true | false | (exit 3) | (exit 8)
    echo ${PIPESTATUS[@]}
    0 1 3 8

cron実行時に多重起動の防止

  • flock (CentOS5以上)を使う。ただし、コマンドの終了コードは取れない。flockが成功時に0、失敗時に1になる
    crontab -e
    ----
    * * * * * /usr/bin/flock -n /tmp/test.lock /tmp/test.sh $(date '+\%Y-\%m-\%d \%H:\%M:\%S') >> /tmp/test.log 2>&1
    ----
  • PIDのロックファイルを作って防止する方法
    #!/bin/bash
    
    export PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
    readonly SCRIPT_NAME=$(basename $0)
    readonly LOCK_FILE=/tmp/${SCRIPT_NAME}.lock
    
    lock() {
    	if [ -f $LOCK_FILE ]; then
    	  PID=$(cat $LOCK_FILE)
    	  if (ps -e | grep -P "^\s*$PID" >/dev/null); then
    	    echo $SCRIPT_NAME' is already running.' >&2
    	    exit 1
    	  fi
    	fi
    	echo $$ > $LOCK_FILE
    }
    
    unlock() {
    	rm -f $LOCK_FILE
    }
    
    main() {
    	lock
    	for i in $(seq 1 7); do
    		echo "PID=$$, seq:$i"
    		sleep 10
    	done
    	unlock
    }
    
    main

日付の範囲指定

  • ソース
    #!/bin/bash
    callback() {
    	local date=$1
    	echo $date
    }
    
    date_loop() {
    	local callback=$1
    	local start=$2
    	local end=$3
    
    	while true; do
    		eval "$callback $start"
    		if [ "$start" == "$end" ]; then
    			break;
    		fi
    		start=$(date "+%Y-%m-%d" -d "${start} 1 day")
    	done
    }
    
    date_loop "callback" "2012-02-28" "2012-03-01"
  • 実行結果
    2012-02-28
    2012-02-29
    2012-03-01

$()演算子

  • コマンドの実行し、結果を変数に取り込める。配列も可
    ls_result=$(ls)
    for temp in ${ls_result[@]};do
        echo ${temp}
    done
  • ``演算子と違い、入れ子にできる。「カレントディレクトリの*.shからhogeを含むファイル名を得る」
    result=$(grep -l "hoge" $(ls *.sh))

netstatで接続中のコネクション数を数える

# get_netstat_established_count
# @param int port : tcp port
# @return int count
get_netstat_established_count() {
	local port=$1
	local established_count=`netstat -nt | grep ":${port} " | grep "ESTABLISHED" | wc -l`
	echo $established_count
}

エラー処理をシンプルに書く

  • {...}で括らないとexitが実行されないので注意
    command ... || { echo ... ; exit 1 ; }
    • 例:通常(mkdir -pを使えば良いが)
      mkdir tmp > /dev/null 2>&1
      if [ $? -ne 0 ]; then
        echo "error"
        exit 1
      fi
    • 例:シンプル化
      mkdir tmp > /dev/null 2>&1 || { echo "error" >&2; exit 1; }

バッチ処理

bashのバージョン情報の取得

bash --version | perl -ane 'if($.==1 && /version (\d+)\./){ print $1;}'
# 3

a-z等の文字列が欲しい

var=`echo {0..10}` # 0 1 2 3 4 5 6 7 8 9 10

var=`echo {a..z}` # a b c d e f g h i j k l m n o p q r s t u v w x y z

空ディレクトリチェック

if [ -z "$(ls -A ./dirname/)" ]; then
        echo "empty"
else
        echo "not empty"
fi

連想配列/ハッシュ

  • v4.0で新しく追加された。v3系ではちょっとわかりにくい
  • bash v4.x
    #/bin/bash
    
    # bash v4. "-A":連想配列を宣言 / "-a":数値配列
    unset ary
    declare -A ary
    
    ary=(
    	["key1"]="value1"
    	["key2"]="value2"
    	["key3"]="value3"
    )
    
    # 要素の追加
    ary+=(
    	["key4"]="value4"
    )
    
    # 要素数
    echo num: ${#ary[@]} # 4
    
    # valueだけ欲しい
    for i in "${ary[@]}"; do
    	echo $i
    done
    
    # key, valueも欲しい
    for i in "${!ary[@]}"; do
    	echo $i, ${ary[$i]}
    done

ヒアドキュメント

まとめて標準入力、標準エラー、ファイル等に出せる

  • 「cat << EOS」のままだと、変数展開される。「cat << 'EOS'」にすると変数展開されない。
  • 標準出力
    cat << EOS
    SHELL: $SHELL
    EOS
  • 標準エラー
    cat 1>&2 << EOS
    SHELL: $SHELL
    EOS
  • ファイルに出力。「>」で上書き、「>>」で追記
    cat > file.tmp << 'EOS'
    hoge
    moke
    EOS
  • 変数に入れる
    json=$(cat << EOD
    {
       "key1":"val1",
       "key2":"val2"
    }
    EOD
    )
    
    echo $json | jq

テンポラリファイル名

  • $$はPIDなので一意。同時実行される可能性のある場合は便利
    TMP_FILE=$$.tmp
  • mktempも使える
    mktemp
    /tmp/tmp.0FEIQ9u8mo
    
    mktemp example.XXXXXX
    example.Os0Nne

bashで秒、ミリ秒の取得

  • 秒:$SECONDS
  • ミリ秒:printf '%.3f' `date '+%s.%N'`
#!/bin/bash

loop(){
    for i in {1..10}; do
        sleep 1
    done
}

START_SEC=$SECONDS
START_MSEC=`printf '%.3f' \`date '+%s.%N'\``

loop

END_SEC=$SECONDS
END_MSEC=`printf '%.3f' \`date '+%s.%N'\``

echo $(($END_SEC - $START_SEC))" s."
echo `echo "scale=3;($END_MSEC - $START_MSEC)" | bc`" s."

bash最短関数

bashで関数名だけ用意したい事があるが、空の関数は書くとエラーになる。

  • bashにコロン":"が何もしない組み込み関数として用意されている
    dummy(){
     : 
    }
    
    dummy

タイムゾーンを日本に変更

  • UTC設定サーバで一時的に日本時間で表示したい
    export TZ=JST-9
    date '+%Y/%m/%d %H:%M:%S %Z'

パイプの先は別シェルなので変数取得やexitが無効

  • シェルスクリプト内でパイプは別シェルが起動するためループ内で以下は無効になる
    • 変数が取得できない
    • exit等が無効
    • bugあり。$strに値が入らない。ループ内でexitしても終了しない
      #!/bin/bash
      
      str=""
      cat /etc/fstab | while read line; do
        str="$line"; break;
        # exit;
      done
      
      echo "str : $str"
    • リダイレクトに修正
      #!/bin/bash
      
      str=""
      while read line; do
        str="$line";break;
      done < /etc/fstab
      
      echo "str : $str"

文字色の変更

  • CentOS: /etc/rc.d/init.d/functions にもchkconfig等で使われている色が乗っている
    echo_success()
    echo_failure()
    echo_passed()
    echo_warning()

IPアドレスの取得

  • eth0の取得
    NIC0=`/sbin/ifconfig | grep eth0 | awk -- '(NR == 1){print $1}'`
  • CentOS6.x
    /sbin/ifconfig eth0 | grep 'HWaddr' | awk -- '{print $5}'
  • CentOS5.x
    /sbin/ifconfig eth0 | grep 'inet addr:' | awk -- '{print $2}' | awk -F':' -- '{print $2}'

関数

  • returnに指定できる値は、1〜255 の正の整数のみ。終了ステータスとなる
    cat >> example1.sh << 'EOS'
    func() {
      return 1
    }
    
    # 呼び出し
    func
    EOS
    
    # 終了ステータス確認(1が返る)
    bash example1.sh
    echo $?
  • 戻り値はないので、var=`func`で受け取る
    cat >> example2.sh << 'EOS'
    func() {
      echo "result-data"
    }
    
    result=`func`
    echo ${result} # result-data
    EOS
  • 引数
    cat >> example3.sh << 'EOS'
    #!/bin/bash
    
    func() {
      echo "\$0=$0" # $0=example3.sh
      echo "\$#=$#" # $#=3
      echo "\$@=$@" # $@=001 002 003
      echo "\$*=$*" # $*=001 002 003
      echo "\$1=$1" # $1=001
      echo "\$2=$2" # $2=002
      echo "\$3=$3" # $3=003
    }
    
    func 001 002 003
    EOS
  • local変数。関数内でだけ定義できる
    cat >> example4.sh << 'EOS'
    #!/bin/bash
    
    GLOBAL=global
    
    func() {
        local GLOBAL=local
        echo $GLOBAL # local
    }
    
    func
    echo $GLOBAL # global
    EOS

sudo実行したユーザ、グループ取得

HOME_USER=${SUDO_USER:-"$USER"}
HOME_GROUP=`id $HOME_USER -g -n`

ファイル名の一括変更

  • *us.txtを*jp.txtに変更
    for F in *us.txt;do mv $F ${F/us./jp.};done

JSONパーサー


計算

  • 出力したい式
    $min + $RANDOM % ( $max - $min )
  • expr
    expr 1 + $RANDOM % \( 10 - 1 \)
  • bash。若干早いが、互換性は無くなる
    echo $(( 1 + $RANDOM % ( 10 - 1 ) ))

ランダムな数値と文字列

  • $RANDOM: 0-32767 の値を返す
  • /dev/urandom から任意の長さの乱数文字列を取得
    • LANG=CはMacOSXで「tr: Illegal byte sequence」が出るための修正。catを使うとansibleからキックした場合に、プロセスが残る問題のためheadに修正。
      echo $(head -c 1k /dev/urandom | LANG=C tr -dc '[:alnum:]' | head -c 32)
      U2egJwX9m1WQyHMtaJpkrTuUdw2v71Bn
  • /dev/urandom を使って、指定範囲の乱数を取り出すスクリプト。/dev/urandom は非常に長いため遅い。mkpasswdも遅い
    #/bin/bash
    
    # rand
    # @param int min : default 0
    # @param int max : default 32767,  max 32767
    # @return int    : 
    rand()
    {
    	local min=0
    	local max=32767
    	if [ $1 ]; then
    		min=$1
    	fi
    	if [ $2 ]; then
    		max=$2
    	fi
    	echo `echo $(( $min + $RANDOM % ( $max - $min ) ))`
    }
    
    # rand_str
    # @param int len : default 8
    # @return int    : 
    rand_str()
    {
    	local len=8
    	if [ $1 ]; then
    		len=$1
    	fi
    	echo `</dev/urandom tr -dc A-Za-z0-9 | head -c $len`
    }
    
    # rand_str_fast
    # @param int len : default 8, max 1000
    # @return int    : 
    RANDSTRING=`</dev/urandom tr -dc A-Za-z0-9 | head -c 1000` # 1000文字まで作成しておく
    rand_str_fast()
    {
    	local len=8
    	if [ $1 ]; then
    		len=$1
    	fi
    	local max=`echo $(( 1000 - $len ))`
    	local start=`rand 1 $max`
    	local end=`echo $(( $start + $len -1 ))`
    	echo `echo -n "$RANDSTRING" | cut -c$start-$end`
    }
    
    for i in {1..10}; do
    	echo `rand 1 10`,`rand_str_fast 8`
    done
  • 実行結果
    2,8NOLYUIK
    3,j7y74euW
    3,5XG20fJ1
    3,Cn5NGZwM
    4,MKwbgRJO
    7,BpeOCLZM
    9,O9zbPunH
    8,0j3cSLWd
    6,yoExCkEX
    8,OHEyoqeJ

指定回数ループ

  • bash
    for i in {1..10}; do
    	echo $i
    done
  • 変数で指定したい場合
    LOOP=10
    for i in `eval echo {1..$LOOP}`; do
    	echo $i
    done
  • seqで桁合わせ
    for i in $(seq -w 001 003); do
    	echo $i
    done
    
    001
    002
    003
  • 変数中の「$i」を置換
    for i in $(seq -w 001 003); do \
      host=$(echo 'web$i' | perl -pe "s/\\$\i/$i/" ); \
      echo $host; \
    done
    
    web001
    web002
    web003

OSの判別

  • http://kanon.ultimania.org/hg/KanonConductor/file/803f7b41dfdf/kanon-setup
    #!/bin/bash
    OS=''
    APACHE_USER=''
    
    get_os() {
    	local os=''
    	if [ -f /etc/debian_version ]; then
    		os='debian'
    	elif [ -f /etc/vine-release ]; then
    		os='vine'
    	elif [ -f /etc/turbolinux-release ]; then
    		os='turbo'
    	elif [ -f /etc/redhat-release ]; then
    		local CHK=`egrep "CentOS .*release [5-6]|Red Hat Enterprise Linux .* [5-6]" /etc/redhat-release`
    		if [ "$CHK" != '' ]; then
    			os='rhel'
    		fi
    	else
    		local os=''
    	fi
    
    	echo "$os"
    }
    
    get_apache_user() {
    	local os=$1
    	local user=''
    
    	if [ "$os" == "debian" ]; then
    		user="www-data"
    	elif [ "$os" == "rhel" ]; then
    		user="apache"
    	else
    		user=''
    	fi
    
    	echo "$user"
    }
    
    OS=`get_os`
    APACHE_USER=`get_apache_user "$OS"`
    echo "OS: $OS"
    echo "APACHE_USER: $APACHE_USER"

複数行コメント

  • 「:<<'#文字列' 〜 文字列」までが複数行コメント
    #!/bin/bash
    
    :<<'#comment'
    echo hoge1
    echo hoge2
    #comment
    
    echo hoge3
  • 実行結果
    hoge3

バージョン番号の比較

  • Apacheの場合
    v=`httpd -v | perl -ane 'if( @F[2]=~/Apache\/(\d+\.\d+)/ ){ print $1; }'`
    r=`echo "$v >= 2.2" | bc`
    if [ $r -eq 1 ]; then
    	echo "$v >= 2.2"
    else
    	echo "$v < 2.2"
    fi

正規表現

  • Stray Penguin - Linux Memo (BASH)
  • `[[`、比較演算子`=~` を使うと、右辺は拡張正規表現とみなされる。
  • 右辺はシングル/ダブルコーテーションは動作しない
  • 右辺にスペースが入るとうまく動作しないので、変数に入れる
  • 左辺はダブルコーテーションで囲ってもOK
  • マッチ部分は BASH_REMATCH という配列に入る。[0]が全体。[1]〜が()で括った部分
  • bash 4.1.2
    mysql_ver=$(mysql -V) # mysql Ver 14.14 Distrib 5.5.30, for Linux (x86_64) using readline 5.1
    pattern='Distrib ([[:digit:]]\.[[:digit:]])'
    if [[ $mysql_ver =~ $pattern ]]; then
      echo ${BASH_REMATCH[0]} # Distrib 5.5
      echo ${BASH_REMATCH[1]} # 5.5
    fi

デバッグ

  • 記事
# -x:実行されたコマンドを表示する。変数も展開される。
bash -x test.sh

# -v:実行されるコマンドを表示する。変数は展開されない。
bash -v test.sh
  • デバッグ時のみ処理
    #!/bin/bash
    DEBUG=1
    [ $DEBUG == 0 ] || { echo "debug";exit; }

エラーの際に処理を止める/未定義変数を参照した時にエラーとする

  • -e: エラーの際に処理を停止
    set -e
    touch /tmp/foo/bar.txt
    # /tmp/foo ディレクトリが無いので停止
    echo success
  • -u: 未定義の変数を参照した時にエラー
    set -eu
    echo $tmp_dir/bar.txt
    # $tmp_dir が未定義なため停止
    echo success

文字列操作

  • 先頭文字の取得
    VAR1=2012-08-01
    
    # 最後から最長一致
    VAR2=${VAR1%%-*} # 2012
    
    # 最後から最短一致
    VAR2=${VAR1%-*} # 2012-08
    
    # 最後の一文字を削除
    VAR2=${VAR1/%?/} # 2012-08-0
  • 末尾文字の取得
    VAR1=example.tar.gz
    
    # 左から最長一致
    VAR2=${VAR1##*.} # gz
    
    # 左から最短一致
    VAR2=${VAR1#*.} # tar.gz

bash変数

  • local変数。関数内のみ有効なスコープ
    test() {
        local var1;
    }
  • 定数
    readonly VAR1=test1
    VAR2=test2
    
    VAR1=hoge1 # エラー
    VAR2=hoge2 # エラーにならない
  • グルーピング。echoでログファイルに書く場合などまとめる事ができる
    {
      echo "hoge"
      echo "fuga"
      echo "foo"
      echo "bar"
    } >>logfile.log
    # 最後を 1>&2 にすれば標準エラーにも吐ける

bash特殊変数



区切り文字変更 IFS変数

  • デフォルト
    IFS="$'\n'$'\t' "
  • カンマ区切り
    #!/bin/bash
    IFS=','
    str=a,b,c
    for i in $str; do
      echo $i
    done
  • 改行
    IFS='
    '
    
    または
    
    IFS=$'\n'

変数にデフォルト値をセット

NAME=$1
NAME2=${2:-"DEFAULT"}

# デフォルトの値を表示
echo ${NAME:-"DEFAULT"}
echo "NAME: $NAME"

# 空文字列、未設定はエラー
echo ${NAME:?"Error"}

# 空文字、未設定は空文字。それ以外は、wordに置換
echo ${NAME:+"word"}

# デフォルトの値を代入
var=${NAME:="Guest"}
echo "NAME: $NAME"

高度


tputでメニュー

  • 10 Tools To Add Some Spice To Your UNIX Shell Scripts
    #!/bin/bash
    
    # clear the screen
    tput clear
    
    # Move cursor to screen location X,Y (top left is 0,0)
    tput cup 3 15
    
    # Set a foreground colour using ANSI escape
    tput setaf 3
    echo "XYX Corp LTD."
    tput sgr0
    
    tput cup 5 17
    # Set reverse video mode
    tput rev
    echo "M A I N - M E N U"
    tput sgr0
    
    tput cup 7 15
    echo "1. User Management"
    
    tput cup 8 15
    echo "2. Service Management"
    
    tput cup 9 15
    echo "3. Process Management"
    
    tput cup 10 15
    echo "4. Backup"
    
    # Set bold mode
    tput bold
    tput cup 12 15
    read -p "Enter your choice [1-4] " choice
    
    tput clear
    tput sgr0
    tput rc

getopt,getopts

  • getopt : 外部コマンド。ロングオプション(--)も対応。後ろにオプションがあっても大丈夫。MacOSX非対応
  • getopts : bashの内部関数。getoptよりすっきり書ける。オプションが最後にあるような構文は非対応。
  • $OPTIND : 次に処理するオプションのインデックスが入っている。
    # オプション部分を切り捨てる
    shift `expr $OPTIND - 1`
  • getopts版。引数を処理する - UNIX & Linux コマンド・シェルスクリプト リファレンス
    #!/bin/bash
    
    export LANG=C
    CMDNAME=`basename $0`
    
    usage_exit() {
        echo "Usage: $CMDNAME [-a] [-b VALUE] [-c VALUE]" 1>&2
        exit 1
    }
    
    echo "[DEBUG GLOBAL] \$#   : $#"
    echo "[DEBUG GLOBAL] \$@   : $@"
    echo_debug() {
        echo "[DEBUG LOCAL] \$FLG_A : $FLG_A"
        echo "[DEBUG LOCAL] \$FLG_B : $FLG_B / $VALUE_B"
        echo "[DEBUG LOCAL] \$FLG_C : $FLG_C / $VALUE_C"
    }
    
    while getopts "a,b:,c:" OPT
    do
      case $OPT in
        "a" ) 
            FLG_A="TRUE"
            ;;
        "b" )
            FLG_B="TRUE"
            VALUE_B="$OPTARG"
            ;;
        "c" )
            FLG_C="TRUE"
            VALUE_C="$OPTARG"
            ;;
        * )
            usage_exit
            ;;
      esac
    done
    
    # オプション部分を切り捨てる
    shift `expr $OPTIND - 1`
    
    echo "[DEBUG GLOBAL] \$1   : $1"
    echo "[DEBUG GLOBAL] \$2   : $2"
    echo_debug
  • getopt版。シェルスクリプトを書くときに地味に便利なgetopt - Kirikaの非人間的生活
    #!/bin/bash
    
    export LANG=C
    CMDNAME=`basename $0`
    
    usage_exit() {
        echo >&2 "Usage: $CMDNAME [-h|--hostname hostname] [-u|--hostuser username] [-s] [--longonly]"
        exit 1
    }
    
    # check command args.
    if [ $# -lt 1 ]; then
        usage_exit
    fi
    
    echo "[DEBUG GLOBAL] \$#   : $#"
    echo "[DEBUG GLOBAL] \$@   : $@"
    echo_debug() {
        echo "[DEBUG LOCAL] hostname  : $hostname"
        echo "[DEBUG LOCAL] hostuser  : $hostuser"
        echo "[DEBUG LOCAL] shortonly : $shortonly"
        echo "[DEBUG LOCAL] longonly  : $longonly"
    }
    
    OPT=`getopt -q -o "h:,u:,s" -l "hostname:,hostuser:,longonly" -- "$@"`
    if [ $? != 0 ]; then
        usage_exit
    fi
    
    eval set -- "$OPT"
    while [ -n "$1" ]; do
      case $1 in
        -h|--hostname) hostname=$2; shift 2;;
        -u|--hostuser) hostuser=$2; shift 2;;
        -s) shortonly=1; shift 1;;
        --longonly) longonly=1; shift 1;;
        --) shift; break;;
        *) echo "Unknown option($1) used."; exit 1;;
      esac
    done
    
    echo "[DEBUG GLOBAL] \$1   : $1"
    echo "[DEBUG GLOBAL] \$2   : $2"
    echo_debug

rootやsudoの判別

  • rootユーザもしくは、sudoの時は$EUID, $UIDが0になる。$SUDO_USERも定義される
    if [ $EUID -eq 0 ]; then
        echo "root"
    fi
    

全引数をダブルクォート付きで渡す

  • ダブルクォートで囲んだ場合の挙動が違う
    • "$*"
      "$1 $2 ..."
    • "$@"
      "$1" "$2" ...
      • よって、別コマンドへスペースも含んだコマンドを渡すには
        #/bin/bash
        hoge.php "$@"

配列変数を使う

  • bash2.0以降ならば、変数に配列が使用できるようです
  • 配列の場合必ず${...}として{}で括る
  • 使用例
    #!/bin/bash
    a=(
    "aaa"
    "bbb"
    "c c c"
    )
    #a[0]="aaa"
    #a[1]="bbb"
    #a[2]="c c c"
    for var in "${a[@]}"; do
      echo "${var}"
    done
  • 出力結果
    aaa
    bbb
    c c c
  • 関数として値を渡す場合
    main(){
        for arg in "$@"; do
            echo $arg
        done
    }
    
    main "$@"
  • 配列のコピー
    ARRAY=( "a" "b" "c" )
    ARRAY2=${ARRAY[*]}
    echo ${ARRAY2[*]}
  • 要素を追加。""で括らないと"123 abc"のようなスペースを含む文字の場合の挙動が変わる
    ARRAY1=()
    str1="a"
    str2="b"
    ARRAY1+=("$str1" "$str2")
  • 要素数(添え字が連続している場合のみ。不連続の場合は意図した結果にならない)
    ${#ARRAY[*]}

シェルスクリプトでカレントディレクトリの取得

CUR_DIR=$(cd $(dirname $0);pwd)

シェルスクリプトの実行ログを記録

  • tee?
    export PATH=$PATH:/usr/sbin:/sbin
    sh install.sh | tee install.log

搭載メモリをMBで取得

MEM_TOTAL=`perl -e '$m=readpipe("free -m");$m=~/Mem:\s+(\d+)/i;print int($1)'`
MEM_FREE=`perl -e '$m=readpipe("free -m");$m=~/cache:\s+(\d+)\s+(\d+)/i;print int($2)'`

日付形式をチェック

  • YYYY-MM-DD形式以外はエラー
    echo "2012-01-01" | egrep '^[0-9]{4}\-[0-9]{2}\-[0-9]{2}$' > /dev/null || echo $?

ホスト名にアンダーバーが含まれていたらエラー

 match=`echo "$1" | egrep '^([0-9a-z\-]+)$' -`
 if [ $? != 0  ]; then
         echo "Error : Invalid character. allow [0-9, a-z, -]"
         exit
 fi

添付ファイル: filemy-tmpwatch 318件 [詳細] filewaiting-for-parallel-tasks.zip 628件 [詳細] fileselect-yes-no.sh 424件 [詳細]

トップ   編集 凍結 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS
Last-modified: 2018-09-15 (土) 07:31:38 (9d)