少し応用なbashシェルスクリプト | 2025.02.02 |
引数があるかチェック |
「$#」はパラメータ(引数)の個数を示す。
C言語のargcとは違い、コマンドそのものは数えない。
C言語のargcとは違い、コマンドそのものは数えない。
if [ $# -ne 0 ]; then
FILENAME=$1
fi
FILENAME=$1
fi
少し応用なbashシェルスクリプト | 2025.02.02 |
変数は未定義か? |
「test 変数=""」は、その変数が空文字の場合だけでなく未定義であっても真である。
if [ "$var" = "" ]; then
echo "undefied"
fi
echo "undefied"
fi
少し応用なbashシェルスクリプト | 2025.02.02 |
ワイルドカードと正規表現のif [[ |
ifと共によく使うtestコマンドは「[[」とするとワイルドカードと正規表現が使えるようになる。
#!/bin/bash
DIR=/usr/bin
if [[ "$DIR" == "/usr/"* ]]
then
echo "$DIR is under /usr/..."
fi
DIR=/usr/bin
if [[ "$DIR" == "/usr/"* ]]
then
echo "$DIR is under /usr/..."
fi
正規表現は「=~」演算子を組み合わせてパターンマッチができる。
#!/bin/bash
STR="abcd01234.89"
if [[ "$STR" =~ [a-z0-9]+\.[0-9]+ ]]
then
echo "$STR match"
fi
STR="abcd01234.89"
if [[ "$STR" =~ [a-z0-9]+\.[0-9]+ ]]
then
echo "$STR match"
fi
abcd01234.89 match
少し応用なbashシェルスクリプト | 2025.02.02 |
空文字を考慮した文字列比較(非推奨) |
以下のVARの文字列の比較の部分は、エラーになる。$VARが空文字の場合ifは「[ = "ABC" ]」と解釈されるからである。
VAR=""
if [ $VAR = "ABC" ]; then
echo $VAR
fi
if [ $VAR = "ABC" ]; then
echo $VAR
fi
VARが空文字でも判断できるように比較する両方に適当な文字を付ける方法がある。
次の場合はVARが空文字でもエラーにならない。
次の場合はVARが空文字でもエラーにならない。
VAR=""
if [ X$VAR = X"ABC" ]; then
echo $VAR
fi
if [ X$VAR = X"ABC" ]; then
echo $VAR
fi
このような方法は古いシェルスクリプトに見かけるが推奨できない。この場合は変数を""で囲めばよいだけである。
if [ "$VAR" = "ABC" ]; then
あるいは変数が空文字かどうかは-zで判断してもよい。
if [ -z "$A" ]; then
echo "len 0"
fi
echo "len 0"
fi
少し応用なbashシェルスクリプト | 2025.02.02 |
if testの論理演算 |
testコマンドは比較演算式を-a(AND)、-o(OR)、!(NOT)で論理演算できる。
for A in 1 2 3 4 5 6 7 8 9 10
do
if [ ! \( "$A" -ge 5 -a "$A" -le 9 \) -o "$A" -eq 7 ]
then
echo $A
fi
done
do
if [ ! \( "$A" -ge 5 -a "$A" -le 9 \) -o "$A" -eq 7 ]
then
echo $A
fi
done
1
2
3
4
7
10
2
3
4
7
10
評価を個別のtestコマンド([])に分ける場合は、testコマンドのオプションではなくシェルの論理演算である「&&」や「||」を使う。
for A in 1 2 3 4 5 6 7 8 9 10
do
if [ ! \( "$A" -ge 5 -a "$A" -le 9 \) ] || [ "$A" -eq 7 ]
then
echo $A
fi
done
do
if [ ! \( "$A" -ge 5 -a "$A" -le 9 \) ] || [ "$A" -eq 7 ]
then
echo $A
fi
done
少し応用なbashシェルスクリプト | 2025.02.02 |
ifにはtestでなくてもよい |
ifは続くコマンドの終了ステータスの0かそれ以外で分岐するので、コマンドの結果で分岐するならtest、[〜]を使わなくてもよい。
if ! rm xxx.file; then
echo "xxx.file not exist"
fi
echo "xxx.file not exist"
fi
rm: cannot remove 'xxx.file': No such file or directory
xxx.file not exist
xxx.file not exist
少し応用なbashシェルスクリプト | 2025.02.02 |
if falseでコメントアウト |
if false~fiで囲むと、その間のコメントアウトになる。
if false; then
This is Comment out
fi
This is Comment out
fi
少し応用なbashシェルスクリプト | 2025.02.02 |
typeでコマンドの存在確認 |
typeは、指定するコマンドが存在かどうかをチェックする。
次は、gitコマンドが存在するかどうかを判断している。
次は、gitコマンドが存在するかどうかを判断している。
if type "git" > /dev/null 2>&1; then
echo "git OK"
fi
echo "git OK"
fi
typeコマンドの判断結果を一切表示しないように、/dev/nullのリダイレクトで表示を捨てている。
あるいは、これも同じことができる。
type "bit" &>/dev/null || echo "git not installed"
少し応用なbashシェルスクリプト | 2025.02.02 |
OR|| AND&&でifの代用 |
A || Bは、Aがエラーの場合のみBを実行する。つまり「if not A then B」である。
A && Bは、Aがエラーの場合はBは実行しない。つまり「if A then B」である。
A && Bは、Aがエラーの場合はBは実行しない。つまり「if A then B」である。
次は、makeが失敗した場合は、exit 1で終了する。
make || exit 1
wget http://ftp.xxx.org/pub/libaaa.tar.gz || exit 1
wget http://ftp.xxx.org/pub/libaaa.tar.gz || exit 1
エラー発生時は直ちにシェルスクリプトを停止させたいときによく使う形である。
エラーでも強制的に継続させたいコマンドがある場合は、次のようにするとよい。
set -e
mkdir dir1 || true
mkdir dir1 || true
シェルスクリプトのデフォルトは、コマンドが非ゼロを返しエラーになってもスクリプトの実行は継続する。
「set -e」などにより、デフォルトでエラー時に直ちに停止する設定になっているときに、前のコマンドエラーでもtrueが適用され、このコマンド行としては正常の扱いとなる。
「set -e」などにより、デフォルトでエラー時に直ちに停止する設定になっているときに、前のコマンドエラーでもtrueが適用され、このコマンド行としては正常の扱いとなる。
次は、コマンドに引数がない場合、右のechoでメッセージを表示して直ちに終了する。
[ $# -ne 0 ] || { echo "Usage: cmd arg, .."; exit 1;}
||以降が複数のコマンドがある場合は、{ }でグループにする。
A && Bは、Aの結果がエラーならばその時点でAND論理が成立しないので、Bを実行する必要がない。
次の例では、コマンド引数が2未満の場合は、右のechoでメッセージを表示して直ちに終了する。
次の例では、コマンド引数が2未満の場合は、右のechoでメッセージを表示して直ちに終了する。
[ $# -lt 2 ] && { echo "Usage: cmd arg, .."; exit 0;}
よく使われるのが、ファイルがあればそのファイルに対し〜という条件を書く場合。
[ -f $FILE ] && cat $FILE
少し応用なbashシェルスクリプト | 2025.02.02 |
パイプからの標準入力を取り込む |
testコマンドの-pは、ファイルが名前付きパイプであるかどうかを判断する。これを利用すると、パイプからの入力を受け入れるスクリプトが作れる。
もし/dev/stdinを-pで判定して、それが名前付きパイプだった場合、現在のスクリプトは「|」のパイプから何か入力されていることを意味する。
testpipe.sh
if [ -p /dev/stdin ]; then
echo "from pipe"
cat -
else
echo "from file"
cat $1
fi
echo "from pipe"
cat -
else
echo "from file"
cat $1
fi
$ echo "ABCD" | sh ./testpipe.sh
from pipe
ABCD
$ sh ./pipetest.sh hosts
from file
127.0.0.1 LOCALhost
from pipe
ABCD
$ sh ./pipetest.sh hosts
from file
127.0.0.1 LOCALhost
少し応用なbashシェルスクリプト | 2025.02.02 |
/dev/nullで出力を破棄 |
/dev/nullへのリダイレクトは出力を破棄する。
/dev/nullへの標準出力のリダイレクトにより、コマンド出力を表示させないようにできる。
/dev/nullへの標準出力のリダイレクトにより、コマンド出力を表示させないようにできる。
make install > /dev/null
cat text.txt > /dev/null
cat text.txt > /dev/null
2>&1は標準エラー出力を標準出力へ統合する。
次のようにすると、cmdコマンドが表示するものを全て/dev/nullに捨てて一切を表示させなくできる。
次のようにすると、cmdコマンドが表示するものを全て/dev/nullに捨てて一切を表示させなくできる。
cmd >/dev/null 2>&1
これを/dev/nullではなくファイルへのリダイレクトにすれば、cmdコマンドは標準出力もエラー出力もファイルに記録できる。
cmd > log.txt 2>&1
少し応用なbashシェルスクリプト | 2025.02.02 |
teeで標準出力とファイル書き込みを同時に |
teeコマンドはほとんどの場合パイプと共に使う。
次のようにすると、lsコマンドを表示しながら内容をlist.txtに書き込むことができる。
次のようにすると、lsコマンドを表示しながら内容をlist.txtに書き込むことができる。
$ ls -l | tee list.txt
また、次のようにすればエラー出力も表示しながらエラーの表示をファイルに記録できる。
$ mkdir existdir 2>&1 | tee err.log
少し応用なbashシェルスクリプト | 2025.02.02 |
ヒアドキュメントで複数行をファイル書き込み |
catとヒアドキュメントのリダイレクトの組み合わせで、任意の文字列の行をファイルに書き込むことができる。
cat > file.txt << EOS
This is
a
Pen
EOS
This is
a
Pen
EOS
ところで、あまり見かけないが、文字列の区間を示す"〜"には改行を含めることができる。
echo "
This is
a
Pen
"
This is
a
Pen
"
少し応用なbashシェルスクリプト | 2025.02.02 |
テキストエディタedで自動編集 |
テキストエディタのedへの入力とコマンドをリダイレクトで追記すると、そのテキストファイルをリダイレクトした通りに編集される。
これをヒアドキュメントで編集内容を行ごとに並べると自動編集のようなことができる。
これをヒアドキュメントで編集内容を行ごとに並べると自動編集のようなことができる。
ed hosts << EOS
2
i
mmmnnn
.
1
s/local/LOCAL/
w
q
EOS
2
i
mmmnnn
.
1
s/local/LOCAL/
w
q
EOS
この例のhostsファイルの編集前がこうだとしたら、
127.0.0.1 localhost
127.0.1.1 ABC1234
127.0.1.1 ABC1234
上のスクリプトで自動編集した後は次のようになる。
127.0.0.1 LOCALhost
mmmnnn
127.0.1.1 ABC1234
mmmnnn
127.0.1.1 ABC1234
少し応用なbashシェルスクリプト | 2025.02.02 |
サブシェル (~) |
()で囲む範囲はサブシェルとして、別のシェルを起動して実行する。
()内のサブシェルで処理したことは、その後のスクリプトには影響しない。
()内のサブシェルで処理したことは、その後のスクリプトには影響しない。
pwd
(mkdir Dir1 ; cd Dir1 ; pwd ; touch AAA)
ls -l Dir1/
pwd
(mkdir Dir1 ; cd Dir1 ; pwd ; touch AAA)
ls -l Dir1/
pwd
home/user/
/home/user/Dir1
合計 0
-rw-rw-r-- 1 user user 0 2月 1 21:11 AAA
/home/user/work_bash
/home/user/Dir1
合計 0
-rw-rw-r-- 1 user user 0 2月 1 21:11 AAA
/home/user/work_bash
(cd dir1)はサブシェルの中でカレントディレクトリを移動しているが、()から出た後は変化していない。
(cmd; cmd; ...)のようにすれば、サブシェル内で複数コマンドを実行させることができる。
(cmd; cmd; ...)のようにすれば、サブシェル内で複数コマンドを実行させることができる。
少し応用なbashシェルスクリプト | 2025.02.02 |
コマンドのグループ化{ ~ } |
{}の中に複数のコマンドを1つのコマンドのようにグループ化できる。
次の2つのechoの標準出力はまとめてout.txtにリダイレクトされる。
次の2つのechoの標準出力はまとめてout.txtにリダイレクトされる。
{ echo "ABC"; echo "DEF";} > out.txt
「{」に続けて書く場合は、1つ以上のスペースを空けなければならない。
グループは、()のサブシェルのように別のシェルとして実行するのではなく、現在のシェル上での実行である。
グループは次のように複数行でも書ける。
{
echo "ABC"
echo "DEF"
} > out.txt
echo "ABC"
echo "DEF"
} > out.txt
&&や||の両辺の処理を複数のコマンドにしたい場合にグループ化できる。
次の例は、xxx.fileが存在すれば、dir1ディレクトリを作成しdir1に移動する。
次の例は、xxx.fileが存在すれば、dir1ディレクトリを作成しdir1に移動する。
(test -f xxx.file) && {
mkdir dir1
mv xxx.file dir1
}
mkdir dir1
mv xxx.file dir1
}
少し応用なbashシェルスクリプト | 2025.02.02 |
スクリプトの中でsourceを実行 |
スクリプトの中からsourceで実行する別のスクリプトは、そのスクリプトに書き写したことと同じ振る舞いになる。
例えば、source実行するスクリプトがcdでディレクトリを変えたものならば、ディレクトリが変更したまま次を継続する。
例えば、source実行するスクリプトがcdでディレクトリを変えたものならば、ディレクトリが変更したまま次を継続する。
dir1にディレクトリ移動するだけのスクリプトchdir.shがあるとする。
chdir.sh
cd dir1
次の場合は、lsはdir1のファイルを表示する。
source chdir.sh
ls
ls
もしchdir.shをsourceではなくスクリプトとして実行した場合は、chdir.shが終了した時点で完結するので、dir1ではなくこのスクリプトのカレントディレクトリのファイルを表示する。
少し応用なbashシェルスクリプト | 2025.02.02 |
未定義変数のデフォルト値 ${A-B} |
${VAR1-$VAR2}は、VAR1が未定義ならばVAR2を適用するという意味。
VAR1が定義されていればVAR1の値を適用し、VAR1がなければVAR2の値を適用する。
VAR1が定義されていればVAR1の値を適用し、VAR1がなければVAR2の値を適用する。
VAR2="ABC"
echo ${VAR1-${VAR2}}
echo ${VAR1-${VAR2}}
ABC
少し応用なbashシェルスクリプト | 2025.02.02 |
不定な変数の代替値「:=」「:-」 |
${A:=x}は$Aが未設定または未定義ならばxを代入して、定義済みならそのままにする。
一種の代入になるのだが、これだけ書くとコマンドとして認識されてエラーになるので、何もしない「:」に続けて、パラメータとして評価させる方法がある。
一種の代入になるのだが、これだけ書くとコマンドとして認識されてエラーになるので、何もしない「:」に続けて、パラメータとして評価させる方法がある。
#A="ABC"
: ${A:="DEF"}
echo $A
: ${A:="DEF"}
echo $A
DEF
「:」がなければ「DEFというコマンドはない」というエラーになる。
${A:-x}は、$Aが未設定または未定義ならばxを代替として返す。このとき$Aにはxは代入しない。
#A="ABC"
B=${A:-"DEF"}
echo $B
echo $A
B=${A:-"DEF"}
echo $B
echo $A
DEF
$Bのみ代替の"DEF"が表示され、$Aは未定義のままで表示されない。
${A:=x}も${A:-x}のxを省略して、${A:=}も${A:-}のようにすると、xに相当するものに空文字「""」が適用される。
少し応用なbashシェルスクリプト | 2025.02.02 |
変数の大文字・小文字変換 |
${A,,}と${A^^}はそれぞれ、変数Aを全て小文字に変換、大文字に変換する。
#!/bin/bash
A="HelloWorld"
echo ${A,,} # 小文字に変換
echo ${A^^} # 大文字に変換
A="HelloWorld"
echo ${A,,} # 小文字に変換
echo ${A^^} # 大文字に変換
helloworld
HELLOWORLD
HELLOWORLD
少し応用なbashシェルスクリプト | 2025.02.02 |
%、#のパターン一致削除 |
${A%x}はxのパターンが末尾から一致する部分をAから削除する。
xのパターンは任意の文字列を表す「*」と共に使うことが多い。%%は末尾からの最長一致で、#と##は先頭からの一致である。
xのパターンは任意の文字列を表す「*」と共に使うことが多い。%%は末尾からの最長一致で、#と##は先頭からの一致である。
A="https://www.yahoo.co.jp"
echo ${A%.*} # 末尾最短一致
echo ${A%%.*} # 末尾最長一致
B="2001::10:20"
echo ${B#*:} # 先頭最短一致
echo ${B##*:} # 先頭最長一致
C="setup.py"
echo ${C%.py} # 拡張子を削除できる
echo ${A%.*} # 末尾最短一致
echo ${A%%.*} # 末尾最長一致
B="2001::10:20"
echo ${B#*:} # 先頭最短一致
echo ${B##*:} # 先頭最長一致
C="setup.py"
echo ${C%.py} # 拡張子を削除できる
https://www.yahoo.co
https://www
:10:20
20
setup
https://www
:10:20
20
setup
$Cの例のようにサフィックスやプリフィックスを取り除くときによく使われる。
少し応用なbashシェルスクリプト | 2025.02.02 |
算術式に置き換える$((~)) |
$((~))の中は、変数を数値として解釈して算術演算を行った結果に置き換える。
#!/bin/bash
A="4"
X=$(($A*10))
echo $X
A="4"
X=$(($A*10))
echo $X
40
代表的には「+ - * / %」であるが、それ以外の演算もできる。
ビット演算
$(($A | $B)) OR
$(($A & $B)) AND
$(($A ^ $B)) XOR
$((~$A)) NOT
比較演算 ※結果は0か1になる
$(($A > $B)) 他 < >= <= == !=
論理演算 ※結果は0か1になる
$(($A && $B)) AND
$(($A || $B)) OR
$((!$A)) NOT
べき乗
$(($A ** $B)) AのB乗
16進数・8進数・2進数を10進数に変換
$((0xff))
$((0766))
$((2#1101))
$(($A | $B)) OR
$(($A & $B)) AND
$(($A ^ $B)) XOR
$((~$A)) NOT
比較演算 ※結果は0か1になる
$(($A > $B)) 他 < >= <= == !=
論理演算 ※結果は0か1になる
$(($A && $B)) AND
$(($A || $B)) OR
$((!$A)) NOT
べき乗
$(($A ** $B)) AのB乗
16進数・8進数・2進数を10進数に変換
$((0xff))
$((0766))
$((2#1101))
少し応用なbashシェルスクリプト | 2025.02.02 |
永久ループ |
trueコマンドは常に正常終了するので、whileループと合わせて永久ループに使われる。
while true
do
date
sleep 2
done
do
date
sleep 2
done
あるいは何もしない「:」という組み込みコマンドで次のようにもできる。
while :
do
echo "infin"
sleep 1
done
do
echo "infin"
sleep 1
done
少し応用なbashシェルスクリプト | 2025.02.02 |
until do |
untilはwhileと同様条件が継続している間繰り返す構文だが、判断する論理が逆で、コマンドが正常終了でない間に繰り返す。コマンドが正常終了まで待機するような場合に利用できる。
次は、aaa.txtが存在しない間sleepを繰り返しながら待機する。
until ls aaa.txt
do
sleep 1
echo "wait"
done
do
sleep 1
echo "wait"
done
少し応用なbashシェルスクリプト | 2025.02.02 |
カウンタで回るループ |
以下は、全てカウンタが0~9をループする例である。
N=0
while [ $N -lt 10 ]; do
echo $N # 0 .. 9
N=$((N+1))
done
while [ $N -lt 10 ]; do
echo $N # 0 .. 9
N=$((N+1))
done
N=0
while :
do
if [ $N -eq 10 ]; then
break
fi
echo $N # 0 .. 9
N=$((N+1))
done
while :
do
if [ $N -eq 10 ]; then
break
fi
echo $N # 0 .. 9
N=$((N+1))
done
N=0
while [ $N -lt 10 ];
do
echo $N # 0 .. 9
N=`expr ${N} + 1`
done
while [ $N -lt 10 ];
do
echo $N # 0 .. 9
N=`expr ${N} + 1`
done
最後はやや古い、カウントアップの計算にexprコマンドを使う方法である。
少し応用なbashシェルスクリプト | 2025.02.02 |
コマンドのリスト出力をforループ |
出力が空白や改行で区切ったリストになるコマンドの出力を変数に格納すればforでイテレーションできる。
lsコマンドのファイルをforで個別に走査する。
lsコマンドのファイルをforで個別に走査する。
LS=$(ls /proc/sys/)
for FILE in $LS; do
echo $FILE
done
for FILE in $LS; do
echo $FILE
done
変数を仲介せずに直接for-inに与えてもよい。
for f in $(find /etc -name *.conf)
do
echo $f
done
do
echo $f
done
少し応用なbashシェルスクリプト | 2025.02.02 |
引数リストの$@と$*をforループ |
「$@」は位置引数を個別に展開するので、それぞれの引数がリストの要素としてforで走査される。
for arg in "$@"
do
echo $arg
done
do
echo $arg
done
test.sh aa bb cc
aa
bb
cc
aa
bb
cc
一方「$*」は、位置引数を全て"~"の中に展開した1つの文字列にまとめられる。
for arg in "$*"
do
echo $arg
done
do
echo $arg
done
test.sh aa bb cc
aa bb cc
aa bb cc
少し応用なbashシェルスクリプト | 2025.02.02 |
ワイルドカード展開したファイル名をfor |
forのリストにワイルドカードのファイル名を指定すると、ワイルドカード展開したファイル名のリストになる。
for F in /etc/*.conf
do
echo $F
done
do
echo $F
done
/etc/adduser.conf
/etc/apg.conf
/etc/appstream.conf
/etc/brltty.conf
...
/etc/apg.conf
/etc/appstream.conf
/etc/brltty.conf
...
少し応用なbashシェルスクリプト | 2025.02.02 |
配列をforループ |
()の中の空白で区切ったリストを、forでイテレーションする。
#!/bin/bash
ITEMLIST=(aaa bbb ccc)
for ITEM in ${ITEMLIST[@]}; do
echo $ITEM
done
ITEMLIST=(aaa bbb ccc)
for ITEM in ${ITEMLIST[@]}; do
echo $ITEM
done
リストは改行で区切ってもよい。
#!/bin/bash
EASIA=(
China
India
Japan
Korea
)
for cname in ${EASIA[@]}; do
echo "Country ${cname}"
done
EASIA=(
China
India
Japan
Korea
)
for cname in ${EASIA[@]}; do
echo "Country ${cname}"
done
Country China
Country India
Country Japan
Country Korea
Country India
Country Japan
Country Korea
少し応用なbashシェルスクリプト | 2025.02.02 |
文字列を配列に変換 |
変数を()で囲むと、空白区切りで分割した配列に変換できる。
#!/bin/bash
STR="A B C D E F"
AR=($STR)
for x in ${AR[@]}
do
echo "=> $x"
done
STR="A B C D E F"
AR=($STR)
for x in ${AR[@]}
do
echo "=> $x"
done
=> A
=> B
=> C
=> B
=> C
少し応用なbashシェルスクリプト | 2025.02.02 |
複数の変数を連続してforループ |
forのinに続くリストに、それぞれがリストになっている複数の変数を指定すると、それらを展開してから全体を連続して走査する。
LSTA="aa bb cc"
LSTB="xx yy zz"
for a in $LSTA $LSTB
do
echo ">> $a"
done
LSTB="xx yy zz"
for a in $LSTA $LSTB
do
echo ">> $a"
done
>> aa
>> bb
>> cc
>> xx
>> yy
>> zz
>> bb
>> cc
>> xx
>> yy
>> zz
少し応用なbashシェルスクリプト | 2025.02.02 |
forのリストと空白区切りについて |
空白で区切った文字列はリストとして解釈される。
forで空白区切りでイテレーションできる。
forで空白区切りでイテレーションできる。
STR="AB CD EF"
for x in $STR
do
echo "=> $x"
done
for x in $STR
do
echo "=> $x"
done
=> AB
=> CD
=> EF
=> CD
=> EF
ところで、forのところで$STRを"$STR"のようにすると、
for x in "$STR"
結果が次のように変わる。
=> AB CD EF
"~"で囲むことにより空白を含めた一つの文字列として解釈されるからである。
forでイテレーションする配列変数を"~"でクォートした場合は、forが分解する要素のそれぞれに対し"~"で囲むので、次のような"A B"や"C D"はそれ以上は分解されず空白が入ったままの文字列の単位でループする。
#!/bin/bash
AR=("A B" "C D" "E F")
echo "${AR[@]}"
for x in "${AR[@]}"
do
echo "=> $x"
done
AR=("A B" "C D" "E F")
echo "${AR[@]}"
for x in "${AR[@]}"
do
echo "=> $x"
done
A B C D E F
=> A B
=> C D
=> E F
=> A B
=> C D
=> E F
もし、上の$AR[@]を"〜"で囲まなければ、"A B"などの空白から更に分解され、個別の文字がイテレーションされる。
#!/bin/bash
AR=("A B" "C D" "E F")
for x in ${AR[@]}
do
echo "=> $x"
done
AR=("A B" "C D" "E F")
for x in ${AR[@]}
do
echo "=> $x"
done
=> A
=> B
=> C
=> D
=> E
=> F
=> B
=> C
=> D
=> E
=> F
少し応用なbashシェルスクリプト | 2025.02.02 |
配列要素の変更・追加 |
配列の指定要素に代入すると、もとの値と置き換えることができる。
#!/bin/bash
LST=("AB" "CD" "EF")
LST[1]="cd"
echo ${LST[@]}
LST=("AB" "CD" "EF")
LST[1]="cd"
echo ${LST[@]}
AB cd EF
また、次のように配列の末尾に要素を追加できる。
#!/bin/bash
LST=("AB" "CD" "EF")
LST=("${LST[@]}" "GH")
echo ${LST[@]}
LST=("AB" "CD" "EF")
LST=("${LST[@]}" "GH")
echo ${LST[@]}
AB CD EF GH
少し応用なbashシェルスクリプト | 2025.02.02 |
オプションの確認 getopts |
getoptsは、現在のコマンドに渡された引数から指定するオプションリストの中にあるものを探し、順番に変数に格納する。
コマンドのオプション解析は、次のようなwhileとcaseを組み合わせたものが定番である。
コマンドのオプション解析は、次のようなwhileとcaseを組み合わせたものが定番である。
testgetopts.sh
while getopts "lfihv" opt
do
case $opt in
l) echo "option l: show list";;
f) echo "option f: force";;
i) echo "option i: ignore";;
h) echo "option h: show help";;
v) echo "option v: verbose";;
*) echo "unknown option $opt"; exit 1;;
esac
done
do
case $opt in
l) echo "option l: show list";;
f) echo "option f: force";;
i) echo "option i: ignore";;
h) echo "option h: show help";;
v) echo "option v: verbose";;
*) echo "unknown option $opt"; exit 1;;
esac
done
$ sh testgetopts.sh -fv
option f: force
option v: verbose
option f: force
option v: verbose
getoptは外部コマンドだがgetoptsはシェル組み込みコマンドである。
少し応用なbashシェルスクリプト | 2025.02.02 |
引数の$1を順にずらす shift |
shiftコマンドは、実行するたびに引数の先頭を示す$1が次の引数を示すようにずれていく。
testshift.sh
while [ "$1" != "" ]
do
echo ">> $1"
shift
done
do
echo ">> $1"
shift
done
$ testshift.sh aa bb cc
>> aa
>> bb
>> cc
>> aa
>> bb
>> cc
終了の判定は、次のように引数の個数で判断してもよい。shiftのたびに先頭の引数が消えていくので、最後は引数の数が0になる。
while [ $# -gt 0 ]
do
echo ">> $1"
shift
done
do
echo ">> $1"
shift
done
コマンドライン引数だけでなく、関数の引数にも適用できる。
func() {
while [ $# -gt 0 ]
do
echo ">> $1"
shift
done
}
func aa bb cc
while [ $# -gt 0 ]
do
echo ">> $1"
shift
done
}
func aa bb cc
caseと組み合わせて、引数チェックに応用できる。
while [ $# -gt 0 ]
do
case $1 in
-ver)
echo "ver 1.0";;
-help)
echo "usage";;
*)
echo "unknown";;
esac
shift
done
do
case $1 in
-ver)
echo "ver 1.0";;
-help)
echo "usage";;
*)
echo "unknown";;
esac
shift
done
shiftを使わなくても、for文にinを指定しない次のような特殊な書き方で引数を順番に参照できる。ループのたびに引数の先頭から順にargに代入される。
for arg
do
echo ">> $arg"
done
do
echo ">> $arg"
done
少し応用なbashシェルスクリプト | 2025.02.02 |
caseの条件のワイルドカード・文字リスト・OR |
caseには、ワイルドカードや含まれる文字のリストや文字範囲、ORが使える。
case $1 in
-A | -a)
echo "oprion A/a";;
[0-9])
echo "option number";;
-[x-z])
echo "option -x or -y or -z";;
-[Nn])
echo "oprion N/n";;
??)
echo "oprion 2char";;
*)
echo "oprion other";;
esac
-A | -a)
echo "oprion A/a";;
[0-9])
echo "option number";;
-[x-z])
echo "option -x or -y or -z";;
-[Nn])
echo "oprion N/n";;
??)
echo "oprion 2char";;
*)
echo "oprion other";;
esac
A | B A or B
[a-z] a~zの範囲
[0-9] 0~0の範囲
[abc] abcのいずれか
? 任意の1文字に一致
* 全ての文字列に一致(デフォルトの意味になる)
[a-z] a~zの範囲
[0-9] 0~0の範囲
[abc] abcのいずれか
? 任意の1文字に一致
* 全ての文字列に一致(デフォルトの意味になる)
少し応用なbashシェルスクリプト | 2025.02.02 |
質問に全てEnterで答える yes |
シェルスクリプトなどが
Do you want to use this? [YES]
のように、質問を表示してユーザの入力に対しEnterでデフォルト「YES」とするようなものがある。yesコマンドにより、そのような質問に対しすべてデフォルトでEnterを入力することを自動化できる。
Do you want to use this? [YES]
のように、質問を表示してユーザの入力に対しEnterでデフォルト「YES」とするようなものがある。yesコマンドにより、そのような質問に対しすべてデフォルトでEnterを入力することを自動化できる。
yesコマンドは引数の文字列をkillされるまで改行(Enter)しながら出力し続けるというものである。
「yes ''」はプロセスが止められるまで改行(Enter)を出力し続けることになる。
「yes ''」はプロセスが止められるまで改行(Enter)を出力し続けることになる。
このスクリプトを実行すると、
enter.sh
#!/bin/bash
read -u 0 var
echo "Input Enter1"
read -u 0 var
echo "Input Enter2"
read -u 0 var
echo "Input Enter3
read -u 0 var
echo "Input Enter1"
read -u 0 var
echo "Input Enter2"
read -u 0 var
echo "Input Enter3
$ bash enter.sh
Input Enter1
Input Enter2
Input Enter3
Input Enter1
Input Enter2
Input Enter3
Enterを押すたびに「Input Enter?」が表示される。
これを、次のようにすると、Enterを押さなくても一気に3つの「Input Enterx」が表示されて終わる。
これを、次のようにすると、Enterを押さなくても一気に3つの「Input Enterx」が表示されて終わる。
$ yes '' | bash enter.sh
Input Enter1
Input Enter2
Input Enter3
Input Enter1
Input Enter2
Input Enter3
連続したEnterを標準入力のリダイレクトで、enter.shに入力すると、標準入力を待ち受けているread -u 0にyesからのEnterが流れ込み、3つのreadコマンドが自動でEnter押されたようにふるまう。
質問が何度も出るが、たいていはEnterでデフォルト選択で進めてよいようなスクリプトを自動化できる。
yes '' | make config
少し応用なbashシェルスクリプト | 2025.02.02 |
ファイルを行ごとに読み込む |
ファイルを行ごとに読み込む方法はいくつかある。
次の場合はファイルをリダイレクトで入力している。
次の場合はファイルをリダイレクトで入力している。
while read ln
do
echo ">> $ln"
done < /etc/hosts
do
echo ">> $ln"
done < /etc/hosts
コマンド出力をパイプからの標準入力で読み込む場合は次のようにできる。これは上と同じことができる。
cat /etc/hosts | while read ln
do
echo ">> $ln"
done
do
echo ">> $ln"
done
コマンドの結果出力を行単位で読み込むならば次のようにできる。
while read ln
do
echo ">> $ln"
done << EOS
$(ls -l /usr/lib/)
EOS
do
echo ">> $ln"
done << EOS
$(ls -l /usr/lib/)
EOS
$()により文字列に置き換えられたコマンドの出力をヒアドキュメントから読み込んでいる。
少し応用なbashシェルスクリプト | 2025.02.02 |
フィールド区切りでreadの入力 |
readコマンドはEnterまでを変数に取り込むが、複数の変数を指定すると、空白で区切った入力をそれぞれの変数に代入させることができる。
testread.sh
while read F1 F2 F3; do
echo "(1)$F1 (2)$F2 (3)$F3"
done
echo "(1)$F1 (2)$F2 (3)$F3"
done
$ sh testread.sh
AA 100 +++
(1)AA (2)100 (3)+++
AA 100 +++
(1)AA (2)100 (3)+++
少し応用なbashシェルスクリプト | 2025.02.02 |
dateで日時の文字列作成 |
年月日時分秒の文字列を作りたいときはdateコマンドで次のようにする。
echo $(date +%m%d%H%M%Y.%S)
例えばファイル名に時刻を入れる場合は次のようにできる。
LogFile=cmd_`date +%Y%m%d%H%M%S`.log
dateの部分は「年月日時分秒」に置き換わる。「cmd_20231115173133.log」のようになる。
UTCからの経過秒数は次のようにできる。
echo $(date +%s)
1734429807
少し応用なbashシェルスクリプト | 2025.02.02 |
printfコマンド |
C言語のprintf()とほとんど同等な書式付きで文字列を出力できる。
printf "フォーマット" 変数 値 変数 ..
S="ABC"
N=100
printf "<%s> val=%04d\n" $S $N
N=100
printf "<%s> val=%04d\n" $S $N
printfはほとんどのシェルで組み込みコマンドになっているらしい。
少し応用なbashシェルスクリプト | 2025.02.02 |
rootユーザで実行したか? |
whoamiでユーザ名がrootかどうかを確認して、root(あるいはsudo)でなければ実行できないようにする。
if [ $(whoami) != 'root' ]; then
echo "should be root to run"
exit -1
fi
echo "RUN"
echo "should be root to run"
exit -1
fi
echo "RUN"
少し応用なbashシェルスクリプト | 2025.02.02 |
プロセスのPIDを取得 |
psコマンドの結果からPIDを取得する例である。
次のように、psコマンドで全体からgrepでプロセス名を絞ってからawkでPIDを取得するような場合、grepコマンド自身が対象にならないように、grep -vで除外しているのがポイント。
次のように、psコマンドで全体からgrepでプロセス名を絞ってからawkでPIDを取得するような場合、grepコマンド自身が対象にならないように、grep -vで除外しているのがポイント。
PID=$(ps ax | grep smbd | grep -v "grep" | awk '{print $1}')
echo $PID
echo $PID