最近の更新

関連


その他いろいろ

MODxでつくる! 最強のCMSサイト カバー
MODxでつくる! 最強のCMSサイト

はやくMODx 2.0でないかなあ

Shell scripts

Bash/Zsh向けのシェルスクリプト
初出:2008年11/09 更新:2009年06/20

この文書のキーワード: なし

必要になるまでパスフレーズを要求しない lazy-ssh-agent

lazy-ssh-agentはこちら。

zshに乗り換えよう

大抵のの環境でデフォルトのbashにもそれなりに便利な補完機能がついていますが、zshの便利さといったらもう比ではありません。履歴から補完、補完メニュー、右プロンプト、'**/*'で全てのサブディレクトリなどの様々な展開パタン、コマンドラインスタック、履歴共有、履歴のunique化、自動pushd、コマンドに応じた補完ファイルの限定、コマンドに応じたサブコマンドの補完(svn, aptitudeのパッケージ, scpのリモートディレクトリ等)、補完リストからカーソルキーで選択、 etc...

今すぐsudo aptitude install zshしましょう。割と便利な.zshrcをこちらで公開しています。

make (gcc)のエラーと警告を色づけ

make (gcc)の吐くメッセージは視認性が悪く、エラーが出てもgrepしないと何処で問題が発生しているのかわからないことがしばしばあります。このスクリプトはmakeをラップし、bashの色づけエスケープシーケンスを用いて警告の背景色はマゼンタ、エラーは赤に設定し、大量のスクロールバックがあっても簡単に問題発生箇所を見分けられるようにします。makeのエイリアスにでも設定すると便利です。

ふつうのmakeと色つきmake
#!/bin/bash
# colored make (bash color escape sequence)
#  20090620 first release

lightgray="echo -e \e[1;37m"
lightred="echo -e \e[1;31m"
lightgreen="echo -e \e[1;32m"
lightpurple="echo -e \e[1;35m"

underline="echo -e \e[4m"
nounderline="echo -e \e[0;4m"

bglightred="echo -e \e[1;41m"
bglightgreen="echo -e \e[1;42m"
bglightpurple="echo -e \e[1;45m"
bgnormal="echo -e \e[41;0m"

normal="echo -e \e[0m"

(make "$@" | cat) 2>&1 | \
  sed -u -r -e "s/^(.*?)(error:)(.*)$/`$bglightred`\\1\\2\\3`$bgnormal`\n/" \
            -e "s/^(.*?)(: undefined reference)(.*)$/`$bglightred`\\1\\2\\3`$bgnormal`\n/" \
            -e "s/^(.*?)(warning:)(.*)$/`$bglightpurple`\\1\\2\\3`$bgnormal`\n/" \
            -e "s/^(make.*?)(Error )(.*)$/\\1`$bglightred`\\2`$normal`\\3/" |\
  cat

xterm/gnome-terminalでコマンド実行をラップ

xtermやgnome-terminalの-eオプションでコマンドを指定して実行出来ます。ラップしてちょっと使いやすく。

#!/bin/bash

# spawnterm: run a command in new sync/async xterm window
#
# usage:
#   spawnterm [x|g] [sync|async] {command} [directory] [title]
#   [x|g] is x:xterm g:gnome-terminal
#
# example:
#   spawnterm x sync 'env | grep LD && read' '..' 'title'
#   spawnterm g sync 'vim'
#   spawnterm x async '(ls; cd ..; ls) && read' / 'list of the root dir'
#
# note:
#   the shebang should be 'bash' but not 'sh'.
#
function spawnxterm() {
  spawnterm x "$@"
}
function spawngterm() {
  spawnterm g "$@"
}
function spawnterm() {
  GNOME_TERMINAL='gnome-terminal --hide-menubar'
  XTERM="xterm"
  ASYNC=1
  DIR=.
  TITLE=
  TERM=$1
  shift 1
  case $1 in
   async)
    ASYNC=1
    shift 1;;
   sync)
    ASYNC=0
    shift 1;;
  esac
  CMD=$1
  shift 1
  if [ $# -ge 1 ]; then DIR=$1; shift 1; fi
  if [ $# -ge 1 ]; then TITLE=$1; shift 1; fi
  if [ $TERM == 'x' ]; then
    (builtin cd $DIR
     if [ $ASYNC -eq 1 ]; then
       $XTERM -title "$TITLE" -e $(which bash) -c "$CMD" &
     else
       $XTERM -title "$TITLE" -e $(which bash) -c "$CMD"
     fi)
  else
    (builtin cd $DIR
     if [ $ASYNC -eq 1 ]; then
       $GNOME_TERMINAL --title "$TITLE" --working-directory="$DIR" -x $(which bash) -c "$CMD" &
     else
       $GNOME_TERMINAL --title "$TITLE" --working-directory="$DIR" -x $(which bash) -c "$CMD"
     fi)
  fi
}

VisualStudio (C#, C++) のプロジェクトを丸々バックアップ

タイムスタンプをつけてプロジェクトのソースコード(/binや/obj以外)をtar.bz2で圧縮。保存先はカレントディレクトリかenv BACKUP_PROJ_PATH

#!/bin/sh
# usage: backup cpp|cs dir_name
# env: BACKUP_PROJ_PATH

dir_name=${2/\\/} # remove terminating /
case "$1" in
	"cpp")
	  FL=`find "$dir_name" -type f | grep -vE "/Debug/?" | grep -v ".ncb$"`
	;;
	"cs")
	  FL=`find "$dir_name" -type f | grep -vE "/(bin|obj)/?"`
	;;
