見出し画像

[入門] Bash Codex ~実践Bashによる効率的なシステム管理

Bashに関する入門者向けのガイド
BashはLinuxの標準シェルとして強力なスクリプト機能を提供します。この記事では、Bashの基礎的な使い方から実践的なスクリプトの書き方まで解説します。変数やif文、for文といった基礎構文や、文字列処理、変数置換、ループ処理などの実用テクニックを網羅しています。



第1章 Bashの概要

Bashは、LinuxやmacOSのシステムで広く使用されているシェルおよびスクリプト言語です。主に、システムの管理や操作、タスクの自動化のために利用されます。Bashには次のような特徴があります。

  • コマンドラインインターフェースによる操作の実行

  • パイプラインを使用した複数のコマンドの連携

  • テキスト処理だけでなく、ファイルやプロセスの管理も可能

  • スクリプトを利用した処理の自動化

  • UNIXの伝統を引き継いだコマンド体系とシンタックス

  • LinuxやmacOSなど多くのシステムにデフォルトで搭載されている

Bashの歴史
Bashは1989年にBrian Foxによって、Bourneシェル(`sh`)の無料の代替品として作成されました。それ以来、ほとんどのLinuxディストリビューションでデフォルトのシェルになっています。「Bash」という名前は、Stephen Bourneによって書かれたオリジナルのUnixシェルに基づいていることから、「Bourne-Again SHell」を意味します。

Bashの歴史の主なマイルストーンは以下の通りです。

  • 1989年 - Brian FoxがBash 1.0をリリース

  • 1996年 - Chet Rameyが主要なBashのメンテナになる

  • 2009年 - 多くの新機能が追加されたBash 4.0がリリース

  • 2019年 - 更なる改良が加えられたBash 5.0がリリース

Bash 5の主な変更点

  • ファイル名や変数の補完速度の向上

  • [[ ]]内でパターンマッチングに`||`や`&&`が使用可能に

  • 算術において`**`指数演算子が追加

  • [[ ]]パターンマッチングが周囲のテキストと一貫性を持つように改良

  • 行を配列に読み込むための`mapfile`ビルトインが追加

詳細はBash 5.0の変更履歴↗をご覧ください。

Bashの起動
Bashセッションを開始する方法はいくつかあります。

  • LinuxまたはmacOSのターミナルエミュレータプログラムを開く
    (例:GNOME Terminal, Konsole, Terminal.app)

  • SSHを使用してリモートシステムにログインし、デフォルトのBashシェルを使用する

  • ターミナル多重化ツール(tmux ↗screen ↗など)を使用する

  • 既存のシェルから`bash`を実行してサブシェルを起動する

対話的な使用には、ターミナルプログラムがコマンドラインの編集や出力の表示に便利な方法を提供します。Bashの動作をカスタマイズするための多くの設定オプションが利用可能です。

ヘルプの表示
Bashはコマンド、変数などのための組み込みヘルプを提供しています。役立つヘルプのトピックには以下のものがあります。

  • `help` - すべてのビルトインに対するヘルプ

  • `help` トピック - 特定のトピックについての詳細なヘルプ

  • `man bash` - Bashのマニュアルページ

  • `info bash` - Bashの詳細なリファレンス情報ページ

  • `bash --help` - Bashの全コマンドラインオプション

コマンドがビルトインであるか外部プログラムであるかを判断するには、`type`と`which`コマンドを使用します。

ビルトインによる管理
Bashには、外部プロセスを起動せずにシステム管理を行うための多くのビルトインコマンドが含まれています。一般的なビルトインには以下のものがあります。

  • `cd` - ディレクトリの変更

  • `echo` - テキストの出力

  • `exit` - シェルの終了

  • `help` - ビルトインのヘルプ

  • `pwd` - 現在の作業ディレクトリの出力

  • `set` - シェルオプションの設定

  • `source` - ファイルからコードをロード

  • `type` - コマンドのタイプを表示

すべてのリストについては`help`を参照してください。ビルトインにより、シェルはすべてを別々のバイナリで起動するよりも効率的になります。

パイプラインによる連携
パイプラインは、一つのコマンドの出力を別のコマンドの入力にリダイレクトすることで、複数のコマンドを連鎖させることを可能にするBashの機能です。これは強力なデータフローの機能を提供します。

ps aux | grep bash | wc -l

このコマンドは、`ps`の出力を`grep`にパイプライン化し、"bash"をフィルターにかけ、実行中の"bash"プロセスの数を数えています。

を`wc -l`にパイプライン化して行数を数えます。パイプラインにより、不必要な中間ファイルの作成を避けることができます。


第2章 対話的なシェルの操作

