Hands-on learning record blog

ハンズオン学習記録、更新のない日は技術本を読む📚

6/30~7/2 Linux学習(シェルスクリプト①)

シェルスクリプト

コマンドラインをあらかじめ記述しておくファイルのこと(一種の言語)

→一度シェルスクリプトを作れば、あとで同じ処理が必要になったときにそのシェルスクリプトを再利用できる

→自分だけではなく、ほかの人にも配布して使用することができる

→コマンドを打ち間違えることによる作業ミスをなくすことができる

 

※どのシェルのシェルスクリプトを書くべきか

→sh or bash

●sh...互換性、移植性に配慮するなら(bashで書かれたシェルスクリプトも互換性、移植性はとても高い)

bash...shのシェルスクリプトには様々な問題があるため、近年はbashが良いとされる

 

bashにはシェルスクリプトを書く上で便利な機能が多く用意されている

 

シェルスクリプトは実行したいコマンドを記述したテキストファイルなので、vimなどのテキストエディタを利用して作成できる

 

●ホームディレクトリのファイル使用量を表示する

[cloudshell-user@ip-10-132-40-13 ~]$ du -h ~ | tail -n 1
80K     /home/cloudshell-user
[cloudshell-user@ip-10-132-40-13 ~]$ 

 

↓このコマンドをシェルスクリプトにする

 

vimシェルスクリプトhomesize.shを作成

[cloudshell-user@ip-10-132-40-13 ~]$ vim homesize.sh

 

・テキストファイルに以下を入力し、:wqで保存して終了

#!/bin/bash

du -h ~ | tail -n 1

 

シェルスクリプトは実行されるファイルのため、+xとして実行権限を付与しておく必要がある

[cloudshell-user@ip-10-132-40-13 ~]$ chmod +x homesize.sh

 

・作成したシェルスクリプトを実行

[cloudshell-user@ip-10-132-40-13 ~]$ ./homesize.sh
84K     /home/cloudshell-user
[cloudshell-user@ip-10-132-40-13 ~]$

 

※実行するファイルの前に「.」を必ずつけること

→カレントディレクトリにあるhomesize.shファイルを指定して実行するという意味

 

・sourceコマンドは、指定したファイルの内容をそのままコマンドラインに入力したときと同じように実行する

※対象ファイルを直接実行するわけではないため、ファイルに実行権限は不要

→シバンなしのテキストファイルを作成し、sourceコマンドで実行

[cloudshell-user@ip-10-132-40-13 ~]$ vim homesize-noshebang.sh
[cloudshell-user@ip-10-132-40-13 ~]$ source ./homesize-noshebang.sh
96K     /home/cloudshell-user
[cloudshell-user@ip-10-132-40-13 ~]$ 

 

→また、「.」はsourceコマンドと同じ意味を持つため、以下のコマンドでも実行可能

[cloudshell-user@ip-10-132-40-13 ~]$ . ./homesize-noshebang.sh
]96K    /home/cloudshell-user
[cloudshell-user@ip-10-132-40-13 ~]$ ]

bashスクリプトではsourceコマンドを利用することを推奨

 

・サブシェルとは、現在のシェルから新しく起動される子プロセスのシェルのこと

 

シェルスクリプトの基本

→複数のコマンドを実行させる場合には、順番に何行でもコマンドを書ける

vimで複数コードを記載したテキストファイルを作成

#!/bin/bash

echo "root directory"
cd /
ls -l

 

→作成したファイルに実行権限を付与して、rootls.shを実行

[cloudshell-user@ip-10-132-40-13 ~]$ chmod +x rootls.sh
[cloudshell-user@ip-10-132-40-13 ~]$ ./rootls.sh
root directory
total 76
drwxr-xr-x.   3 root root 4096 Jun 30 12:03 aws
lrwxrwxrwx.   1 root root    7 Jan 30  2023 bin -> usr/bin
dr-xr-xr-x.   2 root root 4096 Jan 30  2023 boot
drwxr-xr-x.  10 root root 2880 Jun 30 12:03 dev
drwxr-xr-x.   1 root root 4096 Jun 30 12:03 etc
drwxr-xr-x.   3 root root 4096 Jun 30 11:40 home
lrwxrwxrwx.   1 root root    7 Jan 30  2023 lib -> usr/lib
lrwxrwxrwx.   1 root root    9 Jan 30  2023 lib64 -> usr/lib64
drwxr-xr-x.   2 root root 4096 May  9 20:44 local
drwxr-xr-x.   2 root root 4096 Jan 30  2023 media
drwxr-xr-x.   2 root root 4096 Jan 30  2023 mnt
drwxr-xr-x.   1 root root 4096 Jun 30 12:03 opt
dr-xr-xr-x. 215 root root    0 Jun 30 12:03 proc
dr-xr-x---.   3 root root 4096 Jun 30 11:40 root
drwxr-xr-x.   1 root root 4096 Jun 30 12:03 run
lrwxrwxrwx.   1 root root    8 Jan 30  2023 sbin -> usr/sbin
drwxr-xr-x.   2 root root 4096 Jan 30  2023 srv
dr-xr-xr-x.  13 root root    0 Jun 30 11:26 sys
drwxrwxrwt.   1 root root 4096 Jun 30 12:03 tmp
drwxr-xr-x.   1 root root 4096 May  9 20:44 usr
drwxr-xr-x.   1 root root 4096 May  9 20:44 var
[cloudshell-user@ip-10-132-40-13 ~]$ 

 

