Shellスクリプト基礎知識(全11記事+1)
├─シェルスクリプトの基本事項!
├─変数と特殊変数について!
├─演算子「算術演算子」「比較演算子」について!
├─条件分岐「if」「case」について!
├─ループ処理「for」「while」について!
├─文字列置換「bash」「sed」について!
├─複数行のテキスト出力!ヒアドキュメントについて!
├─書式?戻り値?シェルスクリプト内の関数について!
├─シェルの組み込みコマンドについて!
├─クォートとは?コマンド置換とは?実現方法と内容の違いについて!
└─リダイレクトとは?標準入力・出力、標準エラー出力等について!
(補足)シェルスクリプトの設計書とは?必要な項目や書き方等を解説!
実際にスクリプトを書いていると同じ処理を何度もしないといけないことってありますよね?そのときに、条件分岐と繰り返し処理を組み合わせれば、特定の処理を指定した回数、処理してくれるようにすることが可能です。
一度覚えてしまえば便利なので、この機会に条件分岐と繰り返しをマスターしましょう。
ループ処理の基本概要
ループ処理は、特定の条件に基づいて同じ処理を繰り返すプログラムの構造を指します。この機能は、単純な繰り返しタスクから複雑なデータ処理まで、さまざまな用途で利用されます。この記事では、ループ処理の基本概念と、シェルスクリプトで使用される主要なループの種類について解説します。
ループ処理とは何か?
ループ処理は、プログラミングにおいて特定の処理を繰り返し実行するための重要な仕組みです。例えば、同じ計算を複数回行いたい場合や、ファイルのリストを一つずつ処理する場合に便利です。
![](https://www.pmi-sfbac.org/wp-content/uploads/2020/04/loop-logic-02.png)
初心者の方でも簡単に理解できる例として、学校の出席簿を考えてみましょう。一人ひとりの名前を呼び出して「出席」を記録する作業を手作業ではなく、自動化できるのがループ処理です。このように、一定の条件を満たすまで繰り返し処理を行うことで、手作業に比べて効率的かつエラーの少ない運用が可能になります。
ループ処理は、シンプルな条件から複雑な条件まで対応できる柔軟性があり、初心者だけでなくプロフェッショナルなエンジニアにも不可欠なツールといえます。
シェルスクリプトで使用されるループの種類
シェルスクリプトでは、ループ処理を実現するために以下の3つの主要な方法があります。それぞれの特徴を理解し、適切な場面で使い分けることが重要です。
forループ
forループは、指定した範囲やリスト内の要素を順番に処理するために使用されます。例えば、1から10までの数を順番に出力する場合や、ディレクトリ内のすべてのファイルを処理する場合に便利です。
for i in 1 2 3 4 5
do
echo "Number: $i"
done
whileループ
whileループは、指定した条件が真である間、処理を繰り返します。条件が動的に変化する場合や、特定の状態になるまで繰り返す処理に適しています。
count=1
while [ $count -le 5 ]
do
echo "Count: $count"
count=$((count + 1))
done
untilループ
untilループは、条件が偽である間、処理を繰り返します。whileループとは反対の動作をするため、条件が満たされるまで繰り返したい場合に適しています。
count=1
until [ $count -gt 5 ]
do
echo "Count: $count"
count=$((count + 1))
done
これらのループは、それぞれの用途や条件に応じて使い分ける必要があります。まずは基本的な構文を理解し、小さな例題で練習を重ねることで、実務でも役立つスクリプトを作成できるようになります。
forループの実践と活用
forループは、シェルスクリプトにおいて繰り返し処理を実現するための基本的な構文です。以下では、その基本構文と応用例、さらには避けるべき落とし穴について解説します。
forループの基本構文
for文は、for直後の変数名に、in直後に羅列したワードリスト( 値 1 値 2…)を順番に代入しながら、「do」から「done」によって囲まれた処理を実行するループする構文です。
C言語やjavaではカウンタを使ってカウンタの分だけ繰り返しますが、シェルスクリプトではワードリストを使って繰り返し処理をするという違いがあります。ただし、bashの場合はC言語やjavaと同じような記述にも対応しています。
コマンドの書式
for 変数 in ワードリスト
do
処理...
done
実際にfor文に対応したシェルスクリプトを記述記述してみます。これから作成するシェルスクリプトは、ワードリスト(「a」「b」「c」「d」)の順にループ処理を行い、変数の値が「b」の時のみ、変数の値をコンソールへ出力します。
for i in a b c d
do
if [ "${i}" = "b" ]; then
echo ${i}
fi
done
for文の実行例)
1 2 3 4 5 6 7 | [root@CentOS7 bin]# for i in a b c d > do > if [ "${i}" = "b" ]; then > echo ${i} > fi > done b |
結果としては表示されてはいませんが、変数の値が「b」の時、コンソールへの出力処理が終了した後も、ループ処理は(変数「d」が終了するまで)続いています。ハッキリいって無駄な処理です。
そこで使用されるのが「break」コマンドと「continue」コマンドです。
「continue」で処理のスキップが行え、「break」コマンドで、ループ処理から抜け出すことが可能です。「break」コマンドと「continue」コマンドは、予め組み込みコマンドとして、Linuxへ標準で用意されているコマンドです。
ベテランエンジニアとエンジニア初心者の差は、無駄な処理の後始末に顕著に表れます。ループ処理の実装時、それ以降必要のないロジックは、「スキップ」、もしくは「ループから抜ける」等、なるべく無駄を省く実装を心がけましょう。
ファイルや配列を使用したforループ
forループは、配列やファイル操作にも応用できます。これにより、より柔軟なスクリプトを作成可能です。
配列を使用した例:
以下は、配列内の要素を順に処理する例です。
array=("apple" "banana" "cherry")
for fruit in "${array[@]}"
do
echo "Fruit: $fruit"
done
ファイルを処理する例:
ディレクトリ内のすべてのファイルを処理するスクリプトの例です。
for file in /path/to/directory/*
do
echo "Processing file: $file"
done
このスクリプトは、指定したディレクトリ内のすべてのファイルに対して同じ処理を実行します。
実行時に避けたい落とし穴
forループを使用する際には、いくつかの注意点があります。これらを理解しておくことで、効率的でエラーの少ないスクリプトを作成できます。
典型的なエラー
- リストの空白処理:
リスト内の要素が空白を含む場合、意図しない動作を引き起こすことがあります。 - 改善策:
要素をダブルクォートで囲む。
for item in "item1" "item with space" "item3"
do
echo "$item"
done
- 予期しないファイル名:
ワイルドカード(*)を使用する際、不正なファイル名が含まれる場合があります。 - 改善策:
ファイル名のバリデーションを追加する。
非効率な記述:
- 不必要な処理や複雑な条件を含めると、スクリプトの実行速度が低下します。
- 改善策: 繰り返し処理を最適化し、必要最低限の処理に絞る。
forループは、シンプルな処理から複雑な応用まで幅広く対応できる強力なツールです。
基本構文を理解し、配列やファイル操作と組み合わせることで、実務でも役立つスクリプトを効率的に作成できるようになります。同時に、典型的なエラーや非効率な記述を避けるための注意点を押さえておくことが重要です。
whileループの実践と活用
whileループは、指定した条件が満たされている間、特定の処理を繰り返し実行する構文です。この柔軟性の高いループ処理は、動的な条件に応じた繰り返し作業に最適で、スクリプトを効率化する際に欠かせない要素となります。以下では、基本構文から応用例までを解説します。
while文の基本構文
while文は、条件文の実行結果が真であるかぎり、ループ中の処理を繰り返し実行する構文です。一番最後の終了コードが「0」か「0以外」かを判定し、「0」なら「do」から「done」によって囲まれた処理を実行するループ構文です。
コマンドの書式
while コマンドリスト
do
処理...
done
while文に対応した記述例)
count=1
while [ ${count} -lt 3 ]
do
echo ${count}
count=$(expr ${count} + 1)
done
while文に対応したスクリプトの実行例)
1 2 3 4 5 6 7 8 | [root@CentOS7 bin]# count=1 [root@CentOS7 bin]# while [ ${count} -lt 3 ] > do > echo ${count} > count=$(expr ${count} + 1) > done 1 2 |
1行目:「count」という変数を用意し、これに数値の「1」を代入します。
2行目:「count」の値が3より小さいかどうかを調べます。
4行目:この結果は真(3より小さい)ので、echoコマンドでその値「1」を出力します。
5行目:「count」の値に1を加えます。これで1回目のループが終了し、判定文のところに戻ります。
2行目:「count」の値は2になっていますが、まだ3より小さいのでもう一度doの中の処理を実行します。
4-5行目:echoで2を表示し、1を加えます。2回目のループが終了し、whileの判定文に戻ります。
2行目:「count」は3になっているため、「3より小さい」という条件に対しては偽となり、0以外(false)を返します。この時点で、whileが終了します。
無限ループを防ぐ方法
whileループを使用する際、条件が適切に設定されないと無限ループが発生することがあります。無限ループはスクリプトの動作を停止させ、システムに負荷をかける原因となるため、以下の方法で防ぐことが重要です。
条件を明確にする
ループの終了条件を明確に定義することで、意図しない無限ループを防ぐことができます。
count=1
while [ $count -le 5 ]
do
echo "Count: $count"
count=$((count + 1))
done
タイムアウトを設定する
ループが一定時間以上続く場合に強制的に終了させる仕組みを導入します。
start_time=$(date +%s)
timeout=10
while [ 条件 ]
do
current_time=$(date +%s)
elapsed_time=$((current_time - start_time))
if [ $elapsed_time -ge $timeout ]; then
echo "タイムアウトによりループを終了します。"
break
fi
# 他の処理
条件の更新
done
実用例:サーバーログ監視
whileループは、サーバー運用やシステム監視で特に活用されることが多いです。以下は、サーバーログをリアルタイムで監視し、特定のエラーが発生した際にアラートを出すスクリプトの例です。
log_file="/var/log/syslog"
error_keyword="ERROR"
while :
do
if tail -n 10 "$log_file" | grep "$error_keyword" > /dev/null; then
echo "エラーが検出されました: $error_keyword"
break
fi
sleep 10
done
このスクリプトは、10秒ごとにサーバーログをチェックし、エラーキーワードが検出された場合にループを終了します。
whileループは、条件に応じた繰り返し処理を実現するための強力なツールです。適切な条件設定やタイムアウトの導入により、安全かつ効率的なスクリプトを作成できます。また、実務での応用例を通じて、その有用性を最大限に引き出すことが可能です。
条件分岐とループの組み合わせ
条件分岐とループを組み合わせることで、より柔軟かつ効率的な処理を実現できます。この組み合わせにより、複雑な条件下での繰り返し処理が可能となり、スクリプトの応用範囲が広がります。
if文との組み合わせ
if文は、条件分岐を実現するための基本的な構文です。ループと組み合わせることで、各ループの実行内容を条件に応じて変更することができます。
例1: ファイルの種類を判別する
for file in /path/to/directory/*
do
if [ -f "$file" ]; then
echo "$file is a regular file."
elif [ -d "$file" ]; then
echo "$file is a directory."
else
echo "$file is of an unknown type."
fi
done
このスクリプトは、ディレクトリ内の各項目を確認し、それが通常のファイル、ディレクトリ、またはそれ以外のタイプであるかを判別します。
例2: 特定の条件を満たすファイルを処理する
for file in /path/to/directory/*
do
if [[ "$file" == *.txt ]]; then
echo "Processing text file: $file"
# テキストファイルに対する処理を記述
fi
done
このスクリプトは、.txt拡張子を持つファイルに対して特定の処理を行います。
case文を使った柔軟な処理
case文は、複数の条件に基づいて処理を分岐させる際に便利です。特に、条件が多い場合やパターンマッチングが必要な場合に有用です。
例1: ファイル拡張子ごとの処理
for file in /path/to/directory/*
do
case "$file" in
*.txt)
echo "$file is a text file."
;;
*.sh)
echo "$file is a shell script."
;;
*.log)
echo "$file is a log file."
;;
*)
echo "$file is of an unknown type."
;;
esac
done
このスクリプトは、ファイルの拡張子に応じて異なる処理を実行します。
例2: ユーザー入力に基づく動作の変更
while true
do
echo "Select an option:"
echo "1) Start"
echo "2) Stop"
echo "3) Exit"
read choice
case "$choice" in
1)
echo "Starting the process…"
;;
2)
echo "Stopping the process…"
;;
3)
echo "Exiting…"
break
;;
*)
echo "Invalid option. Please try again."
;;
esac
done
このスクリプトは、ユーザー入力に基づいて動作を変更し、適切な選択がされるまで繰り返します。
条件分岐とループを組み合わせることで、柔軟性と効率性を兼ね備えたスクリプトを作成できます。特にif文やcase文を活用することで、条件に応じた動的な処理を簡潔に記述でき、実務での応用範囲が大きく広がります。
シェルスクリプトにおけるループ処理の応用例
ループ処理はシェルスクリプトの中で強力なツールであり、特にファイル操作や自動化、ログ解析などの場面で広く活用されています。以下に、実務での応用例をいくつか紹介します。
ファイルリスト処理の実践例
シェルスクリプトでのループ処理は、ファイル操作を効率化するために頻繁に使用されます。以下は、その一例です。
例1: ファイルの一括リネーム
for file in /path/to/directory/*
do
mv "$file" "${file%.txt}_backup.txt"
done
このスクリプトは、指定したディレクトリ内のすべての.txtファイルをリネームし、ファイル名に_backupを追加します。
例2: ファイルサイズのチェック
for file in /path/to/directory/*
do
size=$(du -h "$file" | cut -f1)
echo "$file: $size"
done
このスクリプトは、ディレクトリ内のすべてのファイルのサイズを取得し、それを表示します。
ネストループの活用方法
ネストループは、複雑な条件下で複数の要素を処理する際に便利です。以下に、多層ループを使用した具体例を示します。
例1: ディレクトリとファイルの再帰的処理
for dir in /path/to/parent/*
do
if [ -d "$dir" ]; then
for file in "$dir"/*
do
echo "Processing file: $file in directory: $dir"
done
fi
done
このスクリプトは、親ディレクトリ内のすべてのサブディレクトリを探索し、その中にあるファイルを処理します。
例2: 配列と条件分岐の組み合わせ
arrays=("array1" "array2")
for array in "${arrays[@]}"
do
for element in "${array[@]}"
do
if [[ $element == "pattern" ]]; then
echo "Match found: $element"
fi
done
done
このスクリプトは、配列内の各要素をチェックし、特定のパターンに一致する要素を出力します。
ログ解析や自動化スクリプトでの使用例
シェルスクリプトでのループ処理は、業務の効率化や自動化において重要な役割を果たします。以下に、実際に使用される例を示します。
例1: ログ解析
log_file="/var/log/syslog"
keywords=("ERROR" "WARNING" "CRITICAL")
for keyword in "${keywords[@]}"
do
echo "Searching for: $keyword"
grep "$keyword" "$log_file"
done
このスクリプトは、指定されたキーワードごとにログファイルを検索し、一致する行を出力します。
例2: サーバーメンテナンスの自動化
servers=("server1" "server2" "server3")
for server in "${servers[@]}"
do
echo "Updating packages on $server"
ssh "$server" "sudo apt update && sudo apt upgrade -y"
done
このスクリプトは、複数のサーバーにSSHで接続し、パッケージの更新を自動的に行います。
シェルスクリプトでのループ処理は、さまざまな業務の効率化に寄与します。ファイル操作、複雑なネストループ、ログ解析やサーバー管理の自動化など、応用範囲は非常に広く、適切に活用することで大幅な時間短縮が期待できます。
ループ処理の実践とよくある疑問への回答
ループ処理は、シェルスクリプトで繰り返し作業を効率的に処理するための重要な構文です。本記事では、実用的なスクリプト例を通じてループ処理の実践方法を解説するとともに、初心者が抱きやすい疑問についての解決策を提示します。
この記事を参考に、ループ処理を活用したスクリプト作成スキルをさらに向上させてください。
ループ処理を使った実用スクリプト例
ループ処理は日々の業務を効率化するための基本的な構文です。以下に実用的なスクリプト例をいくつか紹介します。
例1: ファイル名の変更
for file in /path/to/files/*.txt
do
mv "$file" "${file%.txt}_renamed.txt"
done
このスクリプトは、指定したディレクトリ内のすべての.txtファイルをリネームし、新しい名前に_renamedを追加します。
例1: ファイル名の変更1
servers=("server1" "server2" "server3")
for server in "${servers[@]}"
do
echo "Checking status of $server"
ssh "$server" "uptime"
done
このスクリプトは、リストにあるサーバーにSSHで接続し、それぞれの稼働時間(uptime)を確認します。
例1: ファイル名の変更2
for job in {1..10}
do
echo "Executing job $job"
./execute_job.sh "$job"
done
このスクリプトは、連番で指定されたジョブを順番に実行します。
ループ処理に関するよくある質問とその回答
初心者がループ処理を使用する際に抱えがちな疑問について、回答をまとめました。
ループ処理で無限ループに陥った場合の対処法は?
無限ループが発生した場合、Ctrl+Cを使用して強制的にスクリプトを終了できます。また、ループ条件を適切に設定することで無限ループを防ぐことが重要です。
ファイルが空の場合、ループ処理が失敗します。どうすればいいですか?
ファイルが空の場合を考慮したエラーハンドリングをスクリプトに追加することをお勧めします。
if [ ! -s "$file" ]; then
echo "File is empty: $file"
continue
fi
配列をループで使用する方法は?
配列を使う場合は、${array[@]}を用いて要素を順番に取得できます。
array=("item1" "item2" "item3")
for item in "${array[@]}"
do
echo "$item"
done
ループの実行速度を改善する方法は?
実行速度を改善するためには、無駄な処理を省き、適切なコマンドを選択することが重要です。また、可能であれば並列処理を検討してください。
ループ処理内でエラーが発生した場合、スクリプト全体を停止するには?
set -eをスクリプトの冒頭に記述することで、エラーが発生した時点でスクリプト全体を停止できます。
よくあるエラーとトラブルシューティング
スクリプトを開発および実行する際には、さまざまなエラーやトラブルに直面することがあります。その中でも、無限ループの発生や予期しない動作は、特に初心者にとって悩ましい問題です。本セクションでは、よくあるエラーの原因を解説し、それらを防ぐための実践的な解決策とデバッグ手法を紹介します。
無限ループの原因と解決策
無限ループは、ループ処理の設計における一般的な問題の一つです。スクリプトが意図せずに終了しない場合、システムリソースに負荷をかける可能性があります。以下に、無限ループが発生する典型的な原因とその解決策を解説します。
原因1: 終了条件が設定されていない
終了条件が適切に設定されていない場合、ループが永遠に実行されることがあります。
count=1
while [ $count -le 10 ]
do
echo "Count: $count"
# count の値が更新されていないため無限ループになる
done
解決策:ループ内で条件を満たすように変数を更新します。
count=1
while [ $count -le 10 ]
do
echo "Count: $count"
count=$((count + 1))
done
原因2: 条件が常に真になる
ループ条件が常に真の場合も無限ループが発生します。
while true
do
echo "This will run forever."
done
解決策:必要に応じて、条件に依存したbreak文を追加します。
while true
do
echo "This will stop after one iteration."
break
done
原因3: 外部リソースの状態に依存
外部リソースの状態に依存している場合、リソースが期待通りに更新されないとループが終了しません。
解決策:外部リソースの状態を適切に監視し、タイムアウトやエラーハンドリングを導入します。
start_time=$(date +%s)
timeout=30
while some_external_condition
do
current_time=$(date +%s)
elapsed_time=$((current_time - start_time))
if [ $elapsed_time -ge $timeout ]; then
echo "Timeout reached, exiting loop."
break
fi
done
スクリプトのデバッグ方法
スクリプトを開発および実行する際には、さまざまなエラーやトラブルに直面することがあります。その中でも、無限ループの発生や予期しない動作は、特に初心者にとって悩ましい問題です。本セクションでは、よくあるエラーの原因を解説し、それらを防ぐための実践的な解決策とデバッグ手法を紹介します。
手法1: set -x を使用
set -xをスクリプトに追加することで、各コマンドの実行内容を詳細に表示できます。
#!/bin/bash
set -x
for i in {1..5}
do
echo "Processing $i"
done
set +x
手法2: echoを活用
スクリプトの途中で変数の値や条件を出力することで、問題の発生箇所を特定します。
#!/bin/bash
count=1
while [ $count -le 5 ]
do
echo "Current count: $count"
count=$((count + 1))
done
手法3: シンタックスチェッカーを使用
スクリプトの構文エラーをチェックするツールを活用します。
bash -n script.sh
手法4: デバッガツールを使用
bashdbなどのデバッガツールを使用して、ステップバイステップでスクリプトを解析します。
手法5: 小さな部分に分割してテスト
スクリプトを小さな部分に分割し、それぞれを個別にテストします。
無限ループの防止とスクリプトのデバッグは、安定した動作を確保するために重要なステップです。適切な条件設定、エラーハンドリング、そしてデバッグ手法を組み合わせることで、問題を迅速に特定し解決できます。
まとめ
![まとめ](https://www.pmi-sfbac.org/wp-content/uploads/2017/11/matome.jpg)
ループ処理は、プログラムの効率性を大幅に向上させる基本的な構文の一つです。特に、繰り返しタスクの自動化や動的条件に応じた処理を行う場合に欠かせないツールです。この記事では、ループ処理の基礎から応用例、さらにはエラー対応やトラブルシューティングまで幅広く解説しました。
重要な振り返りポイント
- 基本構文の理解:
forループ、whileループ、untilループなど、状況に応じた適切な選択が必要。 - 応用例の活用:
ファイル操作やログ解析、条件分岐との組み合わせで、実務に直結するスクリプトを作成可能。 - トラブルシューティング:
無限ループを防ぐ設計と、デバッグツールの活用により、安定したスクリプトの開発が実現。