ShellScript

忘れがちなシェルスクリプト

■ インタプリタ指定(シバン)

#!/bin/sh

#!/bin/bash

【オプション】
-e :終了ステータスが1で停止
-x :実行コマンド・変数値表示
-v :実行コマンド表示

■ 構文

if文

cnt=0
if [ $cnt -eq 0 ]
then
  echo "value=0"
elif [ $cnt -eq 1 ]
then
  echo "value=1"
else
  echo "value!=0 or 1"
fi

case文

case $# in
  0)
    cnt=0
    ;;
  1)
    cnt=$1
    ;;
  *)
    cnt=$2
    ;;
esac

for文

for file in *
do
  echo $file
done
for((i=1; i<=10; i++)){
  echo $i
}

while文

while [ $cnt -le 10 ]
do
  echo $cnt
  if [ $cnt -eq 5 ]
  then
    break
  fi
done

■ 条件式

testコマンド

if [ $# -eq 0 ]
then
  echo "引数なし"
fi

【真になる条件】
A -eq B :数値A == 数値B(equal to)
A -ne B :数値A != 数値B(not equal to)
A -lt B :数値A < 数値B(less than)
A -le B :数値A <= 数値B(less than or equal to)
A -gt B :数値A > 数値B(greater than)
A -ge B :数値A >= 数値B(greater than or equal)
A -a B :条件A and 条件B
A -o B :条件A or 条件B
! A :条件Aが偽
A = B :文字列Aと文字列Bが一致
A != B :文字列Aと文字列Bが不一致
-d A :ファイルAがディレクトリ
-e A :ファイルAが存在する

■ シェル関数

同一ファイル内のシェル関数実行

output.sh
cut_2-3()
{
  echo $1 | cut -c 2-3 
}

output="0123456789"
cut $output

echo $1 
$ bash output.sh 5
12
5

別ファイルのシェル関数実行

cut.sh
func_cut()
{
  echo $1 | cut -c 2-3 
}
disp.sh
. cut.sh
input="0123456789"
func_cut $input
value=`func_cut $input`
echo `expr $val + 1`
$ bash disp.sh
12
13

■ 標準出力へ出力

printf-フォーマット指定して出力

$ answer=0xffffffff
$ printf '16進数では0x%xです。\n10進数では%dです。\n' "$answer" "$answer"
16進数では0xffffffffです。
10進数では4294967295です。
$ answer=0.1234
$ printf '小数点では%.3fです。' "$answer"
小数点では0.123です。

%d :符号付き10進数
%u :符号なし10進数
%x :16進数
%s :文字列
%f :浮動小数点
%e :浮動小数点(指数表現)
%8d :8文字分のスペースで10進数表示(右詰め)
%-8d :8文字分のスペースで10進数表示(左詰め)
%08x :足りない分は0をつけて8桁で16進数表示

echo - メッセージを出力

$ answer="0xffff"
$ echo '答えは'"$answer"'です。'
答えは0xffffです。
10進数⇒16進数
$ echo "obase=16;ibase=10;65535" | bc
FFFF
16進数⇒10進数
$ echo "obase=16;ibase=10;FFFF" | bc
65535
16進数⇒2進数
$ echo "obase=2;ibase=16;FF" | bc
11111111
2進数⇒16進数
$ echo "obase=16;ibase=2;01011101" | bc
5D

■ 計算

16進数の計算

$ value=$((0x20 + 0x10))
$ echo $value
48
$ value="a0"
$ echo $((0x$value * 2))
320
$ echo "obase=10;ibase=16;F0 + FF" | bc
495
$ val=`echo "obase=10;ibase=16;F0 + FF" | bc`
$ echo $val
510

下位1Byte取得

$ source=257
$ output=`expr $source % 256`
$ echo $output
1

桁数取得

$ count=10000
$ echo "length($count)" | bc
5

■ 組み込みコマンド

変数の値を標準入力に設定

$ output="012345"
$ echo $output | cut -c 3-4
23

■ クォート

特殊文字の打ち消して文字列使用

シングルクォート
$ echo 'HOME=$HOME'
HOME=$HOME

パラメータ展開をして文字列使用

ダブルクォート
$ echo "HOME=$HOME"
HOME=/home/vagrant
$ dir0="/home"
$ dir1="/ubuntu"
$ cd "${dir0}/vagrant${dir1}"
$ echo `pwd`
/home/vagrant/ubuntu

1文字分の特殊文字の打消し

バックスラッシュ
$ echo \*
*

コマンドの標準出力を引数として取り込み

バッククォート
$ name=`basename /home/vagrant`
$ echo $name
vagrant
$ name=`grep "test" output.txt | cut -c 2-4`
$ mkdir `cat output.txt | grep "A"`

■ リダイレクト

標準出力を非表示にする

$ grep "aaa" * 1> /dev/null

標準エラーを非表示にする

$ grep "aaa" * 2> /dev/null

標準出力・エラーを非表示にする

$ grep "aaa" * > /dev/null 2>&1

■ 外部コマンド

sleep - 一定時間ウエイト

$ sleep 10
$ sleep 0.1
$ sleep 10m

s :秒
m :分
h :時間
d :日

basename - ファイル名からディレクトリ名/拡張子除去

$ name=`basename home/user/output.txt`
$ echo $name
output.txt
$ basename `pwd`
user
$ basename home/user/output.txt .txt
output

dirname - ファイル名からディレクトリ名取り出し

$ name=`dirname home/user/output.txt`
$ echo $name
home/user/
$ dirname `pwd`
home
$ dirname home/user
home

expr - 数値計算

$ expr 10 \* 5
50
$ cnt=`expr 10 \* \(5 + 2\)`
$ echo $cnt
70
if [ $count -eq `expr 500 \* 2` ]
then
  echo "一致"
fi

■ 特殊パラメータ

引数の数を表示

$ echo $#

引数の数が0なら終了

if [ $# -eq 0 ]; then
  exit 1
fi

最後の引数を表示

echo ${@:$#:1}

終了ステータスを表示

$ echo $?

■ 配列

array=("\[a\] one" "\[b\] two" "\[c\] three" "\[d\] four" "\[e\] five")
for((i=0; i<5; i++)){
  val=`grep "${array[$i]}" output.txt`
  echo $val
}

■ Git操作

git diffの"+"と"-"行数を出力

grep -v "+++" test.diff | awk '/^+/{printf $0 "\n"}' | sed -e "s/+//g" | sed -e "s/ //g" | awk 'NF' | gawk '! /^\*/{printf $0 "\n"}'| gawk '! /^\//{printf $0 "\n"}' | wc -l | awk '{print "Add    : " $0}'
grep -v "\-\-\-" test.diff | awk '/^-/{printf $0 "\n"}' | sed -e "s/-//g" | sed -e "s/ //g" | awk 'NF' | gawk '! /^\*/{printf $0 "\n"}'| gawk '! /^\//{printf $0 "\n"}' | wc -l | awk '{print "Delete : " $0}'

■ その他

カレントディレクトリのファイルに対する操作

$ ls * | while read f; do touch $f ; done

標準出力に対する一行単位の操作

$ grep "LOG" aaaa.txt | while read line; do printf 'result : %s\n' "$line"  ; done
$ grep "LOG" aaaa.txt | awk '{print "result : " $1}'

無限ループ

while :; do
  echo `date`
done

複数行のコメントアウト

: << '#COMMENTOUT'
ソース
#COMMENTOUT

複数行に区切って入力

printf \
"%d" $count