シェルは、ユーザーがオペレーティングシステムと対話するためのコマンドラインインターフェースです。Bashは、LinuxやmacOSシステムで最も一般的なシェルの一つです。ユーザーが入力したコマンドを受け付け、それらのコマンドに基づいてプログラムを実行します。

対話モード
Bashを対話モードで開始すると、例えばターミナルプロンプトから、それはプロンプトを表示し、ユーザーの入力を待ちます。

$ 

デフォルトのプロンプトは、通常のユーザーの場合は `$`、ルートユーザーの場合は `#` です。プロンプトは後述の方法でカスタマイズできます。

コマンドの実行
Bashでコマンドを実行するには、プロンプトでコマンドを入力し、Enterキーを押します。

$ echo "Hello World"
Hello World

これは、"Hello World"を表示するためにecho ↗という組み込みコマンドを実行します。

他の一般的なBashコマンドには以下のものがあります。

そして他にもたくさんあります!コマンドが組み込みのものか外部のものかを調べるためには、`type`コマンドや`which`コマンドを使用します。

パイプラインによる連携
Bashでは、パイプライン演算子 `|` を使用して複数のコマンドを連鎖させることができます。これにより、あるコマンドの標準出力が次のコマンドの標準入力に接続されます。

$ ps aux | grep bash

これは `ps aux` を実行してプロセスを一覧表示し、その結果を `grep` にパイプライン化して "bash" を含む行をフィルタリングします。パイプラインにより、一時ファイルが不要になります。

コマンド補完
Bashは、ファイル名、ディレクトリ名、コマンド名などの補完をサポートしています。名前の一部を入力した後、Tabキーを押すと残りの部分を補完します。Tabキーを2回押すと、可能な補完候補を一覧表示します。これにより、多くの入力作業を省略できます。

補完は以下のものに対して動作します。

  • コマンド名

  • ファイル名

  • ディレクトリ名

  • ホスト名

  • 変数

  • ユーザー名

  • オプション

コマンド履歴
Bashは以前に使用したコマンドを記録し、これらのコマンドを参照したり再利用したりすることを可能にします。履歴は `~/.bash_history` ファイルに保存されます。

役立つ履歴コマンドは以下のとおりです。

  • `history` - コマンド履歴を表示

  • `!n` - `n`番目のコマンドを繰り返し実行

  • `!<prefix>` - `<prefix>`で始まる最後のコマンドを繰り返し実行

  • `!!` - 前のコマンドを繰り返し実行

  • `Ctrl-R` - 履歴を逆順に検索

コマンド履歴を使うと、以前のコマンドを素早く呼び出し、実行することができます。

終了
対話的なプロンプトからBashシェルを終了するには、`exit`という組み込みコマンドを使用します。

$ exit

これにより、ターミナルウィンドウまたはSSHセッションが閉じられ、Bashプロセスが終了します。



第3章 パイプライン

Bashのパイプラインでは、`|`演算子を使用して一つのコマンドのstdoutを別のコマンドのstdinに接続することができます。これにより、不要な中間ファイルを避けて、データ処理コマンドのシーケンスを簡単に連鎖させることができます。

パイプラインの利点
Bashでパイプラインを使用する利点は以下のとおりです。

  • 中間の一時ファイルを避ける

  • プロセス間でデータをストリーム化する

  • それぞれのコマンドを組み合わせることで複雑なタスクを実現する

  • 柔軟なアドホックな組み合わせ

  • 大量のデータに対して効率的

パイプラインは、大きな多機能アプリケーションを持つのではなく、シンプルな単一目的のプログラムを連鎖させるというUnixの哲学を支えています。

パイプラインの例
たとえば、実行中のプロセスの一覧から特定の条件を満たすものを抽出するためには、次のようにコマンドを連鎖させます。

$ ps aux | grep bash

これは、ps aux ↗ の出力を grep ↗ にパイプライン化し、"bash"を含む行をフィルタリングします。

より複雑なデータ処理のパイプライン。

$ netstat -an | grep ':443' | cut -d ' ' -f1 | uniq -c | sort -n
  1. `netstat -an` - 開いているポートを一覧表示

  2. `grep ':443'` - ポート443に対してフィルタリング

  3. `cut -d ' ' -f1` - 最初のフィールドを抽出

  4. `uniq -c` - ユニークな項目をカウント

  5. `sort -n` - 数値でソート

パイプラインを使用することで、一つのデータストリームのために複数の一時ファイルを作成することを避けられます。

パイプラインの接続
パイプラインは `tee` を使用して連鎖させることができます。

$ ps aux | tee processes.txt | grep bash

tee ↗ はパイプラインを分割し、`ps` の出力をファイルと `grep` の両方に送ります。

