bash
おドン
bash
準備中
準備中
準備中

改行

l\
s

出力を表示しない

echo 'Hello' > /dev/null

エラーを表示しない.

echo 'Hello' 2> /dev/null

出力とエラーどちらも表示しない.

echo 'Hello' > /dev/null 2>&1

終了ステータス

bashでは0が真

trueやfalseの後に$?を参照すると, 終了ステーテスが得られる.

何か コマンドを実行した後に$?を参照すると終了ステータスがわかる.

cat hoge.txt

echo $?

#>0

! cat hoge.txt

#>1(逆になる.)

echo $?

パイプライン

コマンド1 | コマンド2

コマンド1の出力をコマンド2に渡して実行する.

&&リスト

コマンド1 && コマンド2

コマンド1が実行され, 終了ステータスが0なら, コマンド2が 実行される

cmp -s file1 file2 && {

echo '重複ファイルfile2を削除します.'

rm -f file2

}

||リスト

コマンド1 || コマンド2

どちらかが真なら, 終了ステータスが真になる.

つまり, コマンド1が真なら, コマンド2は実行されない.

test -f file1 || {

echo 'file1が存在しません'

exit 1

}

if文

if [ "$i" -eq 3 ]

then

echo "3です"

elif [ "$i" -eq 5 ]

then

echo "5です"

else

echo "3,5以外です."

fi

case

case文ではinの後ろに何も書かない. caseの後ろはvarではなく$varとしなければならない

case $1 in

start)

echo 'start'

;;

stop)

echo 'stop'

;;

*)

;;

esac

////////////////////////////

