シェルスクリプトの構造
シェルスクリプトは、プロセシジュアル言語であり、一行ずつ解釈して実行されます。一方、高度なプログラミング言語はコンパイル型(C、C++など)またはインタープリタ型(Python、PHP、JavaScriptなど)があります。
Javaは、コンパイルとインタープリテーションを組み合わせた特殊な高度なプログラミング言語です。
シェルスクリプトの実行方法
1. 実行権限を付与してパスを通じて実行する
chmod +x script.sh
chmod 777 script.sh
chmod +x script.sh を実行した後、絶対パスや相対パスでスクリプトを実行できます。
2. スクリプトを /usr/local/bin ディレクトリに移動して任意のディレクトリから実行する
3. bash コマンドを使用して実行する
[root@centos8 ~]# bash ./example.sh
これは最初のシェルスクリプトです
[root@centos8 ~]# cat example.sh | bash
これは最初のシェルスクリプトです
[root@centos8 ~]#
スクリプトの文法チェックと逐行実行
bash -n script.sh
bash -x script.sh
スクリプトの一般的なエラーは以下の3種類です:
- 文法エラー:次のコマンドが実行されません。bash -n を使用してエラーをチェックできますが、表示されるエラーラインは正確ではない場合があります。
- コマンドエラー:デフォルトでは次のコマンドは実行され続けます。bash -n では検出できませんが、bash -x を使用して観察できます。
- 論理エラー:bash -x を使用して観察する必要があります。
bash -x script.sh はスクリプトを一行ずつ実行し、エラーを分析します。
スクリプトヘッダーの自動生成
新しいシェルスクリプトを作成するたびに、/bin/bash、作成者、日付などの情報を自動的に生成します。.vimrc ファイルに以下の設定を保存します。
[root@centos8 ~]# vim .vimrc
set ignorecase
set cursorline
set autoindent
autocmd BufNewFile *.sh exec ":call SetHeader()"
func SetHeader()
if expand("%:e") == 'sh'
call setline(1,"#!/bin/bash")
call setline(2,"#********************************************************************")
call setline(3,"#Author: 作成者名")
call setline(4,"#Date: ".strftime("%Y-%m-%d"))
call setline(5,"#FileName: ".expand("%"))
call setline(6,"#Description: 註明されたスクリプト")
call setline(7,"#********************************************************************")
call setline(8,"")
endif
endfunc
autocmd BufNewFile * normal G
ウェブ上のスクリプトリソースのリモート実行
curl http://www.example.com/scripts/hello.sh | bash
# -s オプションはダウンロード情報を非表示にします
curl -s http://www.example.com/scripts/hello.sh | bash
curl -s http://www.example.com/scripts/sysinfo.sh | bash
変数
変数の種類
変数の有効範囲に基づいて変数の種類を分類します。
- 通常の変数:現在のシェルプロセスでのみ有効。他のシェルプロセス(子プロセスを含む)では無効です。
- 環境変数:現在のシェルプロセスとその子プロセスで有効です。
- ローカル変数:現在のシェルプロセス内の特定のコードスニペットで有効です。通常、関数を指します。
定義タイプによっても変数を分類します。
- 組み込み変数:PS1、PATH、UID、HOSTNAME、$$、BASHPID、PPID、$?、HISTSIZEなど。
- ユーザー定義変数
通常の変数
通常の変数は継承性がありません。つまり、親プロセスの変数は子プロセスで参照できません。変数を子プロセスや孫プロセスで使用するには、環境変数を使用する必要があります。
変数の代入
変数の値は複数のソースから取得できます。
- 直接文字列として
- 他の変数から
- Linux コマンドの実行結果から
変数の代入は一時的なもので、終端を閉じると自動的に削除されます(unset name で手動削除)。スクリプト内の変数はスクリプトが終了すると自動的に削除されます。
変数の代入:
name='value' 文字列を直接代入
name="$USER" 変数を参照
name=`COMMAND` または name=$(COMMAND) コマンドの実行結果をnameに代入。コマンドはバッククォート `` または $( ) で囲むことができます
変数の削除
unset name
シェルでの変数の命名規則
命名要件
- 大文字と小文字を区別します。
- プログラムの予約語や組み込み変数(if、forなど)を使用することはできません。
- 数字、アルファベット、アンダースコアのみを使用でき、数字で始めることはできません。
命名習慣
- 名前から役割が分かりやすいものを使います。ATMのような略称は避けてください。
- グローバル変数は大文字で。
- ローカル変数は小文字で。
- 関数名は小文字で。
- キャメルケース:StudentFirstName、複数の単語で構成され、各単語の先頭は大文字で。
- スネークケース:student_name
while ループ
特定ホストの開いているポートをスキャンするスクリプトを作成します。
#! /bin/bash
counter=1
target_host=192.168.1.1
while [ $counter -le 65535 ];do
if nc -z $target_host $counter &> /dev/null; then
echo $counter | tee -a open_ports.txt
fi
let counter++
done
1から100までの数を足すスクリプトを作成します。
#! /bin/bash
total=0;
index=1;
while ((index<=100));do
let total+=index;
let index++;
done;
echo $total
環境変数
環境変数は子プロセス(孫プロセスを含む)に親プロセスの変数を継承させますが、子プロセスの変数は親プロセスで使用できません。
子プロセスが親プロセスから継承した変数を変更すると、新しい値が孫プロセスに渡ります。
通常、環境変数はシステム設定ファイル(/etc/profileなど)で使用され、スクリプトではあまり使用されません。
# 宣言と代入
export name=value
declare -x name=value
# または2ステップで実現
name=value
export name
位置変数
スクリプトの後に続く文字列は、システム変数 $1, $2, ... に代入されます。
# スクリプトで自動的に使用される変数
$1, $2, ... 1番目、2番目の引数に対応
$0 コマンド自体、パスを含む
$* スクリプトに渡されたすべての引数、1つの文字列として扱われる
$@ スクリプトに渡されたすべての引数、それぞれが個別の文字列として扱われる
$# スクリプトに渡された引数の数
注意:$@ と $* はダブルクォートで囲われている場合のみ違いがあります
$? 変数
$? には直前のコマンドの最終的な実行状態が格納されます。この値は数字で、成功時は0、失敗時は1から255までの数値が格納されます。
カスタムステータスコード
スクリプトの実行後のステータスコードはスクリプトの最後のコマンドによって決まります。スクリプト内で exit 200 と記述すると、スクリプトのステータスコードは200になります。
for ループの2つの書き方
CentOS 7では両方の書き方がヘルプドキュメントに記載されていますが、CentOS 8およびRocky Linuxでは for NAME [in WORDS … ] ; do COMMANDS; done のみが記載されています。
ネットワーク内のオンラインIPを確認するスクリプトを作成します。
[root@rocky8 scripts]# for num in 1 2 3 4; do echo num=$num;done
num=1
num=2
num=3
num=4
1から100までの数を足すスクリプトを作成します。
[root@rocky8 scripts]# total=0;for num in {1..100};do let total+=num;done;echo total=$total
total=5050
[root@rocky8 scripts]#
九九の掛け算表を表示するスクリプトを作成します。
#! /bin/bash
for outer in {1..9};do
for inner in `seq $outer`;do
echo -e "${inner}X${outer}=$[outer*inner]\t\c"
done
echo
done
文字列テスト
test と [] を使用した文字列テストの用法
-z string 文字列が空かどうか。未定義または空の場合は真、それ以外は偽。
-n string 文字列が空でないかどうか。空でない場合は真、空の場合は偽。
string1 = string2 文字列が等しいかどうか。= の前後にスペースが必要。
string1 != string2 文字列が等しくないかどうか。
条件テスト
test expression
[ expression ] # testと等価ですが、[] を推奨します。
[[ expression ]] []の拡張版で、拡張正規表現やワイルドカードをサポートします。
注意:expressionの前後に空白が必要です。
スクリプト例
system_info.sh
#!/bin/bash
RED="\E[1;34m"
GREEN="echo -e \E[1;35m"
END="\E[0m"
. /etc/os-release
$GREEN----------------------システム情報--------------------$END
echo -e "ホスト名: $RED`hostname`$END"
echo -e "IPアドレス: $RED`hostname -I`$END"
echo -e "OSバージョン: $RED$PRETTY_NAME$END"
echo -e "カーネル: $RED`uname -r`$END"
echo -e "CPU: $RED`lscpu|grep '^Model name'|tr -s ' '|cut -d : -f2`$END"
echo -e "メモリ: $RED`free -h|grep Mem|tr -s ' ' : |cut -d : -f2`$END"
echo -e "ディスク: $RED`lsblk |grep '^nv' |tr -s ' ' |cut -d " " -f4`$END"
$GREEN---------------------------------------------------------$END