複数のプロセスも、xargs ↗ を使用してテキストストリームをコマンドライン引数として他のコマンドに渡すことができます。

$ ps aux | grep bash | grep -v grep | awk '{print $2}' | xargs -n1 renice +10

第4章 変数とデータ型

Bashはデータを保存するための変数をサポートしています。変数は英数字の名前で指定し、`=`を使って値を割り当てます。

name="John"

変数の値にアクセスするには、変数名の前に`$`を付けます。

echo $name
John

変数名は大文字と小文字を区別し、文字、数字、アンダースコアを含むことができます。

グローバルスコープ
スクリプトのどこからでもアクセスできる変数をグローバル変数といいます。スクリプト内のすべての関数からアクセスできます。以下にその例を示します。

bashCopy codename="John"  # This is a global variable

function greet() {
  echo "Hello, $name"
}

greet  # Prints: Hello, John

ローカルスコープ
関数内で定義された変数はその関数内でのみ有効であり、そのスコープはローカルです。関数内でlocalキーワードを使って変数を定義することでローカル変数を作成できます。以下にその例を示します。

bashCopy codefunction greet() {
  local name="John"  # This is a local variable
  echo "Hello, $name"
}

greet  # Prints: Hello, John
echo $name  # Prints nothing because 'name' is local to the function

この例では、`name`変数は`greet`関数内でのみアクセス可能です。そのため、関数の外から`name`変数を表示しようとしても何も表示されません。

これらが変数の基本的なスコープですが、他にも環境変数という特殊なスコープの変数が存在します。環境変数は、シェルセッションを越えて他のプロセスと共有することが可能な変数です。環境変数は`export`コマンドで定義されます。

環境変数

bashCopy codeexport name="John"  # This is an environment variable

上記のように`export`コマンドを使って定義された変数は、そのシェルと子プロセス間で共有されます。環境変数は大文字で書かれることが多いです。

データ型
Bashの変数は明示的なデータ型を持ちません。すべてがデフォルトでは文字列として扱われます。特殊な操作により、変数を数値などの他の型として扱うこともできます。

  • 文字列 - 任意のテキスト

  • 整数 - 整数

  • 配列 - 要素の順序付きリスト

  • 連想配列 - キーと値のペアのマップ

文字列はスペースや特別な意味を持つ値を除けば、クォートは必要ありません。

データ型のチェック
変数の型をチェックするには、パラメータ展開修飾子を使用します。

str="hello"
num=123

[[ $str =~ ^[0-9]+$ ]] && echo "$str is a number" || echo "$str is not a number"
[[ $num =~ ^[0-9]+$ ]] && echo "$num is a number" || echo "$num is not a number"

これは`^[0-9]+$`を使用して、値が数値のパターンに一致するかどうかをチェックします。

データ型の変換
文字列は算術評価を用いて数値に変換することができます。

str="5"
num=$((str + 1))
echo $num 
6

`(( ))`は変数を整数の算術式として扱います。

文字列は`split`を使って配列に変換できます。

str="apple orange lemon"
arr=($(echo $str | tr ' ' '\n'))
echo ${arr[1]}
orange

これは文字列をスペースで分割して配列にします。


第5章 制御フロー (if, case, for, while)

Bashは、条件分岐やループを含む一般的な制御フロー構造を提供しており、スクリプト作成に使用できます。

if文
`if`文はコマンドを評価し、その終了ステータスに基づいて追加のコードを実行します。

if ping -c 1 google.com; then
  echo "Network OK"
else
  echo "Network down"
fi

`ping`コマンドの終了ステータスにより、表示されるメッセージが決まります。

簡単な例は次の通りです。

if COMMAND; then
  STATEMENTS
elif COMMAND; then ## 任意のelse if
  STATEMENTS
else ## 任意のelse
  STATEMENTS  
fi

case文
`case`文は変数が一致するパターンを確認します。

case $LANG in
  en_US.UTF-8) echo "Locale is US English";;
  fr_FR.UTF-8) echo "Locale is French";;
  *) echo "Unknown locale";;
esac

`$LANG`はパターンに対して確認されます。`*;`は何でも一致します。

forループ
`for`ループは項目のリストを繰り返します。

for i in apple banana orange; do
  echo "Item: $i"
done

これは項目のリストを繰り返し、`$i`をそれぞれの項目に設定します。

whileループ
`while`ループは条件が真である間、ステートメントを実行します。

count=1
while [ $count -le 5 ]; do
   echo $count
   count=$((count + 1)) 
done

これは1から5までの数を出力します。条件は各繰り返しでチェックされます。