esac

if [ "$BACKUP_PROJ_PATH" = "" ]; then BACKUP_PROJ_PATH="."; fi
BACKUP_PROJ_PATH=${BACKUP_PROJ_PATH%/}

for f in $FL; do echo $f; done
echo "OK? [Y/n]"
read p
if [ "$p" != "n" ]; then
	filename=$BACKUP_PROJ_PATH/${dir_name}_$(date +%Y%m%d-%H%M).tar.bz2
	echo "$FL" | tar jcvf "$filename" --files-from=-
	echo
	echo $filename
fi

ついでに忘れないようにメモ。tarでパイプから圧縮すべきファイル名を受け取る時は--files-from=-とする。

年/月/日/* 構造のディレクトリを 年/月/日.tar として(非圧縮)tarする

日ごとに溜まっていくログや自動処理の結果、写真などをCD/DVDに焼いて保存するとき、ファイル名の長さがどうとか文字がどうとかうるさいので、tarして誤魔化すスクリプト。

全部一つのアーカイブにするとファイルが壊れたときに損害が大きいため一日単位でまとめます。 日付はYYYY/MM/DD形式のみ対応。

tarは文字コードのことを特に考えてくれません。作ったのと同じ環境で展開しないとファイル名が文字化けします。

#!/bin/sh
# -*- coding: utf-8 -*-
# tree_archive.sh
# 20081213 v1.00 7bit
#
# usage:
#   tree_archive.sh [-v] SRC_DIR BACKUP_DIR FROM_DATE [TO_DATE]
#
# 2008/12/{01,02,03}/{a,b,c} のような構造のディレクトリを
# 2008/12/{01,02,03}.tarのように日ごとにまとめてtar
# DATEは2008, 2008-12, 2008-07-23の何れかの書式。省略した場合はFROMは範囲の先頭、TOは末尾として扱う。
# -v で処理過程を表示

v=0
if [ "$1" = "-v" ]; then
  v=1
  shift 1
fi

if [ "$#" -lt 3 ]; then echo "tree_archive.sh [-v] SRC_DIR BACKUP_DIR FROM_DATE [TO_DATE]"; exit 1; fi
SRC_DIR=$1
BACKUP_DIR=$2
FROM_DATE=$3
if [ "$#" -ge 2 ]; then TO_DATE=$4; else TO_DATE=$(date +%Y-%m-%d); fi

# 年月日を切り出し
YEAR_FROM=$(echo $FROM_DATE|cut -f 1 -d '-')
YEAR_TO=$(echo $TO_DATE|cut -f 1 -d '-')
MONTH_FROM=$(echo $FROM_DATE|cut -f 2 -d '-')
MONTH_TO=$(echo $TO_DATE|cut -f 2 -d '-')
DAY_FROM=$(echo $FROM_DATE|cut -f 3 -d '-')
DAY_TO=$(echo $TO_DATE|cut -f 3 -d '-')

function verbose() {
  if [ "$v" -eq 1 ]; then echo $*; fi
}
verbose "$YEAR_FROM-$MONTH_FROM-$DAY_FROM から $YEAR_TO-$MONTH_TO-$DAY_TO のバックアップ"
verbose "バックアップ元: $SRC_DIR"
verbose "バックアップ先: $BACKUP_DIR"

# 停止
trap 'echo "canceled."; exit 1;' 2

# ------- 年
for year in $(seq $YEAR_FROM $YEAR_TO); do
  iter_month_from="01"
  iter_month_to="12"
  # 最初の年は指定された月以降
  if [ "$year" -eq "$YEAR_FROM" ]; then iter_month_from=$MONTH_FROM; fi
  # 最後の年は指定された月まで
  if [ "$year" -eq "$YEAR_TO" ]; then iter_month_to=$MONTH_TO; fi
  # ------- 月
  for month in $(seq $iter_month_from $iter_month_to); do
    iter_day_from="01"
    iter_day_to="31"
    # 最初の年・月は指定された日以降
    if [ "$year" -eq "$YEAR_FROM" -a "$month" -eq "$MONTH_FROM" ]; then iter_day_from=$DAY_FROM; fi
    # 最後の年・月は指定された日まで
    if [ "$year" -eq "$YEAR_TO" -a "$month" -eq "$MONTH_TO" ]; then iter_day_to=$DAY_TO; fi
    # ------- 日
    for day in $(seq $iter_day_from $iter_day_to); do
      # パス生成、存在すれば処理
      fromdir=$SRC_DIR$(printf '/%02d/%02d/%02d' $year $month $day)
      todir=$BACKUP_DIR$(printf '/%02d/%02d' $year $month)
      tofile=$todir$(printf '/%02d.tar' $day)
      if [ -d "$fromdir" ]; then
        mkdir -p $todir
        cd $fromdir;
        tar cf "$tofile" .
        verbose "$year-$month-$day"
      fi
    done
  done
done

Subversion+tracリポジトリ作成・バックアップ

Subversionとtracのリポジトリを作成する

MySQLのユーザtrac, Apacheのユーザwww-data, svnとtracは/var/svn, /var/trac以下にあると想定しています。


#!/bin/sh
# -*- coding: utf-8 -*-
# create SVN and Trac repos
# Usage: svntrac_createrepos.sh REPOS_NAME

if [ ! $# -ge 1 ]; then
  echo "Usage: svntrac_createrepos.sh REPOS_NAME"
  exit 1
fi

REPOS_NAME=$1

if [ -d ~/svn/$REPOS_NAME -o -d /var/trac/$REPOS_NAME ]; then
  echo "$REPOS_NAME is already in use."
  exit 1
fi

# create svn repos
cd ~svn/
sudo -u svn svnadmin create "$REPOS_NAME"
cd "$REPOS_NAME"
sudo -u svn chmod g+w -R .
sudo -u svn chmod g-w -R conf

# create MySQL db
cat <<END | mysql -uroot -pYOURPASSWORD
create database trac_$REPOS_NAME;
grant all on trac_$REPOS_NAME.* to trac@localhost;
END

# create Trac repos
cd /var/trac
sudo -u www-data mkdir "$REPOS_NAME"
cd "$REPOS_NAME"
sudo -u www-data chown -R www-data:www-data .
sudo -u www-data trac-admin . initenv

echo
echo 'Complete. Now edit /var/trac/$REPOS_NAME/conf/trac.ini'
echo ' default_charset = utf-8'
echo
echo 'and ~svn/$REPOS_NAME/conf/svnserve.conf'
echo
echo 'and add an entry to ~/bin/svn_backup.sh'

Subversionのリポジトリと対応するtracのデータをバックアップ

svn+tracをtar.gzでバックアップします。リポジトリが沢山あると一つ一つcronに書くのは面倒ですが、このスクリプトではREPOSにバックアップするリポジトリの名前を書き足すだけで対応できます。

BACKUP_DIR, REPOSを環境に合わせて変更します。svnは/var/svn、tracは/var/trac以下にあるものとしています。


#!/bin/sh
# -*- coding: utf8 -*-

BACKUP_DIR=/mnt/media1/backup
REPOS="repos1 repos2 repos3 reposN"
DATE=`date +%Y%m%d`
for r in $REPOS; do
  # trac DB
  mysqldump trac_${r} -utrac -pYOURPASSWORD | gzip > $BACKUP_DIR/trac_db_${r}_${DATE}.gz
  # trac
  tar zcf $BACKUP_DIR/trac_${r}_${DATE}.tar.gz /var/trac/$r 2>/dev/null
  # svn
  svnadmin dump /var/svn/$r 2>/dev/null | gzip > $BACKUP_DIR/svn_${r}_${DATE}.gz
done

aptの更新をチェックしてメール送信

管理しているサーバが多いと一々apt-get update && apt-get upgradeするのは面倒です。しかしcronでupgradeも怖いので、updateだけしてみて、更新があればメールを送るスクリプトを書いてみました。


18 20 * * * sh /usr/local/sbin/apt-check.sh 'APT ' to1@example.com to2@example.com
のようにして使います。

届くメールは、

25 apt updates found at www. 2009/03/30-23:02
Linux www 2.6.18-6-amd64 #1 SMP Wed Oct 15 10:07:11 UTC 2008 x86_64 GNU/Linux

Inst dpkg [1.13.25] (1.13.26 Debian:4.0r7/oldstable)
Inst login [1:4.0.18.1-7] (1:4.0.18.1-7+etch1 Debian:4.0r7/oldstable,
Debian-Security:4.0/oldstable)
(略)
のような感じです。もちろん更新すべきパッケージがない場合は何も送信しません。

#!/bin/sh
# apt-check.sh
#
# check updates for apt and mail it to the administrator if any updates exist.
#
# usage:
#  apt-check.sh FROM_MAILADDRESS MAILADDRESS1 [MAILADDRESS2, ...]

if [ $# -lt 2 ]; then
  echo usage:
  echo  apt-check.sh FROM_MAILADDRESS MAILADDRESS1 [MAILADDRESS2, ...]
  exit 1
fi

FROM=$1
shift 1

# super user privilege required
if [ $UID -ne 0 ]; then
        echo "this script should be run as root."
        exit 1
fi

# update
apt-get update > /dev/null
apt-get check > /dev/null

# packages to be updated
INST=$(apt-get -s upgrade | grep -E '^Inst ')

if [ "$INST" == "" ]; then
        # no updates
        exit 0
fi

# build mail body
NUM=$(echo "$INST" | wc | awk '{print $1}')
HOST=$(hostname)
TITLE="$HOST: apt-get updates"
MAILBODY=$(cat <<EOD
$NUM apt updates found at $HOST. $(date '+%Y/%m/%d-%H:%S')
$(uname -a)

$INST
EOD
)

# send mail
for TO in $@; do
        echo "$MAILBODY" | mail -a 'From: '"$FROM" -s "$TITLE" "$TO"
done

svn diffに着色

http://nanabit.net/blog/2008/03/30/svn-diff-colored/で書いた物を再掲。

#!/bin/sh
# svndiff: colored svn diff
# ref: http://www.linux.or.jp/JF/JFdocs/Bash-Prompt-HOWTO-5.html
mod="echo -e e[1;37m"
del="echo -e e[1;31m"
add="echo -e e[1;32m"
new="echo -e e[1;35m"
nor="echo -e e[0m"

echo "Newly created (but not 'svn add'ed):"
svn st | grep "?" | sed -r -e "s/^(.*)$/`$new`\\1`$nor`/"
echo `$mod`"___________________________________________________________________"`$nor`

svn diff "$@" | sed -r -e "s/^(-.*)$/`$del`\\1`$nor`/g;s/^(+.*)$/`$add`\\1`$nor`/g;s/^([@=_].*)$/`$mod`\\1`$nor`/g"
echo `$nor`

*.vmxで使用しているメモリを一覧にする

VMwareで複数の仮想マシンを扱う際、それらのメモリの合計がシステムの空きメモリを越えると同時に起動できなくなってしまいます。 このスクリプトvmx-mem-sum.shは、指定ディレクトリ以下/指定vmxファイルにある仮想マシンが利用するメモリを見やすい一覧にし、合計メモリ使用量を算出します。

vmx-mem-sum.sh vmx-mem-sum.sh をダウンロード

#!/bin/sh
# vmx-mem-sum.sh
#
# calc total memory used in *.vmx files
#
# usage:
#  sh vmx-mem-sum.sh                     # ( search *.vmx in current directory )
#  sh vmx-mem-sum.sh '/var/lib/vmware/'  # ( search *.vmx in /var/lib/vmware   )
#  sh vmx-mem-sum.sh a.vmx               # ( show memory usage for a.vmx       )

SEARCH='.'
if [ $# -ge 1 ]; then
  SEARCH=$1
fi

find "$SEARCH" -name '*.vmx' | (
TOTAL=0
while read LINE; do
  DISPLAYNAME=$(awk '/displayName/{print $3}' "$LINE" | sed 's/"//g')
  MEM=$(awk '/memsize/{print $3}' "$LINE" | sed 's/"//g')
  printf '%15s:  %4s MB (%s)\\n' "$DISPLAYNAME" "$MEM" "$LINE"
  TOTAL=$(($TOTAL + $MEM))
done
echo
echo "total $TOTAL MB"
)

実行結果

% sh ~/bin/vmx-mem-sum.sh
          Haiku:   256 MB (./Haiku/haiku.vmx)
         Debian:   128 MB (./Debian4.0r3/Debian4.0r3.vmx)
 ExternalServer:   128 MB (./ExternalServer/ExternalServer.vmx)
        Windows:   256 MB (./Windows 2000 Professional/Windows 2000 Professional.vmx)
   DebianRouter:   128 MB (./DebianRouter/Ubuntu 64-bit.vmx)
      Debian5.0:   256 MB (./Debian5.0/Ubuntu 64-bit.vmx)
        Windows:   256 MB (./Windows 2000 Professional (2)/Windows 2000 Professional.vmx)
        Windows:  1024 MB (./Windows Server 2008 Enterprise x64 Edition/Windows Server 2003 Enterprise x64 Edition.vmx)
       NISSlave:   256 MB (./NISSlave/NISSlave.vmx)
    Maintenance:   128 MB (./DebianMaintenance/Maintenance.vmx)

total 2816 MB