→「\」を行末に置くことで途中で改行できる

#!/bin/bash

echo \
"root directory"

 

[cloudshell-user@ip-10-132-40-13 ~]$ chmod +x rootdir.sh
[cloudshell-user@ip-10-132-40-13 ~]$ ./rootdir.sh
root directory
[cloudshell-user@ip-10-132-40-13 ~]$ 

 

 

・変数の書き方

→代入時には$をつけない

※$を変数の前につけるのは変数展開して値を参照したいときだけ

→=の前後にスペースを入れない

→変数名に利用できる文字はアルファベットと数値と_(アンダースコア)だけ

→変数名の区切りを明示する({}などを使用する)

 

・シングルクォート=変数展開されない(文字列として表示される)

 ダブルクォート=変数展開される

 

シェルスクリプトからコマンドライン引数を扱う

vimでparameters.shファイルを作成

#!/bin/bash

echo "\$0 = $0"
echo "\$1 = $1"
echo "\$2 = $2"
echo "\$3 = $3"
echo "\$4 = $4"
echo "\$5 = $5"

※「\$」とは、変数展開せずに$記号そのものを出すためにエスケープする記法

 

→引数を指定しないと何も表示されない

[cloudshell-user@ip-10-132-40-13 ~]$ chmod +x parameters.sh
[cloudshell-user@ip-10-132-40-13 ~]$ ./parameters.sh
$0 = ./parameters.sh
$1 = 
$2 = 
$3 = 
$4 = 
$5 = 

 

→引数を指定すると指定した引数が表示される
[cloudshell-user@ip-10-132-40-13 ~]$ ./parameters.sh aaa bbb ccc
$0 = ./parameters.sh
$1 = aaa
$2 = bbb
$3 = ccc
$4 = 
$5 = 
[cloudshell-user@ip-10-132-40-13 ~]$

 