breakとcontinue
`break`は現在のループを終了します。`continue`は現在の繰り返しの残りをスキップします。


第6章 関数

関数は、Bashスクリプト内で再利用可能なコードブロックを提供します。

関数とは何か
関数は`function`キーワードを使用して定義します。

function sayHello {
   echo "Hello $1"
}

関数の本体は `{` と `}` の間に記述します。
関数を呼び出すには。

sayHello "John"

位置パラメーター(例えば `$1`)は引数として渡されます。関数は、繰り返しコードのコピーペーストをせずに、コードの再利用を可能にします。

パラメータ
関数は`local`を使用して名前付きパラメータを定義できます。

function calcArea {
  local length=$1
  local width=$2
  
  echo $((length * width))
}

calcArea 10 20 

`local`は関数内のローカル変数を定義します。

戻り値
`return N`を使用して終了コードを返します。

function isFile {
  [[ -f "$1" ]]
  local result=$?
  
  return $result
}

if isFile "/etc/passwd"; then
  echo "File exists"
fi

`return`コードは呼び出し後に`$?`として利用可能です。


第7章 スクリプティング

Bashスクリプティングは、タスクを自動化するためのスクリプトを書くことを含みます。この章では、スクリプトの作成と実行の基本について説明します。

スクリプティングとは
Bashスクリプトは、Bashコマンドを含むプレーンテキストファイルです。実行すると、スクリプト内のコマンドが対話型プロンプトで入力されたかのように実行されます。

スクリプトを用いることで、繰り返しのタスクの自動化、複雑なワークフローのカプセル化、長時間実行するプロセスの実装が可能となります。

スクリプトの作成
Bashスクリプトは通常、インタープリタを指定するシェバング行から始まります。

#!/bin/bash

その後にスクリプトコマンドが続きます。

#!/bin/bash

echo "Hello World"

このファイルを`script.sh`として保存し、`chmod +x script.sh`で実行可能にします。

スクリプトは一般的に以下の構造を持ちます。

  • シェバング行

  • コメント

  • コマンド

    • 関数定義

    • メインのロジック

変数定義
スクリプトは多くの場合、設定のための変数定義から始まります。

#!/bin/bash

## Config 
OUTPUT_DIR=/tmp
LOG_FILE=/var/log/app.log

意味のある変数名と大文字小文字の規則を使用します。

関数定義
再利用可能なロジックは関数で定義できます。

function process_data {
  ## Code to process data
  echo "Processing..."
}

スクリプトはモノリシックなコードではなく、モジュラーな関数に分割します。

メインの処理
メインのロジックはフロー制御と呼び出しを処理します。

## Main logic
process_data
archive_logs
send_report

読みやすさのために、トップレベルのロジックはシンプルで簡潔に保ちます。

スクリプトの実行
スクリプトを実行するには。

./script.sh

先頭の `./` は、現在のディレクトリでこのスクリプトを実行することを示しています。

スクリプトは、`$PATH`内のディレクトリに保存されていれば、通常のコマンドと同様に呼び出すこともできます。

パラメータの受け渡し
任意のコマンドと同様に、引数をスクリプトに渡すことができます。

./script.sh arg1 arg2

`$1`、`$2`で位置パラメータにアクセスします。


第8章 スクリプトのソース化

スクリプトファイルを実行するだけでなく、Bashはスクリプトをソース化して現在のシェルコンテキストで実行することができます。

ソース化とは
スクリプトをソース化するとは、新しいプロセスを生成するのではなく、現在のシェルでそれを実行することを意味します。これにより、変数、エイリアス、関数を既存のシェルセッションにエクスポートすることができます。

スクリプトのソース化
スクリプトをソース化するには、`.`(ドット)コマンドを使用します。

. ~/.bash_profile

これと同等の操作を行う別のコマンドとして`source`が存在します。これは以下のように行います。

source ~/.bash_profile

これらのコマンドはどちらも、`~/.bash_profile`内のコマンドを現在のシェルで実行します。その結果、`~/.bash_profile`で定義された関数や変数が現在のシェルで利用可能となります。

ソース化されたスクリプトの内容
ソース化されたスクリプトの典型的な内容。

  • エクスポートされた変数

export EDITOR=vim
  • エイリアス

alias ll='ls -l'
  • 関数

function myfunc {
  ## Code
}

これにより、定義が現在のシェルで利用可能になります。

ソース化可能なスクリプトの作成
スクリプトをソース化するための手順は次の通りです。

  • 変数には`export`を使用する

  • エイリアスを定義する

  • 関数を定義する

  • `if`/`for`などの制御フローコマンドは避ける

その後、ソース化します。

. myscript.sh

第9章 文字列の操作

