#
ドキュメント

Document

自分のための備忘録です。

シェルスクリプトの基礎

シェルスクリプト実行方法

  • shコマンドを使用:$ sh ./test.sh
  • .(ドット)コマンドを使用: $ . ./test.sh
  • execコマンドを使用: $ exec {{command}}
  • シェルスクリプトにシバング(#!)を記載 + 実行権付与:例:!# /bin/bash

shを使った実行例

sample.sh

HELLO=hello
echo $HELLO

別プロセスでスクリプトが実行されるため、スクリプト実行後にカレントシェルでsample.shで定義された変数を使用できない。

$ sh ./sample.sh
hello
$ echo $HELLO
// 何も表示されない

.(ドット)を使った実行例

別プロセスを作成せずにカレントシェルのプロセスでスクリプトが実行されるため、スクリプト実行後にカレントシェルでsample.shで定義された変数を使用できる。

変数

基本

ポイントをまとめると以下のとおりとなる。
  1. = を使う
  2. 値の設定時は変数名の先頭に $ を付けない ($ は参照時のみ)
  3. = の前後にスペースを入れない (前後にスペースがあるとエラー)
  4. 型は存在しない (declare コマンドや typeset コマンドで似たようなことができるが推奨はしない)
  5. 変数名の大文字・小文字は区別される (VAR と var は別物)

特に「2」は初心者が犯しがちなミスなので注意すること。

変数を使用する | UNIX & Linux コマンド・シェルスクリプト リファレンス

特殊変数

$0 # シェルスクリプト名
$1~$9 # 1番目~9番目の引数
$* # すべての引数(詳細後述)
$@ # すべての引数(詳細後述)
$# # 引数の数
$? # 直前に実行したコマンドの終了ステータス。0は成功、0以外は失敗
$- # シェルの実行オプション (/bin/bash -opt)
$$ # シェルのプロセスID
$! # 最後に実行したバックグランドプロセスのプロセスID
$_ # 最後に実行したコマンドの最後の引数

引用元:https://www.tohoho-web.com/ex/shell.html#special

$(command)

command出力結果を変数へ代入するためには、$(command)と記載します。

output=$(echo 'abc');echo $output;
// abc

文字

文字 内容
' すべてキャラクタがエスケープされます。
" $で変数を展開します。$, `,\, "はエスケープする必要があります。
クォートなし 要調査

簡単な例1

$ vi script.sh

#!/bin/bash
if [ -f file.txt ]; then
  while read LINE
  do
    echo $LINE
  done < file.txt
fi

script.shを直接実行するために、オーナーの実行権限を許可します(ファイルのデフォルトのパーミッションは多くの場合644なので、実行権限を許可する必要があります)。

$ chmod u+x script.sh

サンプルスクリプトで読み込むファイル(file.txt)を作成します。

$ cat >> file.txt
aaa
bbb
ccc
Ctrl + D

スクリプト実行します。

$ ./script.sh
aaa
bbb
ccc

$ script.shでは実行できません。これは環境変数PATHへ登録しているパスは.(カレントディレクトリ)なので、.の指定が必要なためです。

簡単な例2

#!/bin/bash
if [ -f file.txt ]; then
  total=0
  while read LINE
  do
    total=$(expr $total +  $LINE)
  done < file.txt
fi
echo $total # 現在のシェルはループ内の値をループが抜けても参照できる

file.txt

2
4
6
$ ./script.sh
12

環境変数

環境変数は、すべてのプロセスから参照できる変数です。
環境変数の定義は以下のようにexportを使用します。

$ export Foo=bar

環境変数の一覧は、printenvコマンドで確認できます。

$ printevn

環境変数の例 PATH

変数PATHの値を確認します。

$ echo ${PATH}

.bash_profile

パスを通すとは、シェル設定ファイル(.bash_profile.zshrcで、環境変数PATHにパスを追加することです。   exportで環境変数(この場合はPATH)を更新します。

PATH=$PATH:/path/to/add
export PATH

PATHのTIPS

(1) PATHの優先順位を確認

$ cat /etc/paths
// 優先度の高い順に表示

例えば以下のように、.bash_profileで優先順位を変更できます。

PATH=/path/to/dir:$PATH
export PATH

(2) sourceコマンド

.bash_profileを変更したときは、ログインし直さないと変更が反映されません。
sourceコマンドを使うとログインし直さなくても変更が反映されます。
.コマンドはsourceコマンドとほぼ等価です.

$ source /path/to/.bash_profile
// or
$ . /path/to/.bash_profile

.bash_profile、.bashrc

.bash_profile

PATH=/usr/local/Cellar/git/2.13.3/bin:/usr/local/bin:/usr/local/lib/node_modules/yuidocjs:/usr/local/bin/dot:$PATH
export PATH=$(brew --prefix homebrew/php/php56)/bin:~/.composer/vendor/bin:$PATH
# VAGRANT_HOME を設定
export VAGRANT_HOME=/Volumes/production/s_hiroshi/.vagrant.d
eval "$(rbenv init -)"
source ~/.phpbrew/bashrc
test -r ~/.bashrc && . ~/.bashrc

.bashrc

alias ctags='/usr/local/Cellar/ctags/5.8/bin/ctags'
alias lsa='ls -al'

本当に正しい .bashrc と .bash_profile の使ひ分け - Qiita

export

// input.txt
abc
def
ghi
THIS="LINE is"
export THIS
while read LINE
do
  echo ${THIE} ${LINE}  // 子プロセスで実行。exportしてない変数は使えません。
done < input.txt
LINE is abc
LINE is def
LINE is ghi

変数一覧を表示

$ MYNAME='Hiroshi'
$ set | grep MYNAME

if文

DB="mysql";
if [[ $DB == "mysql" ]]; then
  mysql -u root myapp_test < ${TRAVIS_BUILD_DIR}/bin/infotownlinkwp.sql;
fi
if [[ $DB == "pgsql" ]]; then 
  psql myapp_test < ${TRAVIS_BUILD_DIR}/bin/infotownlinkwp.sql;
fi

[ ... ]はtestの糖衣構文。...の前後に半角スペースが必要。

[[ ... ]]はbash専用の言語構造で[ ]より高機能。

[ ... ] で使用できる表記。

( exp ) # exp をグルーピング
! exp # exp が偽であれば
exp1 -a exp2 # exp1 かつ exp2 であれば
exp2 -o exp2 # exp1 または exp2 であれば
str1 = str2 # 文字列 str1 と str2 が等しければ
str1 != str2 # 文字列 str1 と str2 が等しくなければ>
-z str # 文字列 str が0文字であれば(zero)
-n str # 文字列 str が0文字で以上であれば(not zero)
str # 文字列 str が0文字で以上であれば(-n str と同じ)
num1 -eq num2 # 数値 num1 が num2 と等しければ(equal)
num1 -ne num2 # 数値 num1 が num2 異なっていれば(not equal)
num1 -ge num2 # 数値 num1 が num2 以上であれば(grater than or equal)
num1 -gt num2 # 数値 num1 が num2 より大きければ(grater than)
num1 -le num2 # 数値 num1 が num2 以下であれば(less than or equal)
num1 -lt num2 # 数値 num1 が num2 より小さければ(less than)
file1 -ef file2 # ファイル file1 が file2 と同一実体であれば(equal file)
file1 -nt file2 # ファイル file1 が file2 より新しければ(newer than)
file1 -ot file2 # ファイル file1 が file2 より古ければ(older than)
-e file # ファイル file が存在していれば
-s file # ファイル file が0バイト以上であれば
-f file # ファイル file がレギュラーファイルであれば
-r file # ファイル file が読み込み可能であれば
-w file # ファイル file が書き込み可能であれば
-x file # ファイル file が実行可能(ディレクトリの場合は移動可能)であれば
-d file # ファイル file がディレクトリであれば
-h file # ファイル file がシンボリックリンクファイルであれば(-Lと同義)
-L file # ファイル file がシンボリックリンクファイルであれば(-hと同義)
-b file # ファイル file がブロックデバイスファイルであれば
-c file # ファイル file がキャラクタデバイスファイルであれば
-p file # ファイル file が名前付きパイプであれば
-S file # ファイル file がソケットファイルであれば
-k file # ファイル file にスティッキービットが設定されていれば(chown o+t)
-u file # ファイル file にセットユーザIDビットが設定されていれば(chown u+s)
-g file # ファイル file にセットグループIDビットが設定されていれば(chown g+s)
-O file # ファイル file が実効ユーザIDに所有されていれば
-G file # ファイル file が実効グループIDに所有されていれば
-t fd # ファイルディスクリプタ fd がターミナルとして開かれていれば

引用元:https://www.tohoho-web.com/ex/shell.html#if-statemant

for in

#!/bin/bash
total=0
for x in 100 200 300
do
    total=$(expr $total + $(expr $x \* 2))
done
echo $total
#!/bin/bash
total=0
for x in 100 200 300
do
    total=`expr $total + \`expr $x \* 2\``
done
echo $total

while/read

// input.txt
abc
def
ghi
while read LINE
do
  echo $LINE
done < input.txt

環境構築

env.sh

#!/bin/bash
wget http://rpms.famillecollet.com/enterprise/remi-release-6.rpm
rpm -ivh remi-release-6.rpm
ls /etc/yum.repos.d
yum yum install httpd
yum -y -enablerepo=remi-php70 --disablerepo=amzn-main install php
yum -y --enablerepo=remi-php70 --disablerepo=amzn-main install php-pdo php-mysqlnd php-mbstring
$ chmod o+x env.sh

サンプル集

ディレクトリのファイル一覧を取得

#!/bin/bash
for f in `find . -maxdepth 1 -type f`
do
    echo $f
done

ディレクトリ内のファイルサイズを取得

#!/bin/bash
for f in  `find . -maxdepth 1 -type f`
do
  wc -c < $f
done

ディレクトリサイズ表示

#!/bin/bash
for d in  `find . -maxdepth 1 -type d`
do
  du $d
done

文字列結合

#!/bin/bash

lines=''
while read line
do
   lines=$lines$line 
done < file.txt
echo $lines

file.txt

aaa
bbb
ccc

実行

$ ./script.sh
aaabbbccc

expr

#!/bin/bash
total=0
for x in 100 200 300
do
    total=`expr $total + \`expr $x \* 2\``
done
echo $total

Travis CI

before_script:
  - bash bin/install-wp-tests.sh wordpress_test root '' localhost $WP_VERSION
  - export PATH="$HOME/.composer/vendor/bin:$PATH"
  - |
    if [[ ${TRAVIS_PHP_VERSION:0:2} == "7." ]]; then
      composer global require "phpunit/phpunit=5.7.*"
    else
      composer global require "phpunit/phpunit=4.8.*"
    fi
  - |
    composer global require wp-coding-standards/wpcs
    phpcs --config-set installed_paths $HOME/.composer/vendor/wp-coding-standards/wpcs

script:
  - npm install
  - grunt build
  - phpcs --standard=phpcs.ruleset.xml $(find . -name '*.php')
  - phpunit

after_success: bash ./bin/build-wp.sh

install-wp-tests.sh

#!/usr/bin/env bash

if [ $# -lt 3 ]; then
    echo "usage: $0 <db-name> <db-user> <db-pass> [db-host] [wp-version] [skip-database-creation]"
    exit 1
fi

DB_NAME=$1
DB_USER=$2
DB_PASS=$3
DB_HOST=${4-localhost}
WP_VERSION=${5-latest}
SKIP_DB_CREATE=${6-false}

WP_TESTS_DIR=${WP_TESTS_DIR-/tmp/wordpress-tests-lib}
WP_CORE_DIR=${WP_CORE_DIR-/tmp/wordpress/}

download() {
    if [ `which curl` ]; then
        curl -s "$1" > "$2";
    elif [ `which wget` ]; then
        wget -nv -O "$2" "$1"
    fi
}

if [[ $WP_VERSION =~ [0-9]+\.[0-9]+(\.[0-9]+)? ]]; then
    WP_TESTS_TAG="tags/$WP_VERSION"
elif [[ $WP_VERSION == 'nightly' || $WP_VERSION == 'trunk' ]]; then
    WP_TESTS_TAG="trunk"
else
    # http serves a single offer, whereas https serves multiple. we only want one
    download http://api.wordpress.org/core/version-check/1.7/ /tmp/wp-latest.json
    grep '[0-9]+\.[0-9]+(\.[0-9]+)?' /tmp/wp-latest.json
    LATEST_VERSION=$(grep -o '"version":"[^"]*' /tmp/wp-latest.json | sed 's/"version":"//')
    if [[ -z "$LATEST_VERSION" ]]; then
        echo "Latest WordPress version could not be found"
        exit 1
    fi
    WP_TESTS_TAG="tags/$LATEST_VERSION"
fi

set -ex

install_wp() {

    if [ -d $WP_CORE_DIR ]; then
        return;
    fi

    mkdir -p $WP_CORE_DIR

    if [[ $WP_VERSION == 'nightly' || $WP_VERSION == 'trunk' ]]; then
        mkdir -p /tmp/wordpress-nightly
        download https://wordpress.org/nightly-builds/wordpress-latest.zip  /tmp/wordpress-nightly/wordpress-nightly.zip
        unzip -q /tmp/wordpress-nightly/wordpress-nightly.zip -d /tmp/wordpress-nightly/
        mv /tmp/wordpress-nightly/wordpress/* $WP_CORE_DIR
    else
        if [ $WP_VERSION == 'latest' ]; then
            local ARCHIVE_NAME='latest'
        else
            local ARCHIVE_NAME="wordpress-$WP_VERSION"
        fi
        download https://wordpress.org/${ARCHIVE_NAME}.tar.gz  /tmp/wordpress.tar.gz
        tar --strip-components=1 -zxmf /tmp/wordpress.tar.gz -C $WP_CORE_DIR
    fi

    download https://raw.github.com/markoheijnen/wp-mysqli/master/db.php $WP_CORE_DIR/wp-content/db.php
}

install_test_suite() {
    # portable in-place argument for both GNU sed and Mac OSX sed
    if [[ $(uname -s) == 'Darwin' ]]; then
        local ioption='-i .bak'
    else
        local ioption='-i'
    fi

    # set up testing suite if it doesn't yet exist
    if [ ! -d $WP_TESTS_DIR ]; then
        # set up testing suite
        mkdir -p $WP_TESTS_DIR
        svn co --quiet https://develop.svn.wordpress.org/${WP_TESTS_TAG}/tests/phpunit/includes/ $WP_TESTS_DIR/includes
        svn co --quiet https://develop.svn.wordpress.org/${WP_TESTS_TAG}/tests/phpunit/data/ $WP_TESTS_DIR/data
    fi

    if [ ! -f wp-tests-config.php ]; then
        download https://develop.svn.wordpress.org/${WP_TESTS_TAG}/wp-tests-config-sample.php "$WP_TESTS_DIR"/wp-tests-config.php
        # remove all forward slashes in the end
        WP_CORE_DIR=$(echo $WP_CORE_DIR | sed "s:/\+$::")
        sed $ioption "s:dirname( __FILE__ ) . '/src/':'$WP_CORE_DIR/':" "$WP_TESTS_DIR"/wp-tests-config.php
        sed $ioption "s/youremptytestdbnamehere/$DB_NAME/" "$WP_TESTS_DIR"/wp-tests-config.php
        sed $ioption "s/yourusernamehere/$DB_USER/" "$WP_TESTS_DIR"/wp-tests-config.php
        sed $ioption "s/yourpasswordhere/$DB_PASS/" "$WP_TESTS_DIR"/wp-tests-config.php
        sed $ioption "s|localhost|${DB_HOST}|" "$WP_TESTS_DIR"/wp-tests-config.php
    fi

}

install_db() {

    if [ ${SKIP_DB_CREATE} = "true" ]; then
        return 0
    fi

    # parse DB_HOST for port or socket references
    local PARTS=(${DB_HOST//\:/ })
    local DB_HOSTNAME=${PARTS[0]};
    local DB_SOCK_OR_PORT=${PARTS[1]};
    local EXTRA=""

    if ! [ -z $DB_HOSTNAME ] ; then
        if [ $(echo $DB_SOCK_OR_PORT | grep -e '^[0-9]\{1,\}$') ]; then
            EXTRA=" --host=$DB_HOSTNAME --port=$DB_SOCK_OR_PORT --protocol=tcp"
        elif ! [ -z $DB_SOCK_OR_PORT ] ; then
            EXTRA=" --socket=$DB_SOCK_OR_PORT"
        elif ! [ -z $DB_HOSTNAME ] ; then
            EXTRA=" --host=$DB_HOSTNAME --protocol=tcp"
        fi
    fi

    # create database
    mysqladmin create $DB_NAME --user="$DB_USER"$EXTRA
}

install_wp
install_test_suite
install_db

build-wp.sh

#!/usr/bin/env bash

if [[ "false" != "$TRAVIS_PULL_REQUEST" ]]; then
    echo "Not deploying pull requests."
    exit
fi

if [[ "master" != "$TRAVIS_BRANCH" ]]; then
    echo "Not on the 'master' branch."
    exit
fi

if if [[ ${TRAVIS_PHP_VERSION:0:3} != "5.6" ]]; then
    exit;
fi

cd dev/build

git init
git config user.name "s-hiroshi"
git config user.email "info@info-town.jp"
git add .
git commit --quiet -m "Push from Travis CI"
git push --force --quiet "https://${GH_TOKEN}@${GH_REF}" master:build > /dev/null 2>&1

データベースバックアップ

#!/bin/sh

# バックアップ先ディレクトリ
# BKDIR=/var/www/minker/backup/database/full
BKDIR=/data/minker/backup/database/full

# 本スクリプトパス
MYPATH=$(cd $(dirname $0);pwd)

#世代管理(何日分残すか)
DAYAGO="31"

# MySQユーザー
USER=minker

# MySQL minkerパスワード
PASS=Ma0em1007R

# 日付・時刻取得し変数に代入
TIMESET=`date +%Y%m%d-%H%I%M`

#バックアップ先ファイル名を設定 設定ファイルにあるファイル名+日付.tar.gzする
BKFILE=${BKDIR}/mysql.$TIMESET.tar.gz

# データベース名取得
DBNAME=minker

# DBNAMEデータベースをmysqldumpでバックアップ
# $?は最後に実行したコマンドの戻り値
mysqldump --user=${USER} --password=${PASS} $DBNAME --opt  > ${BKDIR}/$(date "+${DBNAME}-%Y%m%d%H%M%S").sql | logger -t mysqldump
if [ $? = 0 ]; then
  logger -t mysqldump "Backup OK mysqldump:${DBNAME}"
else
# 失敗したらその旨を表示かつ、ログに保存、メール送信
  logger -t mysqldump "Backup NG mysqldump:${DBNAME}"
fi

# yyyymmdd形式のファイルから古い物を削除
# 削除しきい日付(yyyymmdd)を設定。(DAYAGO日前の日付求める)
tday=`date -d "${DAYAGO} day ago" +%Y%m%d`

# 対象Dir配下のファイル名をwhileに渡し「$list」となる
ls $BKDIR|while read list
do
   #ファイル名に「数字x14」が含まれている場合、
   if  expr "$list" : "minker\-[0-9]\{14\}.sql" > /dev/null; then
      # $listから、「yyyymmdd」を取り出しepoch時間($fday)にする
      fday=`expr $list : "minker\-\([0-9]\{8\}\)[0-9]\{6\}.sql"`
      #ファイル名から取得した「yyyymmdd」がDAYAGOの「yyyymmdd」よりも小さいとき削除
      if [ -n "$fday" ] && [ $fday -lt $tday ]; then
         # echo する
         # echo $list
         #削除する
         `sudo rm -f ${BKDIR}/${list}`
      fi
   fi
done