→「*」を指定(ワイルドカード

[cloudshell-user@ip-10-132-40-13 ~]$ ./parameters.sh *
$0 = ./parameters.sh
$1 = []
$2 = homesize-noshebang.sh
$3 = homesize.sh
$4 = parameters.sh
$5 = reo
[cloudshell-user@ip-10-132-40-13 ~]$

 

vimで最終行に「echo "\$# = $#"」を入力し、実行

[cloudshell-user@ip-10-132-40-13 ~]$ ./parameters.sh aaa bbb ccc
$0 = ./parameters.sh
$1 = aaa
$2 = bbb
$3 = ccc
$4 = 
$5 = 
$# = 3
[cloudshell-user@ip-10-132-40-13 ~]$

※「$#」は特殊パラメータで、引数の個数を表示できる

 「$@」「$*」で引数を分割せずにまとめて扱うことができる

 

・制御構造について

●if文(条件を評価して、その真偽に応じて処理を分岐する)

コマンドライン引数がbinならOKと表示する

#!/bin/bash

if [ "$1" = "bin" ]; then
        echo "OK"
else    
        echo "NG"
fi

 

→$1をbinでOKと表示され、その他で指定するとNGと表示される

[cloudshell-user@ip-10-136-39-122 ~]$ ./if-bin.sh bin
OK
[cloudshell-user@ip-10-136-39-122 ~]$ ./if-bin.sh hello
NG
[cloudshell-user@ip-10-136-39-122 ~]$

 

シェルスクリプトでは、if文の後ろに置くのは「コマンド」であって、条件式ではない

 

★if文の構造

if <コマンド1>; then

 #<コマンド1>の結果が真である場合の処理

elif <コマンド2>; then

 #<コマンド2>の結果が真である場合の処理

elif <コマンド3>; then

 #<コマンド3>の結果が真である場合の処理

else

 #上記の結果がすべて偽である場合の処理

fi

 

→else句はコマンドの結果が偽の場合に実行される部分

→elif句は追加の条件を指定するために利用し、必要に応じていくらでも追加できる

 

※今回ifの後ろに[ "$1" = "bin" ]を置いていたが、「[」は単なるカッコではなく、”bashの組み込みコマンド”である

 

・lsやgrepなどのコマンドはすべて終了時に終了ステータスと呼ばれる整数値を返す

 $?というシェル変数で参照できる

[cloudshell-user@ip-10-136-39-122 ~]$ echo $?
0
[cloudshell-user@ip-10-136-39-122 ~]$

 

→正常に終了した場合は0を、エラー時には0以外の値を返す

 

・ls /とls /dummy(存在しないディレクトリ)を実行

#!/bin/bash

ls /
echo "exit status = $?"

ls /dummy
echo "exit status = $?"

 

[cloudshell-user@ip-10-136-39-122 ~]$ ./showstatus.sh
aws  bin  boot  dev  etc  home  lib  lib64  local  media  mnt  opt  proc  root  run  sbin  srv  sys  tmp  usr  var
exit status = 0
ls: cannot access '/dummy': No such file or directory
exit status = 2

→ls /では存在するディレクトリを指定しているため、0が返される

→ls /dummyでは存在しないディレクトリを指定しているため、エラーで2が返された

 

・testコマンドは「[」と同じ機能を持つ

→以下は同じ意味となる

if [ "$1" = "bin" ]; then

if test "$1" = "bin" ; then

 

・文字列の比較

→「=」...等しい、「!=」...等しくない、「-n」...空文字ではない、「-z」...空文字である

 

・整数に関する評価演算子

→「-eq」...等しい、「-ne」...等しくない、「-lt」...より小さい、「-le」...以下、

 「-gt」...より大きい、「-ge」...以上

 

・ファイル属性に関する評価演算子

→「-e」...存在する、「-d」...ディレクトリ、「-h」...シンボリックリンク

 「-L」...-hと同じ、「-f」...通常のファイル、「-r」...読み取りパーミッション

 「-w」...書き込みパーミッション、「-x」...実行パーミッション

 「-nt」...より変更時刻が新しい、「-ot」...より変更時刻が古い

 

・結合演算子

→「-a」...条件式1と2の両方が真の場合に真(AND)

 「-o」...条件式1と2のどちらか少なくとも1つが真の場合に真(OR)

 「!条件式」...条件式の真偽を逆にする(NOT)

 「()」...条件をグループ化

 

・if文で複数のコマンドの終了ステータスすべてがゼロの条件

#!/bin/bash

int1=$1
if [ "$int1" -gt 3 ] && [ "$int1" -lt 6 ]; then
        echo "int1 > 3 and int1 < 6"
fi

 

→両方満たす場合、出力される

[cloudshell-user@ip-10-136-56-161 ~]$ ./if-and.sh 4
int1 > 3 and int1 < 6

→それ以外は出力されない

[cloudshell-user@ip-10-136-56-161 ~]$ ./if-and.sh 8
[cloudshell-user@ip-10-136-56-161 ~]$

 

・if文でいずれかのコマンドの終了ステータスがゼロの条件

#!/bin/bash

str=$1
if [ "$str" = 'home' ] || [ "$str" = 'usr' ]; then
        echo "str = home or str = usr"
fi

 

→homeを指定

[cloudshell-user@ip-10-136-56-161 ~]$ ./if-or.sh home
str = home or str = usr
[cloudshell-user@ip-10-136-56-161 ~]$

→usrを指定

[cloudshell-user@ip-10-136-56-161 ~]$ ./if-or.sh usr
str = home or str = usr
[cloudshell-user@ip-10-136-56-161 ~]$

→それ以外を指定

[cloudshell-user@ip-10-136-56-161 ~]$ ./if-or.sh aaa
[cloudshell-user@ip-10-136-56-161 ~]$

 

●演習1

コマンドライン引数から指定されたファイルやディレクトリの概要を表示するシェルスクリプトを作る

#!/bin/bash

file=$1
 #コマンドライン引数からファイルパスを取得
 
if [ -d "$file" ]; then
 #ディレクトリである場合
  echo "$file is a directory:"
   ls "$file"
elif [ -f "$file" ]; then
 #通常ファイルである場合
  echo "$file is a regular file"
  head -n 5 "$file"
fi

 

→ファイルを指定

[cloudshell-user@ip-10-136-56-161 ~]$ ./summary.sh /etc/passwd
/etc/passwd is a regular file:
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
[cloudshell-user@ip-10-136-56-161 ~]$

 

ディレクトリを指定

[cloudshell-user@ip-10-136-56-161 ~]$ ./summary.sh /home
/home is a directory:
cloudshell-user
[cloudshell-user@ip-10-136-56-161 ~]$