Bashには、文字列やテキストを操作するための多くの方法があります。

文字列
文字列は引用符を使用して定義することができます。

name="John"

スペースやタブのような特殊文字は引用符で囲む必要があります。

greeting="Hello world"

複数行の文字列にはヒアドキュメントを使用します。

cat <<EOF
This is 
a multiline
string
EOF

`echo`や`printf`のようなビルトインは文字列を出力します。

文字列操作
文字列操作一般的な文字列操作は次の通りです。

  • 長さ - `${#string}`

  • 部分文字列 - `${string:0:5}`

  • 検索 - `[[ $string == pattern ]]`

  • 分割 - `${string// /}`

パラメータ展開 ↗は多くの文字列操作をサポートします。

パターンマッチング
Bashはグロブパターンによるパターンマッチングをサポートしています。

  • `*` - 何でも一致

  • `?` - 単一の文字と一致

  • `[abc]` - a、b、またはcと一致

パターンはファイル名の展開やcase文で使用されます。

ファイル名の操作
ファイル名の操作一般的なファイル名の操作は次の通りです。

  • ベースネーム - `${filename##*/}`

  • ディレクトリ - `${filename%/*}`

  • 拡張子 - `${filename##*.}`

パラメータ展開 ↗はファイル部分を抽出するのを容易にします。


第10章 配列データ型

Bashは基本的なデータ型の一つとして配列をサポートしています。

配列
配列とは要素の順序付きリストです。配列のインデックスは0から始まります。

fruits[0]=Apple
fruits[1]=Banana
fruits[2]=Orange

要素には`${fruits[i]}`を使用してアクセスします。

配列に追加する。

fruits+=("Mango")

組み込み関数の`echo`や`printf`は配列を出力できます。

配列操作
配列操作一般的な配列操作は次の通りです。

  • 長さ - `${#array[@]}`

  • スライス - `${array[@]:0:3}`

  • 結合 - `${fruits[*]}`

  • ソート - `sorted=($(sort <<<"${fruits[*]}"))`

パラメータ展開 ↗は配列をサポートします。

配列の反復処理
forループを使用して配列を反復処理します。

for i in "${fruits[@]}"; do
  echo ${i} 
done

インデックスを使用して反復処理します。