コマンドを使うこともできる. コマンドは「`」(@+shift)で囲む.

case `uname -s` in

Linux|FreeBSD)

echo 'osはLINUXかFreeDOSです'

;;

*)

echo 'そのほかのOSです'

;;

esac

リストの継続実行//エラーが出て使えない.

case $var in

one)

echo '$varがoneの場合'

;&

two)

echo '$varがoneまたはtwoの場合'

;;

esac

パターンの継続探索//エラーが出て使えない

case $var in

one)

echo '$varがoneの場合'

;;&

two)

echo '$varがtwoの場合'

;;&

*)

echo '全ての場合'

;;

for文

カレントディレクトリの全てのファイルについて行う

もちろん*ではなく*.txtなどとしても良い

for file in *

do

cp -p "$file" "$file".bak

done

シェルスクリプトの引数の文だけループ

for file in "$@"

do

cp -p "file" "file".bak

done

コマンド置換を使ったfor文

filelinstというファイルに以下のようなファイル名を箇条書きにしたものを用意する.

memo.txt

prog.c

figure.png

hoge.txt

コマンド置換を使ったfor文

for file in ´cat filelinst´

do

cp -p "file" "$file".bak

done

continueを使う方法

for文の中で組み込みコマンドcontinueを実行すると, その回のループの残りの部分を実行せず, 次の回のループに進む.

すでにbakというファイルがある時, コピーしない

for file in *

do

case $file in

*.bak)

continue

;;

esac

cp -p "file" "file".bak

done

値として特殊な意味を持つ記号を使う場合は, クォートが必要

for i in ';' '\' '('

算術式のfor文

2重かっこで囲まなければならない.

sum = 0

for ((i=1; i <100; i++)) {

((sum += i))

}

echo "$sum"

while文

while文ではexprコマンドでよく加算する

そのほかにシェルの位置パラメータをシフトさせるshiftコマンドが使用されることもよくある

i=´expr "$i" + 1´は((i++))という表現でも良い

sum=0

i=1

while [ "$i" -le 100 ]

do

sum =´expr "$sum" +"$i"´

i=´expr "$i" + 1´

done

echo "$sum"

シェルスクリプトの引数の分だけループ

シェルスクリプトの引数の個数が入っている特殊パラメータ$#の値が「0」になるまでループを繰り返す.

次のループのた目にshiftコマンドで引数をずらす.

$#の値はshiftのたびにデクリメント(-1)されることに注意

while [ $# -gt 0 ]

do

cp -p "$1" "$1".bak

shift

done

while文の中にcase文を記載

while文+case文+shiftでオプションの処理を行うことが常套手段となっている。

suffix=.bak

dir=.

while [ $# -gt 0 ]

do

case $1 in

-s)

suffix="$2"

shift

;;

-d)

dir="$2"

shift

;;

*) break

;;

esac

shift

done

while [ $# -gt 0 ]

do

cp -p "$1" "$dir"/"$1""$suffix"

shift

done

select文

選択メニューを表示し, ユーザの入力を受け付ける

breakコマンドが実行されるか, 標準入力がEOFになると終了

通報はCtrl + D を入力すると標準入力がEOFになる

PS3='コマンド?'

select cmd in up down left right look quit

do

case $cmd in

up)

echo '上に移動しました' ;;

right)

echo '下に移動しましt' ;;

look)

echo 'アイテムが落ちています' ;;

quit)

echo '終了します'

break;;

*)

echo "$REPLY"'というコマンドはありません';;

esac

echo

done

サブシェル

カレントティれクトリの一時変更や, シェル変数の局所的な使用などの, もとのシェルの状態には影響を及ぼさずに, 一定の処理を行いたい場合, その部分のリストを()で囲んで, リストをサブシェルで実行させるようにする. サブシェルは, もとのシェルとは別の子プロセスになるため, 子プロセス状のカレントディレクトリやシェル変数などが変化しても, もとのシェルには影響しません.

サブシェル内では, シェル変数への代入のほか, シェル変数やその他のパラメータの操作に関する, export/read/readonly/set/shift/unsetなどのコマンドの効果がサブシェル内のみになります.

そのほか, cd/umaskコマンドについても, 影響がサブシェル内だけになり, exec/exitコマンドではもとのシェル状での動作とは異なる動作になります.

サブシェルの標準出力のリダイレクト

(

cd /some/dir

pwd

ls -l

) > logfile

グループコマンド

複数のコマンド(パイプライン)を改行や; などでつなげばリストになり, リストはそのままif文/for文など の構文の要素になれます. しかし, リスト全体のまままとめてファイルにリダイレクトしたり, パイプに通してりしたい場合, リスト全体をいったん1つのコマンドとして, まとめる必要がある.

{}で囲むことにより, 全体が一つのコマンドになる.

{

uname -a
date
who

} > logfile

これは,
uname -a >logfile
date >> logfile
who >> logfile
という野暮ったい書式を簡潔に表現できる.

引数を再度解釈し, コマンドを実行する.

day=Monday
today=day
eval echo \"\$$var\"
まず, シェルに一回解釈されて, echo "$day"となり, echo Mondayになる.

シェル関数

例えば, 再起をつかって階乗を計算する.
#!/bin/bash

func()

{

if [ "$1" -le 1 ]

then

echo 1

return

fi

n=`expr "$1" - 1`

n=`func "$n"`

expr "$n" * "$1"

}

func "$1"

ローカル変数

func()
{
local i
i=5
}

算術評価

少数は無理

(( ))で囲まれた部分は, 算術式とみなされ, 演算子を使った評価ができる. シェル変数は$の記号をつけずに参照できる.ほぼc言語と同じ演算子が使える。

条件式の評価

if [[ "$i" -le 3 || "$i" -le 5 ]]

then

echo 'iの値は3か5です.'

fi

&&, ||, ==, !=という演算子が[[]]を2重にすることで使えるようになる.

何もしないで単に0の終了ステータスを返す.「:」

:${1?'引数を指定して下さい'} #引数を指定しなければ, エラーメッセージ
: >file #新規ファイル作成
他には, if文でelseのみを実行したい時に使ったりする.

パラメータのデフォルトのデフォルトの値を指定する

cp file "${$1:=/tmp}

これは, 以下と同様

if [ -n "$1" ]

then

cp file "$1"

else

cp file /tmp

fi

パラメータにデフォルトの値を代入する

cp file "${TMPDIR:=/tmp}

これは, 以下と同様

if [ -z "$TMPDIR ]

then

fi

cp file "$TMPDIR"

以下とも同様

: ${TMPDIR:=/tmp}

cp file "$TMPDIR"

パラメータが設定されていれば, 指定の値に展開する.

${var:+値}

パラメータ未設定時にエラーメッセージを出してシェルスクリプト を終了させる

引数1で指定されたファイルを/tmpにコピーし, 引数1が未設定または, 空文字列の場合はエラーメッセージを出す

cp "${1:?ファイルが指定されていません}" /tmp

これは以下と同様

if [ -z "$1" ]

then

echo 'ファイルが指定されていません'

exit 1

fi

cp "$1" /tmp

パラメータの文字列の長さを求める

echo ${#var}

パラメータの値の文字列の左側から一定のパターンを取り除く

${パラメータ#パターン}

パラメータの値の文字列の左側からパターンに一致する最短の文字列が取り除かれる

${パラメータ##パターン}

パラメータの値の文字列の左側からパターンに一致する最長の部分が取り除かれる.

拡張子だけを取り出す

F_NAME="hoge.txt"

echo "${F_NAME##*.}"

#>txt

パラメータの値の文字列の右側から一定のパターンを取り除く

パラメータの値の文字列の右側からパターンに一致する最短の部分が取り除かれる.

${パラメータ%パターン}

パラメータの文字列の右側からパターンに一致する最良の部分が取り除かれる.

${パラメータ%%パターン}

拡張子を取り除く

F_NAME="hoge.txt"

echo "${F_NAME%.*}"

#>hoge

オフセットや長さを指定してパラメータの値の文字列を切り出す.

シェル変数varの左側の2文字を除いて残り5も自分を表示

echo "${var:2:5}

シェル変数varの左側の2文字を除く

echo "${var:2}

パターンを指定してパラメータの値の文字列を置換する

パラメータの値の文字列の左側から見て最初にパターンに一致した文字列の部分が置換文字列に置換される

${パラメータ/パターン/置換文字列}

パラメータの値の文字列の左側から見てパターンに一致した文字列の部分全てが置換文字列に置換される

${パラメータ//パターン/置換文字列}

指定した文字列で始める変数名を一覧表示する

Pで始まるシェル変数名を全てリストする

echo ${!P*}またはecho ${!P@}

間接参照${!パラメータ}

パラメータの値をパラメータ名とみなし, さらにその値を参照する

message=hello

var=message

echo "${!var}"

#>hello

シングルクォート

echo '$5は5ドルの意味です'

文字列をシングルクォートで囲むと, 文字列の各文字全てが特殊な意味を失い, 文字通りの意味として解釈される. ただし, シングルクォート自体は含めることはできない.

文字列中でシングルクォートを使いたい場合は, いったんシングルクォートを抜けて\'を使う

echo 'Let'\''s go !'

ダブルクォート

文字列をダブルクォートで囲むと$, ´, \を除き, 文字列の各文字が特殊な意味を失う

シェル変数などんパラメータ展開の際に, 単に$varのようにして参照すると, シェル変数の値に含まれるスペースや*などの文字列が解釈されてしまう. これを避けるためにシェル変数の参照時に, "$var"のようにダブルクォートで囲むのが基本.

コマンド置換

コマンド置換では, バッククォート(``)で囲まれたリストが実行され、この結果, 標準出力に出力された文字列で, ``の部分が置き換えられる. 標準出力の最後に改行コードが付いている場合は, 取り除かれる.

$, ´, \の直前の\については特別に解釈される

i=3

echo "$i"

#>3

i=`expr "$i" + 1`

#>4

$( )

`リスト`と$(リスト)は同じ

ただし, ``によるコマンド置換と異なり, $( )中は, \\, \$, \`が特別に解釈されることがない.

*

0文字以上の任意の文字列

?

任意の一文字

[]

指定した条件の一文字

[123]

[a-z]

[!a-d] #abcd以外の1文字
[a-zA-Z0-9_-] #全ての英数字, アンダースコア, ハイフン

複数の文字列の組み合わせから文字列を生成する

cp /some/dir/{cat,dog}.jpg .

/some/dirの下にあるcat.jpg, dog.jpgをカレントディレクトリにコピー

パス展開とは異なり, 現在のパス名との照合は行われません.

エラーメッセージのリダイレクト 2>

エラーメッセージを出さずに/some/dirというディレクトリを削除する

rmdir /some/dir 2> /dev/null

読み書き両用open

cat <> file

ヒアストリング

ヒアドキュメントよりも単純に記述できる.
cat <<< 'hello world >file.txt
hello world2
hello world3'

ヒアドキュメント

コマンド <<[-] 'EOF'

ヒアドキュメント本体

EOF

ヒアドキュメントによるメールの送信

nkf -j << 'EOF' | mail guest@example.com

このメールは

ヒアドキュメントによる

メールの送信です

EOF

#nkfは文字コードと改行コードを変換するためのコマンド-jはjis, -eはeuc

ファイル名からディレクトリ名の部分や拡張子を取り除く

basename ファイル名 .拡張子

name=`basename /some/dir/memo.txt .txt`

echo "$name"

#>memo

拡張子のリネーム

for file in *.txt

do

name=´basename "file" .txt´

mv "$name".txt "$name".html

done

ファリル名の部分からそのディレクトリ名部分を取り出す

dirname ファイル名

配列

array[3]='hello world'

echo "${array[3]}"

#>hello world

i=4

echo "array[i-1]"

#>hello world

配列の一括代入, 一括参照

array=(one two three)

echo "${array[@]}"

#>one two three

配列を配列に一括代入

array2=("${array1[@]}")

ファイル名に日付文字列を含ませせる

mycomand > log`date +%Y%m%d%H%M%S`

%Y 年

%m 月

%d 日

%H 時

%M 分

%S 秒

ファイルを1行ずつ読んでループする

ファイルから1行ずつ読み込み, その内容を解釈しつつループするには, while readの形式を使う. readコマンドは標準入力から1行ずつシェル変数に読み込み, 入力がEOFになると偽(1)の終了ステータスを返す. よって, while readの形でループを記述すれば, 1行ごとにファイル全体にわたってループすることができる.

readコマンドの引数に複数のシェル変数を指定すると, シェル変数IFSの値を区切り文字として単語に分割された結果が複数のシェル変数に分割して代入されるため, これを利用して簡単な字句解析ができる.

/etc/hostsを1行ずつ読み込みながらループし, シェルスクリプトの引数で指定されたIPアドレスに一致する行を見つけたらそのホスト名部分を表示して終了するシェルスクリプト

case $# in

1)

;;

*)

echo "Usage: $0 ip_adress" 1>&2

exit 1

;;

esac

#ファイル記述子「3」を使って/etc/hostsを読み出しオープン

exec 3< /etc/hosts

#ファイル記述子3から1行ずつ読み込んでループ

while read ip host 0<&3

do

case $1 in

$ip)

echo "$host"

exit 0

;;

esac

done

#ファイルクローズ, 省略可能

exec 3<&-

#一致するIPアドレスが見つからなかった

echo "&1 not found" 1>&2

exit 1

while文全体に直接リダイレクトする方法

case $# in

1)

;;

*)

echo "Usage: $0 ip_adress" 1>&2

exit 1

;;

esac

while read ip host

do

case $1 in

$ip)

echo "$host"

exit 0

;;

esac

done < /etc/hosts

#一致するIPアドレスが見つからなかった

echo "&1 not found" 1>&2

exit 1

シェルスクリプト の引数につけられたオプションを解析する.

-c, -i, -v 変数, -o 変数を解析する

#!/bin/bash
while getopts civ:o: option

do

case $option in

c)

echo '-cオプションが指定されました';;

v)

echo '-vオプションが指定されました';;

i)

echo '-iオプションで'"$OPTARG"'が指定されました.';;

o)

echo '-oオプションで'"$OPTARG"'が指定されました.';;

#不正なオプション
\?)

echo "Usage: $0 [-c] [-v] [-i file] [-o file] [args...]" 1>&2

exit 1;;

esac

done

#OPTINDから1を引いた数だけ一パラメータをshift
shift `expr "$OPTIND" - 1`

if [ $# -ge 1 ]; then

echo 'オプション以外の引数は'"$@"'です'

else

echo 'オプション以外の引数はありません'


fi

標準入力をシェル変数に読みこむ

echo -n 'prompt> ' 1>2&

read input

testコマンドで使用可能な演算子

数値の比較

意味      if文で使う例

n >= y      if [ n -ge y ]; then

n <= y      if [ n -le y ]; then

n > y       if [ n -gt y ]; then

n < y       if [ n -lt y ]; then

n = y       if [ n -eq y ]; then

n != y      if [ n -ne y ]; then

文字列の比較

if [ "n" = "y" ]; then

if [ "n" != "y" ]; then

ディレクトリ/ファイルのテェック

パスがファイルか if [ -f path ]; then

パスがディレクトリか if [ -d path ]; then

Aファイルが存在するか if [ -e fileA ]; then

Aファイルが空じゃないか if [ -s fileA ]; then

Aファイルが読取可能か if [ -r fileA ]; then

Aファイルが書込可能か if [ -w fileA ]; then

Aファイルが実行可能か if [ -x fileA ]; then

複数条件

条件Aかつ条件B if test [ A ] -a [ B ]; then

条件Aまたは条件B if test [ ... ] -o [ ... ]; then

cutコマンド

「cut」は、ファイルを読み込んで、それぞれの行から指定した部分だけを切り出すコマンドです。
-d文字 #フィールドの区切り文字として, タブの代わりに使用する文字を指定する.

コントロールキー

ctrl+c #現在のキーを終了する
ctrl+d #入力を終了する.
ctrl+\ #ctrl+cが効かない場合に, 現在のコマンドを終了する.
ctrl+s #画面への出力を停止する
ctrl+q #画面への出力を再開する
dellまたは, ctrl+? #最後の文字を削除する
ctrl+u #コマンドライン全体を削除する
ctrl+z #現在のコマンドを一時停止する
ctrl+d #前方削除

esc+b #後方に1ワード移動
esc+f #前方に1ワード移動
esc+del #後方に1ワード削除
esc+d #前方に1ワード削除
ctrl+y #最後に削除されたものを取り消す

ctrl+a #コマンドラインの先頭へ移動
ctrl+e #コマンドライン末尾へ移動
ctrl+k #コマンドラインの末尾まで削除

環境のカスタマイズ

ホームディレクトリにある3つのファイルは, bashにとって特別な意味を持つ.
.bash_profile, .bash_logout, .bashrc
.bash_profileを変更する際は, 既存の行の後ろに付け加えるようにしよう.
alias 名前=コマンド .bash_logoutはログアウト時に一時ファイルを削除したり, システムにログインしていた時間を記録したりするコマンドを実行したい場合に設定する

シェルスクリプトのコメント

ヘッダー #################################### # # Name: # 目的 # # Usage: 使い方 # # Date ######################################

定数には大文字を使用し, 通常は読み取り専用として, 宣言する

declare -r CAPIRTAL_OF_ENGLAND="London"

コードをマジックナンバーだらけにするのは良くない

if [[$prosess_result == 68 ]] 
次のように書き換える
declare -ir STAGE_3_FAILURE=68
if [[$prosess_result == STAGE_3_FAILURE ]]

関数の中で変数を代入すると値は関数外でも変わる

function afunc
{
var1="in function"
echo $var1
}
var1="out function"
echo $var1
afunc
echo $var1
# 実行結果
# out function
# in function
# in function

関数内での変数の代入を関数外で反映させないためには localを使う

function afunc { local var1="in function" }

10より大きい番めの位置パラメータ

${10}としなければならない

置換演算子

${variable:-word} #variableが存在すればその値を返す. 存在しなければwordを返す. variable自体に代入されるわけではない
${variable:=word} #variableが存在すればその値を返す. 存在しなければvariableにwoedを代入して返す. variable自体に代入される.
${variable:?message} #variableが存在し, かつ, nullでない場合にその値を返す. それ以外の場合はvariable:に続いてmessageを出力し, 現在のコマンドスクリプトを終了する
${variable:+word} #variableが存在し, nullでない場合にwoedを返す.
${variable:offset:length} #$variableの値からoffsetのいちからlength文字の長さの部分文字列を取り出す


パターン

${variable/pattern/string} #variableの値でpatternと最初に一致した最も長く一致した部分をstringと置換する. ${variable//pattern/string} #variableの値でpatternと最も長く一致した部分全てがstringに置換される. stringがnullの場合は一致した部分が削除される. variableが@または*の場合には, 位置パラメータが順番に処理され, 展開結果がそのリストとなる.

型を持つ変数

val1=2
val2=3
declare -i result #整数型
result=val1*vcal2
echo "$result" #>12

算術, 整数

$(())で囲むと算術演算式として解釈される
echo "only $(( (365-$(date +%j)) /7))" week until the New Year"

条件式

エスケープされたかっこ\( \)を使わなければならない
[ \( 3 -gt 2 \) && \( 4 -le \) ]
これは以下と同じ
[ $(((3 > 2 )) && (4 <= 1)))=1 ]

デバッグ

最も簡単なのはechoで出力すること

set -oコマンド
$ set -o xtrace
なんか色々あるけど理解できなかった

until

whileと条件判断が逆.
until [ ]

print

i=345
print '数字は%dです.\n' "$i"

言語の一時変更

LANG=C date

ホームディレクトリが設定されているシェル変数

$HOME
チルダ展開 ~ と同じ.

sleep

15秒間停止
sleep 15

.tar.gz/.tar.bz2自動展開

#!/bin/sh
dir=${2-/tmp} #省略時はtmpの中に作成される.
case $1 in
*.tar.gz|*.tar.Z)
tar zxvf "$1" -C "$dir"
;;
*.tar.bz2)
tar jxvf "$1" -C "$dir"
;;
*)
echo "$1"'の展開方法が不明です.' 1>&2
exit 1
;;
esac

プログレスバー

Copyright (C) 2017 Odon  All Rights Reserved.