for ((i=0; i<${#fruits[@]}; i++)); do
  echo ${fruits[i]}
done 

連想配列
連想配列は文字列をインデックスとして使用します。

declare -A sounds

sounds[dog]="bark"
sounds[cat]="meow"

要素には`${sounds[dog]}`を使用してアクセスします。


第11章 ファイルとディレクトリ操作

Bashには、ファイルシステム上のファイルやディレクトリを操作するための多くのコマンドがあります。

ファイルとフォルダの一覧表示
ディレクトリの内容を一覧表示するには、`ls`コマンドを使用します。

ls

一般的なオプションは次の通りです。

  • `-l` - 詳細を含む長い一覧表示

  • `-a` - 隠しファイルを含む

  • `-h` - 人間が読めるサイズ表示

  • `-t` - 最終更新日でソート

詳細については`man ls`を参照してください。

ディレクトリの変更
現在のディレクトリを変更するには、`cd`コマンドを使用します。

cd /home/user

一般的なショートカットは次の通りです。

  • `cd` - ホームディレクトリへ

  • `cd ~` - ホームディレクトリへ

  • `cd -` - 前のディレクトリへ

ディレクトリの作成
ディレクトリを作成するには、`mkdir`コマンドを使用します。

mkdir myfolder

一般的なオプションは次の通りです。

  • `-p` - 必要に応じて親ディレクトリを作成

ファイルのコピー
ファイルやディレクトリをコピーするには、`cp`コマンドを使用します。

cp file.txt newfile.txt
cp -r dir1 dir2

一般的なオプションは次の通りです。

  • `-r` - ディレクトリの再帰的なコピー

  • `-i` - 上書き前に確認プロンプトを表示

移動と名前変更
ファイルやディレクトリを移動するか、名前を変更するには、`mv`コマンドを使用します。

mv file.txt newfile.txt
mv dir1 dir2

ファイルの削除
ファイルやディレクトリを削除するには、`rm`コマンドを使用します。

rm file.txt
rm -r dir

一般的なオプションは次の通りです。

  • `-r` - 再帰的な削除

  • `-i` - 削除前に確認プロンプトを表示

ファイルの結合
ファイルを結合するには、`cat`コマンドを使用します。

cat file1.txt file2.txt > combined.txt

これにより、ファイルがcombined.txtに連結されます。

この章について何か拡張や修正してほしい部分があれば、お知らせください。


第12章 プロセスの管理

Bashは、実行中のプロセスを管理するためのさまざまなユーティリティを提供します。

プロセスの一覧表示
実行中のプロセスを確認するには、`ps`コマンドを使用します。

ps

一般的なオプションは次の通りです。

  • `aux` - 全ユーザーのプロセスを表示

  • `ef` - プロセスツリー全体を表示

追加のオプションについては `man ps` を参照してください。

`grep`にパイプでつなぐことでプロセスリストをフィルタリングすることができます。

プロセスの状態
重要なプロセスの状態は次の通りです。

  • `R` - 実行中

  • `S` - スリープ中

  • `T` - 停止

  • `Z` - ゾンビ

プロセス状態の変更
プロセスを停止/開始するには、`kill`コマンドを使用します。

kill -STOP [pid]
kill -CONT [pid] 

`STOP`や`CONT`のようなシグナルはプロセスの一時停止と再開を行います。

PIDによるプロセスの強制終了は次の通りです。

kill [pid]

プロセスの優先度
プロセスの優先度を管理するには、`renice`コマンドを使用します。

renice +5 [pid]
renice -5 [pid]

高いnice値=低い優先度。



第13章 データの比較と検証

Bashはデータの比較と入力の検証に多くのツールを提供しています。

データの比較
文字列の比較するには、`==`を使用します。

[[ $str1 == $str2 ]]

他の文字列の比較は次の通りです。

  • `!=` - 不等号

  • `<` - 小なり

  • `>` - 大なり

  • `-z` - 文字列が空である

数値の大なりでの比較は次の通りです。

[[ $num1 -gt $num2 ]]
  • `-eq` - 等号

  • `-ne` - 不等号

  • `-lt` - 小なり

  • `-le` - 以下

ファイルが存在するかでのファイルの比較は次の通りです。

[[ -f /path/to/file ]] 
  • `-d` - ディレクトリが存在する

  • `-e` - ファイルが存在する

  • `-r` - ファイルが読み取り可能である

  • `-w` - ファイルが書き込み可能である

  • `-x` - ファイルが実行可能である

正規表現のマッチング
正規表現のマッチングは次のように行います

[[ $string =~ [0-9]+ ]]

`=~`は正規表現にマッチします。検証に便利です。

データの検証
データの検証は次のように行います。

if [[ -z "$value" ]]; then
  echo "Missing value"
  exit 1
fi

その他の検証は次の通りです。

  • 正規表現を用いた型チェック

  • 範囲内の数字を検証

  • 必要な引数が渡されていることを確認

  • エラーが早期に発生するようにする

これにより、スクリプトをより堅牢にすることができます。


第14章 デバッグ技法

Bashは、問題を特定するためのスクリプトをデバッグする様々な方法を提供します。

デバッグの概要
Bashの有用なデバッグ技法のいくつかは以下の通りです。

  • Print文

  • `set -x`で実行のトレース

  • `set -v`で変数値のチェック

  • ファイルへのデバッグログ

  • `if`文でのアサーション

  • エラーメッセージの読み取り

デバッグはコード内の問題を見つけ、修正するために使います。

Print文
Print文の一例として、一時的な`echo`を追加する方法があります。

echo "Reached validation check" 

重要な変数の値を出力するというのは有効なテクニックです。

echo "User is: $user"

実行とデータフローのトレースに役立ちます。

実行のトレース
実行のトレースを出力するのは、Bashスクリプトのデバッグに非常に有効なテクニックです。

set -x

これは、実行する前に各コマンドを出力します。プログラムの流れを追うのに役立ちます。

#!/bin/bash

echo "Start script"
set -x

echo "In middle" 
ls -l
echo "End"

上のスクリプトを実行すると、以下のような出力になります。

Start script
+ echo 'In middle'
In middle
+ ls -l
total 0
-rw-r--r-- 1 user group 0 Jan 1 00:00 file1
-rw-r--r-- 1 user group 0 Jan 1 00:00 file2
+ echo 'End'  
End

`set +x`でオフにします。

変数のチェック
変数の値変更時に出力するには`set -v`オプションを利用することで実現できます。

set -v

これは、変数が変更されたときにその名前と値をエコーします。

`set -v -x`でエクスポートされた変数のみに制限することができます。

デバッグログ
デバッグ出力をファイルに記録することができます。

echo "Log message" >> debug.log

debug.logに出力を追加し、後で確認できます。

アサーション
値の妥当性チェックを行うことができます。

## File size should not be 0 bytes  
[[ -s "$file" ]] || { echo "File is empty!"; exit 1; }

アサーションが失敗すると早期に失敗します。

エラーメッセージの読み取り
手がかりを得るため、エラー出力を注意深く読むことをおすすめします。

line 5: syntax error near unexpected token `done'

ファイル名、行番号、テキストの説明を確認することで、原因を特定できる可能性があります。


第15章 エラーハンドリング

エラーのチェック
Bashスクリプトでは、`$?`変数を使用してコマンドが成功したか失敗したかを確認できます。これには最後に実行されたコマンドの終了ステータスが含まれています。終了ステータスが0であれば成功、それ以外は失敗とみなされます。

コマンドの終了ステータスを利用する方法は以下の通りです。

mkdir mydir
if [ $? -ne 0 ]; then
  echo "mkdir failed"
fi

Bashの終了ステータスのドキュメンテーション ↗

終了コードの取得と使用
終了コードを直接変数にキャプチャするには次のようにします。

mkdir mydir
result=$?

これで`$result`に終了コードが含まれ、異なるケースを処理できます。

if [ $result -eq 0 ]; then
  echo "Directory created successfully" 
else
  echo "mkdir failed with exit code $result"
fi

カスタムエラーの生成
`exit`を使用してスクリプトをカスタムの終了コードで終了させることができます。

if [ ! -d "$DIR" ]; then
  echo "Directory $DIR does not exist"
  exit 1
fi

これはコード1で終了し、エラーを示します。


第16章 システム情報

ホスト情報
`hostname`でホスト名を出力できます。

hostname

そして、FQDNは`hostname -f`で出力します。

hostname -f

hostnameのドキュメンテーション ↗

OS情報
`uname`でOS名とバージョンを出力します。

uname -srv

unameのドキュメンテーション ↗

ハードウェア情報
`lscpu`でCPU情報を確認できます。

lscpu

そして、メモリ情報は`free`で確認します。

free -h

ネットワーク情報
`ip link`でネットワークインターフェースのリストを表示します:

ip link

ipのドキュメンテーション ↗

環境変数
`printenv`で全ての環境変数を出力します:

printenv

printenvのドキュメンテーション ↗


第17章 リモート管理

SSH概要
SSHを使用すると、リモートホスト上でリモートターミナルへのアクセスとコマンドの実行が可能となります。

`ssh`を使用してリモートホストに接続します。

ssh user@host

sshのドキュメンテーション ↗

SSHログイン
リモートホストに直接ログインできます。

ssh user@remote-server

または、リモートホストで単一のコマンドを実行できます。

ssh user@remote-server <command>

sshログインのドキュメンテーション ↗

scpとrsync
`scp`を使用してリモートホストにファイルを安全にコピーします。

scp file.txt user@remote:/path/to/destination

scpのドキュメンテーション ↗

`rsync`を使用してフォルダを同期します。

rsync -avz /local/folder user@remote:/remote/folder

rsyncのドキュメンテーション ↗


第18章 ログファイル管理

ログの確認
`less`を使用してログを表示します。

less /var/log/syslog

lessのドキュメンテーション ↗

`grep`を使用してログを検索します。

grep "error" /var/log/syslog

grepのドキュメンテーション ↗

ログファイルのエクスポート
`cat`を使用してログをエクスポートします。

cat /var/log/syslog > syslog.txt

catのドキュメンテーション ↗

または、`more`を使用してログをページングします。

more /var/log/syslog > syslog.txt

moreのドキュメンテーション ↗

ログのクリア
`>/dev/null`を使用してログをクリアします。

> /var/log/syslog

ログイベントの生成
`logger`を使用してログイベントを生成します。

logger "Test log event"

loggerのドキュメンテーション ↗


第19章 高度なテクニック

Bashのカスタマイズ
`.bashrc`と`.bash_profile`を使ってBashをカスタマイズします。

nano ~/.bashrc

Bash起動ファイルのドキュメンテーション ↗

エイリアスを定義します。

alias ll='ls -alF'

エイリアスのドキュメンテーション ↗

非同期処理
`&`を使ってコマンドを非同期で実行します。

command &

ジョブ制御のドキュメンテーション ↗

複雑なパイプラインの例
ユニークなIPアドレスをカウントします。

cat access.log | cut -d' ' -f1 | sort | uniq -c | sort -nr

第20章 ヒント

履歴の利用
`history`で履歴を表示します。

history

Bashの履歴のドキュメンテーション ↗

`Ctrl+R`で履歴を検索します。

データ型の確認
値が数値かどうかを確認します。

if [[ $val =~ ^[0-9]+$ ]]; then
  echo "Number"
fi

Bashの条件式のドキュメンテーション ↗

出力のフォーマット
`numfmt`で数値をフォーマットします。

numfmt --grouping 1234567

numfmtのドキュメンテーション ↗

セキュリティの緩和
ルートログインを許可します。

sudo nano /etc/ssh/sshd_config
## PermitRootLoginをyesに変更

第21章 実用的な例

システム情報スクリプト
システム情報を表示します。

#!/bin/bash

## ホスト名
hostname

## OS
uname -a 

## CPU
lscpu | grep "Model name"

## メモリー
free -h

## ディスク使用量
df -h

ログファイルバックアップスクリプト
ログファイルをバックアップします。

#!/bin/bash

## 日付と時間
current_date=$(date +%Y-%m-%d)

## バックアップディレクトリ
backup_dir="/backups/logs/$current_date"

## バックアップディレクトリを作成
mkdir -p "$backup_dir"

## ファイルをコピー
cp /var/log/*.log "$backup_dir"

mkdirのドキュメンテーション ↗

リモートソフトウェアインストーラー
リモートサーバーにソフトウェアをインストールします。

#!/bin/bash

## リモートサーバーのリスト
servers="server1 server2 server3"

## インストールするソフトウェアパッケージ
package="nginx"

## 各サーバーにインストール
for server in $servers; do
  ssh $server "sudo apt install -y $package"
done

sshのドキュメンテーション ↗


第22章 他のBashのヒント

組み込みコマンドの使用
Bashには `pwd`のような便利な組み込みコマンドがたくさんあります。

pwd

Bashの組み込みコマンドのドキュメンテーション ↗

ドキュメンテーションの検索
`man`でヘルプを取得します。

man rm

manのドキュメンテーション ↗

または `info`で取得します。

info bash

infoのドキュメンテーション ↗

コミュニティリソースの利用
Stack Overflowを検索します。

bash loop through files stackoverflow

Bashのワンライナーを閲覧します。

https://www.commandlinefu.com/commands/matching/bash/UOJZlt/

ワンライナーの例
サブディレクトリ内のファイルを数えます。

find . -type f | wc -l

findのドキュメンテーション ↗


第23章 Bashスキルの向上

組み込みヘルプの読み込み
`help`を使って組み込みコマンドを学びます。

help cd

Bash help組み込みコマンド ↗

例での練習
ワンライナーの例を見つけてテストします。

echo ${PATH//:/$'\n'}

パラメータ展開 ↗

ルーチンタスクの自動化
よく使うタスクのスクリプトを書きます。

#!/bin/bash

## バックアップスクリプト
tar -czf /backups/$(date +%Y-%m-%d).tar.gz /home

コミュニティとの交流
Stack Overflowで質問します。

bash loop through files recursively stackoverflow

最新情報を取得する
リリースノートを読みます。

https://www.gnu.org/software/bash/manual/html_node/Release-Notes.html

第24章 ベストプラクティス

コードの可読性
記述的な名前を使います。

backup_file="/tmp/data_backup.tar.gz"

コードをインデントします。

if [ $? -ne 0 ]; then
    echo "Error occurred" >&2
    exit 1
fi

Bashでのインデントのガイドライン ↗

モジュラーな関数
再利用可能な関数に分割します。

backup() {
  tar cvzf "$1" "${2:-/home/user}"
}

backup "/tmp/mybackup.tar.gz"

Bashの関数 ↗

信頼性の高いエラーハンドリング
戻りコードをチェックします。

if ! backup "/tmp/mybackup.tar.gz"; then
  echo "Backup failed" >&2 
  exit 1
fi

ディフェンシブなBashプログラミング ↗

パラメータの使用
変数を引用符で囲みます。

backup_file="/tmp/${1}.tar.gz"

`$*`よりも`"$@"`を推奨します。

for arg in "$@"; do
    echo "Processing argument: $arg"
done

ロギング
stderrにログを出力します。

echo "Starting backup" >&2

ログのローテーションには`exec`を使用します。

Bashのログ記録のベストプラクティス ↗


おわりに

このガイドでは、Bashの基礎から応用的なテクニックまで幅広く学びました。BashはUNIX系OS環境におけるスクリプト言語として非常に重要です。

Bash Codexを通して学んだ知識を生かし、実際の業務でBashのスキルを磨いていきましょう。スクリプトの参考になるサンプルは多数Webに公開されています。先進的な技法を取り入れ、よりスマートな自動化を実現することができます。

新しいバージョンのBashも頻繁にリリースされています。最新の動向を把握し、スキルアップを続ける努力が大切です。

IT環境は日々進化しています。

Bashを武器に、自動化のプロとして働き続けるためにも、学びを止めないことが肝要です。

ぜひ実践的なスクリプトスキルを身につけ、自動化のプロフェッショナルを目指してください!

この記事が気に入ったらサポートをしてみませんか?