【入門】K シェル(ksh)の特徴と使い方

【入門】K シェル(ksh)の特徴と使い方:詳細徹底解説

はじめに

UNIX/Linuxシステムを操作する上で、シェルは欠かせない存在です。シェルは、ユーザーがコマンドを入力し、カーネルにその実行を指示するためのインターフェースとして機能します。数多くのシェルが存在しますが、その中でも「KornShell (コーンシェル)」、通称「ksh」は、長い歴史を持ち、特にシステム管理やスクリプト作成の分野で根強く利用されています。

本記事では、kshを初めて学ぶ方に向けて、その基本的な使い方から、bashなどの他のシェルにはないkshならではの強力な機能までを、詳細かつ丁寧に解説します。約5000語という長大な記事になりますが、kshの全体像を掴み、自信を持って使い始められるようになることを目指します。

シェルとは何か?

シェルは、オペレーティングシステム(OS)のカーネルとユーザーの間をつなぐプログラムです。ユーザーがキーボードから入力したコマンドを解釈し、カーネルに実行を依頼します。また、カーネルからの応答(コマンドの実行結果など)を受け取り、画面に表示します。ファイル操作、プログラムの実行、環境設定、スクリプトの実行など、システム操作のほとんどはシェルを通じて行われます。

主要なシェルには、Bourne Shell (sh)、C Shell (csh)、TENEX C Shell (tcsh)、Bourne-Again Shell (bash)、Z Shell (zsh)、そして本記事のテーマであるKornShell (ksh) などがあります。それぞれに特徴があり、構文や機能が異なります。

KornShell (ksh) とは?

KornShellは、David Korn氏によって開発され、1983年にリリースされました。Bourne Shell (sh) の互換性を保ちつつ、C Shell (csh) の便利な機能(ジョブ制御、履歴機能など)を取り込み、さらに独自の強力な機能(コマンドライン編集、連想配列、コプロセスなど)を追加したシェルです。

kshは、その堅牢性、豊富な機能、そして高速性から、特にエンタープライズ環境のUNIXシステム(AIX、Solaris、HP-UXなど)で標準シェルとして採用されることが多いです。Linux環境でも利用可能であり、bashほど一般的ではないかもしれませんが、その独自機能に魅力を感じて利用するユーザーも少なくありません。

kshにはいくつかのバージョンがあります。最も普及しているのはksh88とksh93です。ksh93はksh88にさらに多くの機能を追加したバージョンであり、現在主流となっているkshの多くはksh93またはその派生(ast-kshなど)です。本記事では、主にksh93の機能をベースに解説します。

なぜkshを学ぶのか?

  • 歴史と普及度: 特に古いUNIXシステムやエンタープライズシステムでは、kshが標準シェルである場合が多いです。これらのシステムを操作したり、そこで動作するスクリプトを理解したりするためにkshの知識は必須です。
  • 強力な機能: コマンドライン編集機能、豊富な変数属性、コプロセス、洗練された数値演算など、bashなど他のシェルにはない(あるいはkshの方が優れている)独自の強力な機能を持っています。
  • Bourne Shell互換: shの構文と互換性があるため、既存の多くのシェルスクリプトをkshでそのまま実行できます。
  • 堅牢性とパフォーマンス: システム管理などの用途で求められる堅牢性とパフォーマンスに優れています。

記事の目的と概要

本記事は、以下の内容を扱います。

  • kshのインストールと起動方法
  • 基本的なコマンド操作とシェル機能
  • kshスクリプトの作成と実行方法
  • 変数、制御構造、関数といったスクリプトの基本要素
  • ksh独自の強力な機能(変数属性、[[ ]]構文、select、コプロセスなど)の詳細解説
  • bashなど他のシェルとの比較
  • 実践的なスクリプト例

この記事を読むことで、kshの基本的な使い方から、その強力な機能を活用したスクリプト作成までを習得できます。さあ、kshの世界へ踏み込みましょう!

kshのインストールと起動

kshを利用するには、まずシステムにインストールされている必要があります。多くのUNIX系OSには最初から含まれていますが、含まれていない場合や、より新しいバージョンを利用したい場合は、手動でのインストールが必要です。

インストール方法

OSによってインストール方法は異なります。

  • Linux:
    • Debian/Ubuntu系: sudo apt update && sudo apt install ksh
    • RHEL/CentOS/Fedora系: sudo dnf install ksh または sudo yum install ksh
    • Arch Linux系: sudo pacman -S ksh
      ほとんどのディストリビューションでは、kshという名前のパッケージで提供されています。多くの場合、これはast-ksh(ksh93の派生)です。
  • macOS:
    macOSには標準でkshが含まれていない場合があります。Homebrewなどのパッケージマネージャーを使ってインストールするのが簡単です。

    • Homebrew: brew install ksh
  • BSD系 (FreeBSD, OpenBSDなど):
    それぞれのパッケージ管理システムを使用します。例えばFreeBSDなら pkg install ksh
  • AIX, Solaris, HP-UXなど:
    これらの商用UNIXシステムでは、通常kshが標準でインストールされています。追加でインストールする必要はほとんどありません。

ソースコードからビルドすることも可能ですが、入門者にはパッケージマネージャーの利用をお勧めします。

kshの起動方法

インストールが完了したら、kshを起動してみましょう。

  1. 端末エミュレーターを開く: ターミナル、GNOME端末、iTerm2など、お使いのOSの端末エミュレーターを起動します。
  2. kshコマンドを実行: プロンプトが表示されたら、以下のコマンドを入力してEnterキーを押します。
    bash
    ksh

    これで、現在のシェルセッションがkshに切り替わります。プロンプトの表示が変わるかもしれません。シェルから抜けるには、exitコマンドを入力するか、Ctrl+Dを押します。

  3. ログインシェルとして設定: システムにログインしたときに自動的にkshが起動するように設定することも可能です。これには、ユーザーのログインシェルを変更する必要があります。この設定は慎重に行う必要があり、間違えるとログインできなくなる可能性もあります。通常はシステム管理者権限(root権限)が必要です。

    • 自分のログインシェルを確認: echo $SHELL
    • 利用可能なシェルを確認: cat /etc/shells
    • ログインシェルを変更: chsh -s /bin/ksh (/bin/kshはkshの実行ファイルのパスに合わせてください)
      変更は、次回のログイン時から有効になります。

バージョンの確認

起動したkshのバージョンを確認するには、いくつかの方法があります。

  • ksh --version: バージョン情報が表示されます。
  • echo $KSH_VERSION: kshのバージョン情報が格納されている特殊変数KSH_VERSIONを表示します。ksh93では、@(#)PD KSH vXXXX@(#)Version JM X.Y.Z のような形式でバージョン情報が表示されます。

ksh
$ ksh
$ echo $KSH_VERSION
@(#)PD KSH v5.2.14 99/07/13
$ exit
$ ksh --version
version sh (AT&T Labs Research) 93u+ 2012-08-01
$ echo $KSH_VERSION
Version JM 93u+ 2012-08-01
$

(上記は異なるksh実装の例です。システムによって表示は異なります。)

これで、kshを利用するための準備が整いました。

基本的なコマンドとシェル操作

kshも他のシェルと同様に、基本的なコマンド操作が可能です。ここでは、特によく使うコマンドとkshならではの機能について説明します。

コマンドの実行

コマンド名を入力してEnterキーを押すと、そのコマンドが実行されます。
ksh
$ ls -l # 現在のディレクトリの内容を詳細表示
$ cd /tmp # /tmpディレクトリに移動
$ pwd # 現在のディレクトリを表示
$ mkdir mydir # mydirというディレクトリを作成
$ rm myfile.txt # myfile.txtというファイルを削除
$ cp file1.txt file2.txt # file1.txtをfile2.txtにコピー
$ mv oldname newname # oldnameをnewnameに移動/改名

パス

ファイルやディレクトリを指定する際には「パス」を使用します。
* 絶対パス: ファイルシステムのルートディレクトリ (/) から始まるパス。例: /home/user/documents/report.txt
* 相対パス: 現在の作業ディレクトリからの相対的な位置を示すパス。例: ../data/input.csv (../は一つ上のディレクトリ)

標準入出力とリダイレクト

コマンドは通常、標準入力(キーボードからの入力など)からデータを受け取り、標準出力(画面への表示など)と標準エラー出力(エラーメッセージなど)に結果を出力します。これらの入出力先を変更する機能を「リダイレクト」と呼びます。

  • >: 標準出力をファイルに上書き保存。例: ls > file_list.txt
  • >>: 標準出力をファイルの末尾に追加。例: ls >> file_list.txt
  • <: 標準入力をファイルから読み込む。例: sort < data.txt
  • |: パイプ。あるコマンドの標準出力を別のコマンドの標準入力に接続。例: ls -l | grep .txt (lsの結果から.txtを含む行を抽出)
  • 2>: 標準エラー出力をファイルに上書き保存。例: command_that_might_fail 2> error.log
  • &>: 標準出力と標準エラー出力の両方をファイルに上書き保存(ksh93以降)。例: command &> output_and_error.log
  • &>>: 標準出力と標準エラー出力の両方をファイルの末尾に追加(ksh93以降)。例: command &>> output_and_error.log

バックグラウンド実行とフォアグラウンド実行

コマンドの後ろに & をつけると、コマンドをバックグラウンドで実行できます。
ksh
$ long_running_command &
[1] 12345 # ジョブ番号とプロセスIDが表示される
$ _ # プロンプトが戻り、別のコマンドを入力できる

バックグラウンドで実行されているジョブは、jobsコマンドで確認できます。
ksh
$ jobs
[1]+ Running long_running_command

バックグラウンドジョブをフォアグラウンドに戻すには fg、バックグラウンドジョブを再開するには bg を使います。ジョブ番号を指定しない場合は、直前のジョブ(jobs+が付いているもの)が対象になります。
ksh
$ fg %1 # ジョブ番号1をフォアグラウンドに戻す
$ bg %1 # ジョブ番号1をバックグラウンドで再開

コマンド履歴

kshは実行したコマンドの履歴を記憶しており、再利用できます。
* 上/下矢印キー: 履歴を遡る/進む。
* history: 履歴リストを表示。
* r: 直前のコマンドを再実行。例: r ls (直前のlsコマンドを再実行)
* r command_prefix: 指定したプレフィックスで始まる最も新しいコマンドを再実行。例: r grep (直前のgrepコマンドを再実行)

コマンドライン編集

kshの最も優れた特徴の一つが、強力なコマンドライン編集機能です。デフォルトではemacsモードに似たキーバインドですが、viモードも利用できます。

  • 編集モードの設定:
    ksh
    $ set -o emacs # emacs風キーバインド
    $ set -o vi # vi風キーバインド

    .kshrc ファイル(後述)に記述しておくと、シェル起動時に自動的に設定されます。

  • emacsモード (デフォルト):

    • Ctrl+A: 行頭へ移動
    • Ctrl+E: 行末へ移動
    • Ctrl+F: 一文字進む
    • Ctrl+B: 一文字戻る
    • Alt+F: 一単語進む
    • Alt+B: 一単語戻る
    • Ctrl+K: カーソル位置から行末までをカット
    • Ctrl+Y: カットした内容をペースト
    • Ctrl+L: 画面をクリア
    • Ctrl+R: 履歴のインクリメンタルサーチ
  • viモード:

    • Escapeキー: コマンドモードに移行
    • h, j, k, l: 左、下(履歴)、上(履歴)、右へ移動
    • w: 次の単語の先頭へ移動
    • b: 前の単語の先頭へ移動
    • 0: 行頭へ移動
    • $: 行末へ移動
    • i: カーソル位置の前に挿入モード
    • a: カーソル位置の後に挿入モード
    • x: カーソル位置の文字を削除
    • dd: 行全体を削除
    • p: 削除した内容をペースト
    • r: 一文字置き換え
    • / pattern: 履歴を後方検索
    • n: 次の検索結果へ
    • N: 前の検索結果へ
    • v: コマンド全体をviエディタで編集(編集後保存して終了すると、その内容がコマンドラインに反映される)

viモードは、複雑なコマンドを編集する際に非常に便利です。

ワイルドカード (ファイル名パターンマッチング)

ファイル名やディレクトリ名を指定する際に、ワイルドカード(メタ文字)を使って複数のファイルをまとめて指定できます。

  • *: 任意の文字列(ゼロ文字以上)にマッチ。例: ls *.txt (拡張子が.txtのすべてのファイル)
  • ?: 任意の一文字にマッチ。例: ls file?.txt (file1.txt, fileA.txtなどにマッチ)
  • []: 角カッコ内の任意の一文字にマッチ。範囲指定も可能。例: ls [abc]*.txt (a, b, cのいずれかで始まる.txtファイル)、ls file[0-9].txt (file0.txtからfile9.txtにマッチ)
  • [!...]: 角カッコ内の文字以外の任意の一文字にマッチ。例: ls [!a-z]*.txt (英小文字以外で始まる.txtファイル)
  • @(pattern1|pattern2|...): pattern1またはpattern2などにマッチ(ksh93以降)。例: ls @(*.txt|*.log) (.txtまたは.logファイルにマッチ) – bashのextglob機能に相当
  • *(pattern1|pattern2|...): pattern1またはpattern2などの0回以上の繰り返しにマッチ(ksh93以降)。例: ls *(@(.o|.a))(拡張子が.oまたは.aのファイルにマッチ)
  • ?(pattern): patternの0回または1回の出現にマッチ(ksh93以降)。例: ls file?(1).txt (file.txt または file1.txt にマッチ)
  • +(pattern): patternの1回以上の出現にマッチ(ksh93以降)。例: ls file+(.)txt (file.txt, file..txt, file…txt などにマッチ)
  • *(pattern): patternの0回以上の出現にマッチ(ksh93以降)。例: ls file*(.)txt (filetxt, file.txt, file..txt などにマッチ)

ksh93の拡張ワイルドカードは非常に強力で、より複雑なファイルパターンを簡潔に表現できます。使用するには set -o extglob が必要な場合もありますが、多くのksh93環境ではデフォルトで有効です。

クォーテーション (引用符)

コマンドラインでスペースを含む文字列や、$, *, ? などの特殊文字をリテラルとして扱いたい場合に引用符を使用します。

  • ' ' (シングルクォート): ほとんどの特殊文字の機能を無効にします。変数展開やコマンド置換も行われません。
    ksh
    $ VAR="hello"
    $ echo '$VAR is *'
    $VAR is *
  • " " (ダブルクォート): 一部の特殊文字($, `, \)を除いて、特殊文字の機能を無効にします。変数展開やコマンド置換は行われます。
    ksh
    $ VAR="hello"
    $ echo "$VAR is *"
    hello is * # $VARは展開されるが、*は展開されない
  • ` (バッククォート): コマンド置換を行います。バッククォートで囲まれたコマンドが実行され、その標準出力が元のコマンドラインに挿入されます。
    ksh
    $ echo "Current directory: `pwd`"
    Current directory: /home/user

    注: バッククォートはネストが難しく、可読性も低いため、最近のシェル(ksh, bash, zshなど)では $() 構文を使うことが推奨されます。

  • $(): バッククォートと同様にコマンド置換を行います。ネストが可能で、可読性も高いです。
    ksh
    $ echo "Current directory: $(pwd)"
    Current directory: /home/user
    $ echo "Files count: $(ls | wc -l)" # コマンド置換のネスト
    Files count: 15

    kshでは $() 構文を積極的に利用しましょう。

環境変数とシェル変数

シェルには、環境変数とシェル変数があります。

  • 環境変数: シェルから起動された子プロセスにも引き継がれる変数。大文字で記述されることが多い慣習です。システム全体の設定や、プログラムの動作に影響を与える情報(例: PATH, HOME, USER, LANG)を保持します。
  • シェル変数: そのシェルプロセス内でのみ有効な変数。

変数を定義するには VAR=value の形式を使います。参照するには $VAR とします。

“`ksh
$ MY_VAR=”This is a shell variable” # シェル変数
$ echo $MY_VAR
This is a shell variable

$ export EXPORT_VAR=”This is an environment variable” # 環境変数に設定
$ echo $EXPORT_VAR
This is an environment variable

$ unset MY_VAR # 変数の定義を解除
$ echo $MY_VAR

“`

export コマンドで定義済みのシェル変数を環境変数にするか、定義と同時に環境変数に設定できます。

よく使う重要な環境変数/シェル変数:

  • $HOME: ユーザーのホームディレクトリ。
  • $PATH: コマンドを実行する際にシェルが実行ファイルを探しに行くディレクトリのリスト。コロン : で区切られています。
  • $PS1: プライマリプロンプト文字列。コマンド入力待ちの際に表示されます。
  • $PS2: セカンダリプロンプト文字列。コマンド入力が複数行にわたる際に表示されます。
  • $IFS: 内部フィールドセパレータ。シェルの単語分割に使われる文字。デフォルトはスペース、タブ、改行。
  • $LINENO: 現在の行番号(スクリプト実行時)。
  • $$: 現在のシェルプロセスのプロセスID (PID)。
  • $PPID: 親シェルプロセスのPID。

PS1をカスタマイズすると、プロンプトに見やすい情報を表示できます。
ksh
$ PS1='($PWD) $ ' # 現在のディレクトリを()で囲んで表示
(/home/user) $ cd /tmp
(/tmp) $

エイリアス

エイリアスは、長いコマンドやよく使うコマンドオプションに別名を付ける機能です。

“`ksh
$ alias ll=’ls -l’ # llというエイリアスを定義
$ ll # ls -lが実行される
-rw-r–r– 1 user user 1024 Jan 1 10:00 myfile.txt

$ alias rm=’rm -i’ # rmコマンド実行時に確認を求めるようにする
$ rm myoldfile.txt
rm: remove regular file `myoldfile.txt’? y

$ unalias ll # エイリアスの定義を解除
``.kshrc` ファイルに記述しておくと、シェル起動時に自動的に設定されます。

設定ファイル

kshは起動時にいくつかの設定ファイルを読み込みます。主要なものは以下の通りです。

  • /etc/profile: システム全体の設定(ログインシェル用)。
  • $HOME/.profile: ユーザーごとの設定(ログインシェル用)。環境変数やPATHの設定など、ログイン時に一度だけ実行したい処理を記述します。
  • $HOME/.kshrc: ksh固有の設定(インタラクティブシェル用)。エイリアス、変数属性、コマンドライン編集モードの設定など、新しいkshプロセスが起動するたびに実行したい処理を記述します。

ログインシェルとしてkshを使用する場合、まず/etc/profile$HOME/.profileが読み込まれ、その後インタラクティブシェルの場合は$HOME/.kshrcが読み込まれます。インタラクティブシェルでない場合(スクリプト実行など)は、.kshrcは通常読み込まれません。

kshスクリプトの作成と実行

シェルスクリプトは、複数のコマンドを記述したファイルであり、まとめて実行することができます。kshスクリプトを作成し、実行する方法を学びましょう。

スクリプトの基本構造

シェルスクリプトの最初の行には、「シバン (Shebang)」と呼ばれる特別な行を記述するのが慣習です。これは、そのスクリプトをどのインタプリタ(シェル)で実行するかを指定します。

“`ksh

!/bin/ksh

またはksh

!/usr/bin/ksh

``
のように、システム上のkshのパスを指定します。
which ksh` コマンドで確認できます。

シバンの次の行からは、シェルが実行するコマンドや構文を記述していきます。

“`ksh

!/bin/ksh

これはコメントです。#から行末までは無視されます。

echo “Hello, ksh script!”
ls -l
pwd
“`

コメント

# から行末まではコメントとして扱われ、シェルに無視されます。スクリプトの可読性を高めるために、処理の内容や目的をコメントで記述しましょう。

変数の定義と参照

インタラクティブシェルと同様に、スクリプト内でも変数を使用できます。

“`ksh

!/bin/ksh

変数を定義

GREETING=”Hello”
TARGET=”World”

変数を参照

echo “$GREETING, $TARGET!”

コマンドの出力を変数に代入 (コマンド置換)

CURRENT_DIR=$(pwd)
echo “Current directory is: $CURRENT_DIR”

数値変数の定義 (後述のtypeset -iも参照)

COUNT=5
echo “Count: $COUNT”
``
変数名は英数字とアンダースコア
_` で始まり、大文字小文字を区別します。

変数の属性 (typeset)

kshのtypesetコマンドは、変数の「属性」を設定する強力な機能です。これにより、変数の型、表示形式、読み取り専用設定などを制御できます。bashのdeclareコマンドに相当しますが、kshのtypesetはより多くのオプションを持っています。

主なtypesetオプション:

  • -i: 変数を整数として扱います。算術演算が可能です。
    “`ksh
    #!/bin/ksh
    typeset -i count # countを整数型として宣言

    count=10
    echo “Initial count: $count”

    count=$count+5 # 算術演算が可能
    echo “New count: $count”

    count=count*2
    echo “Final count: $count”
    * `-r`: 変数を読み取り専用にします。値を変更しようとするとエラーになります。ksh

    !/bin/ksh

    typeset -r READ_ONLY_VAR=”This cannot be changed”
    echo $READ_ONLY_VAR

    READ_ONLY_VAR=”trying to change” # エラーになる

    * `-x`: 変数を環境変数としてエクスポートします。`export`コマンドと同じですが、`typeset`で他の属性と同時に設定できます。ksh

    !/bin/ksh

    typeset -x MY_CONFIG=”value” # MY_CONFIGを環境変数としてエクスポート
    * `-a`: 変数を一次元配列として扱います。ksh

    !/bin/ksh

    typeset -a myArray # 配列を宣言

    myArray[0]=”apple”
    myArray[1]=”banana”
    myArray[2]=”cherry”

    echo “First element: ${myArray[0]}”
    echo “All elements: ${myArray[]}”
    echo “Number of elements: ${#myArray[
    ]}” # ksh93以降
    ``
    配列の要素を参照する際は、
    ${配列名[インデックス]}の形式を使います。インデックスは通常0から始まります。${#配列名[*]} や ${#配列名[@]}` で要素数を取得できます(ksh93以降)。

  • -A: 変数を連想配列(ハッシュマップ)として扱います(ksh93 vetch以降)。文字列をキーとして値を格納できます。
    “`ksh
    #!/bin/ksh
    typeset -A myAssocArray # 連想配列を宣言

    myAssocArray[“name”]=”Alice”
    myAssocArray[“city”]=”Tokyo”
    myAssocArray[“job”]=”Engineer”

    echo “Name: ${myAssocArray[“name”]}”
    echo “City: ${myAssocArray[“city”]}”

    キーの一覧を取得 (ksh93u+以降)

    echo “Keys: ${!myAssocArray[*]}”

    値の一覧を取得 (ksh93u+以降)

    echo “Values: ${myAssocArray[*]}”
    “`
    連想配列は、関連するデータをキーと値のペアで管理するのに非常に便利です。

  • -L num: 変数の値を左詰めにし、幅をnumバイト(マルチバイト文字の場合は文字数)に設定します。必要に応じて空白が追加されます。
    ksh
    #!/bin/ksh
    typeset -L 10 left_padded="abc"
    echo "[$left_padded]" # 出力: [abc ]

  • -R num: 変数の値を右詰めにし、幅をnumバイト(マルチバイト文字の場合は文字数)に設定します。必要に応じて空白が追加されます。
    ksh
    #!/bin/ksh
    typeset -R 10 right_padded="abc"
    echo "[$right_padded]" # 出力: [ abc]
  • -Z num: 変数の値を右詰めにし、numより小さい数値の場合は先頭をゼロで埋めます。数値以外や数値より大きい場合は右詰めのみを行います。
    ksh
    #!/bin/ksh
    typeset -Z 5 zero_padded=123
    echo "[$zero_padded]" # 出力: [00123]
    typeset -Z 5 zero_padded_large=123456
    echo "[$zero_padded_large]" # 出力: [123456]
    typeset -Z 5 zero_padded_text="abc"
    echo "[$zero_padded_text]" # 出力: [ abc]
  • -u: 小文字を自動的に大文字に変換します。
    ksh
    #!/bin/ksh
    typeset -u upper_case="hello world"
    echo "$upper_case" # 出力: HELLO WORLD
  • -l: 大文字を自動的に小文字に変換します。
    ksh
    #!/bin/ksh
    typeset -l lower_case="HELLO WORLD"
    echo "$lower_case" # 出力: hello world
  • -C: 変数の値を最初の文字のみ大文字、それ以降を小文字に変換します(タイトルケース)。(ksh93u+以降)
    ksh
    #!/bin/ksh
    typeset -C title_case="hELLo wORLd"
    echo "$title_case" # 出力: Hello World
  • typeset: 現在設定されている変数とその属性をすべて表示します。

typesetは、変数に特定の制約や表示形式を強制できるため、スクリプトの信頼性や可読性を高めるのに役立ちます。

特殊な変数

シェルスクリプトの実行時に自動的に設定される特殊な変数があります。

  • $0: スクリプト自身の名前(パスを含むこともあります)。
  • $1, $2, …: コマンドライン引数の1番目、2番目、…。
  • $#: コマンドライン引数の数。
  • $?: 直前に実行されたコマンドの終了ステータス。0は成功、0以外はエラーを示します。
  • $$: 現在のシェルスクリプトのプロセスID (PID)。
  • $!: 直前にバックグラウンドで実行されたコマンドのPID。
  • $@: コマンドライン引数を個別の単語として展開します。特にダブルクォートで囲んだ $@ は、"$1" "$2" ... のように展開され、引数にスペースが含まれていても正しく扱えます。
  • $*: コマンドライン引数を単一の文字列として展開します。通常はIFSの最初の文字(デフォルトはスペース)で区切られます。ダブルクォートで囲んだ "$*" は、"$1 IFS $2 IFS ..." のように展開されます。引数にスペースが含まれていると意図通りにならないことがあるため、多くの場合 $@ の使用が推奨されます。

例:
“`ksh

!/bin/ksh

echo “Script name: $0”
echo “Number of arguments: $#”
echo “First argument: $1”
echo “Second argument: $2”
echo “All arguments (using \$): $
echo “All arguments (using \”\$\”): \”$\””
echo “All arguments (using \”\$@\”): \”$@\””

test.ksh を作成し、以下のコマンドで実行してみる

ksh test.ksh arg1 “arg with spaces” arg3

出力例:

Script name: test.ksh

Number of arguments: 3

First argument: arg1

Second argument: arg with spaces

All arguments (using $*): arg1 arg with spaces arg3

All arguments (using “$*”): “arg1 arg with spaces arg3”

All arguments (using “$@”): “arg1” “arg with spaces” “arg3”

``“$@”` の方が、引数にスペースが含まれていてもそれぞれの引数を独立して扱えるため、より安全で一般的です。

read コマンド

readコマンドは、標準入力やファイルから一行を読み込み、変数に格納します。

“`ksh

!/bin/ksh

echo “Please enter your name:”
read name # 標準入力から読み込み、name変数に格納
echo “Hello, $name!”

echo “Enter multiple values (space separated):”
read val1 val2 val3 # 複数の変数に分割して格納(IFSで区切られる)
echo “Value 1: $val1”
echo “Value 2: $val2”
echo “Value 3: $val3”

read -p “Enter your password:” -s password # プロンプトを表示し、入力内容を非表示にする (-sはksh93)

echo “Password is $password”

``-pオプションは、入力を待つ前にプロンプト文字列を表示します(ksh93以降)。-s`オプションは、入力内容をエコーバックしません(パスワード入力などに利用)。

制御構造

シェルスクリプトでは、条件に応じて処理を分岐させたり、同じ処理を繰り返したりするために制御構造を利用します。kshは他の多くのシェルと同様に、if, for, while, until, case といった制御構造を提供します。

条件分岐 (if, elif, else, fi)

if文は、指定した条件が真 (True) の場合に特定のコマンドブロックを実行します。

“`ksh

!/bin/ksh

if [ condition ]
then
# 条件が真の場合に実行されるコマンド
command1
command2
fi # ifブロックの終了

条件が偽の場合にも処理をしたい場合

if [ condition ]
then
# 条件が真の場合
command_if_true
else
# 条件が偽の場合
command_if_false
fi

複数の条件を試したい場合

if [ condition1 ]
then
# condition1が真の場合
command_if_condition1_true
elif [ condition2 ] # elifで別の条件を指定
then
# condition1が偽で、condition2が真の場合
command_if_condition2_true
else
# どの条件も偽の場合
command_if_all_false
fi
``
条件は通常、
[...](これは実際にはtestコマンドの別名です) または ksh独自の[[...]]` 構文で記述します。

testコマンド ([ ])

testコマンドは、引数に基づいて真偽を評価し、その結果を終了ステータス(真なら0、偽なら1)として返します。[test コマンドへのシンボリックリンク(または組み込みコマンド)であり、] はその最後の引数として扱われます。

主なtestの条件式([] の中に記述):

  • 文字列比較:

    • string1 = string2: string1 と string2 が等しい
    • string1 != string2: string1 と string2 が等しくない
    • -z string: string の長さがゼロ(空文字列)である
    • -n string: string の長さがゼロでない
  • 数値比較: (整数のみ。kshでは -i 属性などを使うと数値比較が容易)

    • num1 -eq num2: num1 と num2 が等しい (Equal)
    • num1 -ne num2: num1 と num2 が等しくない (Not Equal)
    • num1 -gt num2: num1 が num2 より大きい (Greater Than)
    • num1 -ge num2: num1 が num2 以上 (Greater than or Equal)
    • num1 -lt num2: num1 が num2 より小さい (Less Than)
    • num1 -le num2: num1 が num2 以下 (Less than or Equal)
  • ファイルテスト:

    • -e file: file が存在する
    • -f file: file が通常のファイルである
    • -d file: file がディレクトリである
    • -s file: file が空でない(サイズが0より大きい)
    • -r file: file が読み取り可能である
    • -w file: file が書き込み可能である
    • -x file: file が実行可能である
    • -L file or -h file: file がシンボリックリンクである
    • -nt file1 -ot file2: file1 が file2 より新しい (Newer Than)
    • -ot file1 -nt file2: file1 が file2 より古い (Older Than)
    • file1 -ef file2: file1 と file2 が同じデバイス/iノードを参照している(ハードリンクなど)
  • 論理演算子:

    • ! condition: condition を否定する
    • condition1 -a condition2: condition1 かつ condition2 が真である (And)
    • condition1 -o condition2: condition1 または condition2 が真である (Or)

例:
“`ksh

!/bin/ksh

MY_FILE=”data.txt”
MY_DIR=”mydir”
COUNT=10

if [ -f “$MY_FILE” ]; then
echo “$MY_FILE exists and is a regular file.”
fi

if [ -d “$MY_DIR” ]; then
echo “$MY_DIR exists and is a directory.”
fi

if [ “$COUNT” -gt 5 ]; then
echo “Count is greater than 5.”
fi

if [ -n “$1” -a -d “$1” ]; then # 引数が空でなく、かつディレクトリであるか
echo “Argument ‘$1’ is a non-empty directory.”
fi
``
**注:**
[の後の最初の引数と、]の前の最後の引数の間にスペースが必要です。また、変数を含む場合はダブルクォートで囲むのが安全です。例:[ “$VAR” = “value” ]`

[[ ]] 構文 (kshの拡張テスト)

ksh (およびbash, zsh) では、[[]] というより高機能なテスト構文が利用できます。これは test コマンドよりも多くの利点があります。

  • 単語分割やパス名展開 (グロブ) が行われない: 変数をダブルクォートで囲む必要がない場合が多く、安全です。
  • 強力な演算子:
    • &&: 論理AND
    • ||: 論理OR
    • <, >: 文字列の辞書式比較(testコマンドでは数値比較に使われます)
    • =~: 正規表現マッチ(ksh93以降)
  • グロブパターンマッチ: = word の演算子で、ワイルドカードを含むパターンとのマッチングが可能です。

例:
“`ksh

!/bin/ksh

MY_VAR=”abc”
COUNT=10
FILE_NAME=”report.txt”

if [[ “$MY_VAR” = “abc” ]]; then # 文字列比較
echo “MY_VAR is abc.”
fi

if [[ “$COUNT” -gt 5 && “$COUNT” -lt 15 ]]; then # 数値比較と論理AND
echo “Count is between 6 and 14.”
fi

if [[ “$FILE_NAME” = *.txt ]]; then # パターンマッチ
echo “$FILE_NAME ends with .txt.”
fi

if [[ “apple” < “banana” ]]; then # 辞書式比較
echo “apple comes before banana.”
fi

正規表現マッチ (ksh93以降)

if [[ “$FILE_NAME” =~ ^re.*.txt$ ]]; then

echo “$FILE_NAME matches the pattern.”

fi

``[[ ]]構文は、test` コマンドよりも柔軟で安全な場合が多いため、kshスクリプトでは積極的に利用することをお勧めします。

ループ (for, while, until, select)

繰り返し処理を行うためのループ構文です。

  • for ループ (リスト指定)
    指定した単語リストの各要素に対して処理を繰り返します。

    “`ksh

    !/bin/ksh

    for item in apple banana cherry
    do
    echo “Processing $item…”
    done

    ファイルリストに対してループ

    for file in *.txt
    do
    echo “Found text file: $file”
    done
    “`

  • for ループ (Cスタイル)
    ksh93で導入された、C言語のような構文を持つforループです。カウンタ変数を使った繰り返しに便利です。

    “`ksh

    !/bin/ksh

    for (( i=0; i<5; i++ )) # 初期化; 条件; 更新
    do
    echo “Iteration $i”
    done
    ``(( ))` は、kshの算術式評価構文です。詳細は後述します。

  • while ループ
    指定した条件が真の間、処理を繰り返します。

    “`ksh

    !/bin/ksh

    count=0
    while [ $count -lt 5 ] # または while (( count < 5 ))
    do
    echo “Count: $count”
    (( count = count + 1 )) # または (( count++ ))
    done
    “`

  • until ループ
    指定した条件が偽の間、処理を繰り返します。whileループの条件を反転させたものと考えられます。

    “`ksh

    !/bin/ksh

    count=0
    until [ $count -ge 5 ] # または until (( count >= 5 ))
    do
    echo “Count: $count”
    (( count++ ))
    done
    “`

  • select ループ (メニュー作成)
    kshの非常に便利な独自機能の一つです。リストから項目を選択するインタラクティブなメニューを簡単に作成できます。

    “`ksh

    !/bin/ksh

    echo “Please choose your favorite fruit:”

    select fruit in apple banana cherry orange “exit program”
    do
    if [ “$fruit” = “” ]; then
    echo “Invalid selection.”
    continue # ループの先頭に戻る
    elif [ “$fruit” = “exit program” ]; then
    echo “Exiting.”
    break # ループを終了
    else
    echo “You chose $fruit.”
    # ここに選択された項目に対する処理を記述
    break # 処理が終わったらループを終了することも多い
    fi
    done
    ``selectループは、指定したリストからメニュー項目を番号付きで表示し、ユーザーからの入力を待ちます。ユーザーが番号を入力してEnterを押すと、対応する項目の文字列が$REPLY変数に、選択された文字列がselectの後の変数(上記の例ではfruit)に格納され、do … doneブロックが実行されます。REPLYに変数が代入されるのは、selectがユーザーからの入力値をREPLYに格納するためです。ループを抜けるにはbreak` を使用します。

case

変数の値に応じて複数の処理に分岐させたい場合に便利です。

“`ksh

!/bin/ksh

echo “Enter a color (red, green, blue):”
read color

case “$color” in
red|Red) # パターン1: red または Red
echo “You chose red.”
;; # 処理の区切り
green|Green) # パターン2: green または Green
echo “You chose green.”
;;
blue|Blue) # パターン3: blue または Blue
echo “You chose blue.”
;;
) # その他の全てのパターン (デフォルト)
echo “Unknown color: $color”
;;
esac # caseブロックの終了
``
各パターンの最後に
;;を付けるのを忘れないでください。
` は、どのパターンにもマッチしなかった場合のデフォルト処理です。

break, continue, exit

  • break: 現在のループ(for, while, until, select)を直ちに終了します。
  • continue: 現在のループの残りの処理をスキップし、次の繰り返しに移ります。
  • exit [n]: スクリプト全体の実行を終了します。n は終了ステータスで、省略された場合は直前のコマンドの終了ステータスになります。

関数

関数は、特定の処理をひとまとめにして名前を付け、スクリプト内の複数の場所から呼び出せるようにする機能です。これにより、コードの再利用性が高まり、スクリプトが整理されて分かりやすくなります。

関数の定義方法

kshには2種類の関数定義構文があります。

  1. function キーワードを使用 (ksh独自の構文):
    ksh
    function my_function {
    # 関数本体
    echo "Inside my_function"
    echo "Argument 1: $1"
    echo "Number of arguments: $#"
    # ...
    }

    この形式では、関数内で typeset を引数なしで使用すると、その関数内のローカル変数のみが表示されます。また、関数スコープでのトラップ (trap) やオプション (set) 設定が可能です。

  2. 括弧を使用 (sh/bash互換構文):
    ksh
    another_function() {
    # 関数本体
    echo "Inside another_function"
    echo "Argument 1: $1"
    echo "Number of arguments: $#"
    # ...
    }

    この形式はBourne Shell互換であり、bashなど他の多くのシェルでも使用できます。こちらの方が一般的かもしれません。

どちらの構文を使っても、関数は定義する前に呼び出すことはできません(ただし、スクリプト全体が読み込まれてから実行されるため、定義がファイルの後半にあっても呼び出しがエラーにならない場合もあります)。一般的には、スクリプトの先頭付近で関数をまとめて定義するのが良い習慣です。

引数とローカル変数

関数に渡された引数は、関数内で $1, $2, …、$@ のように参照できます。スクリプト全体の引数とは別に関数独自の引数として扱われます。

関数内で定義した変数は、デフォルトではグローバル変数となり、関数から抜け出しても保持されます。関数内で一時的に使用する変数を他の変数と衝突させたくない場合は、typeset コマンドを使ってローカル変数として宣言します。

“`ksh

!/bin/ksh

GLOBAL_VAR=”global”

function show_vars {
echo “Inside function:”
echo “GLOBAL_VAR: $GLOBAL_VAR” # グローバル変数を参照

typeset LOCAL_VAR="local" # ローカル変数を定義
echo "LOCAL_VAR: $LOCAL_VAR"

typeset -i func_count=0 # 整数型のローカル変数
(( func_count++ ))
echo "Func count: $func_count"

}

echo “Outside function (before call):”
echo “GLOBAL_VAR: $GLOBAL_VAR”
echo “LOCAL_VAR: $LOCAL_VAR” # ここではまだ存在しない

show_vars arg1 arg2

echo “Outside function (after call):”
echo “GLOBAL_VAR: $GLOBAL_VAR”
echo “LOCAL_VAR: $LOCAL_VAR” # 関数内で定義したローカル変数は関数終了時に消滅
``typeset(引数なし)またはtypeset variable_nameの形式で変数を宣言すると、それがローカル変数になります(functionキーワードを使った関数定義の場合)。sh/bash互換構文の関数では、typeset variable_nameの形式でローカル変数になります。local`コマンドはkshでは使用できません。

戻り値

関数は return コマンドを使って終了し、終了ステータス(0〜255の整数)を返すことができます。この値は呼び出し側で $? 変数を使って取得できます。

“`ksh

!/bin/ksh

function check_file {
local file_path=$1 # 関数引数をローカル変数に代入
if [ -f “$file_path” ]; then
return 0 # 成功(ファイルが存在する)
else
return 1 # 失敗(ファイルが存在しない)
fi
}

関数を呼び出し、終了ステータスを確認

check_file “myfile.txt”
if [ $? -eq 0 ]; then
echo “File exists.”
else
echo “File does not exist.”
fi

check_file “/non/existent/path”
if [ $? -ne 0 ]; then
echo “File does not exist (checked again).”
fi
``
関数から文字列などのデータ値を返したい場合は、標準出力に出力し、呼び出し側でコマンド置換
$()` を使ってその出力を取得するのが一般的です。

“`ksh

!/bin/ksh

function get_greeting {
local name=$1
echo “Hello, $name!” # 出力する
}

関数の出力を変数に代入

message=$(get_greeting “Alice”)
echo “Received message: $message”
“`

高度なトピック

kshは、他のシェルにはないか、あるいはより洗練された形で提供されるいくつかの高度な機能を持っています。

コプロセス (Coprocess) (|&)

コプロセスは、バックグラウンドで実行されるプロセスと双方向(読み書き両方)の通信を行うための機能です。パイプ (|) は単方向(標準出力から標準入力へ)ですが、コプロセスはより柔軟なプロセス間通信を可能にします。

“`ksh

!/bin/ksh

コプロセスとしてコマンドを実行

この例では、bc (電卓コマンド) をコプロセスとして起動し、計算式を渡して結果を受け取る

bc |& # bc をコプロセスとして起動

コプロセスへの書き込みと読み込み

print -p はコプロセスに書き込む組み込みコマンド

read -p はコプロセスから読み込む組み込みコマンド

print -p “10 + 5” # bcに計算式を渡す
read -p result # bcからの結果をresult変数に読み込む

echo “Result: $result” # 結果を表示

print -p “scale=2; 10/3” # 別の計算式
read -p result
echo “Result: $result”

コプロセスの終了

print -p “quit”
wait # コプロセスが終了するのを待つ
``
コプロセスは、永続的なプロセスとの対話や、複雑なデータ処理パイプラインの構築に役立ちます。
-p` オプションは、コプロセスとの通信チャネルを指定します。

シグナル処理 (trap)

trapコマンドは、スクリプト実行中に発生する特定のシグナル(Ctrl+Cによる割り込み、終了シグナルなど)や、スクリプトの終了(成功/失敗を問わない)、エラー発生時などに実行されるコマンドを指定します。

“`ksh

!/bin/ksh

Ctrl+C (SIGINT) を捕捉し、メッセージを表示して無視する

trap ‘echo “Ctrl+C ignored!”‘ SIGINT

スクリプト終了時(成功/失敗に関わらず)に実行される処理

EXITシグナルは特殊で、trapコマンドが評価された時点で設定される

trap ‘echo “Script finished.” ; rm -f /tmp/temp_file_$$’ EXIT

ERRシグナルは、コマンドがゼロ以外の終了ステータスを返した場合に発生 (ksh93u+以降)

trap ‘echo “An error occurred on line $LINENO”‘ ERR

echo “Running some process…”
sleep 5 # 5秒待つ(この間にCtrl+Cを押してみる)

エラーを発生させてみる

non_existent_command # ERRトラップが設定されていれば実行される

echo “Script completing normally.”
exit 0 # EXITトラップが実行される
``trap` は、一時ファイルのクリーンアップなど、スクリプトの終了処理を確実に行うために非常によく使われます。

連想配列 (Associative Arrays)

前述の typeset -A で触れましたが、連想配列はksh93の強力な機能です。文字列をキーとして値にアクセスできるため、設定情報の管理や、データのグループ化に便利です。

“`ksh

!/bin/ksh

typeset -A config # 連想配列を宣言

config[hostname]=”server.example.com”
config[port]=”8080″
config[user]=”admin”

echo “Hostname: ${config[hostname]}”
echo “Port: ${config[port]}”

全てのキーを取得 (ksh93u+以降)

for key in “${!config[@]}”
do
echo “Key: $key, Value: ${config[$key]}”
done
“`

名前参照 (Nameref) (typeset -n)

ksh93u+で導入された機能で、変数に別名をつけることができます。関数に引数として変数を渡し、関数内でその変数自体を変更したい場合などに非常に便利です(ポインタのような概念)。

“`ksh

!/bin/ksh

function increment {
typeset -n var=$1 # $1(渡された変数名)を参照する変数varを作成
(( var++ )) # varを通じて元の変数をインクリメント
}

count=10
echo “Before increment: $count”
increment count
echo “After increment: $count”

別の変数でも試す

value=100
echo “Before increment: $value”
increment value
echo “After increment: $value”
``typeset -n var=$1は、引数として渡された文字列(例: "count")を変数名として解釈し、その変数本体をvarという名前で参照できるようにします。これにより、関数内でvar` を変更すると、呼び出し側の元の変数も変更されます。

数値演算 (let, (( )))

kshは、他の多くのシェルよりも洗練された数値演算機能を持っています。

  • let コマンド: 算術式を評価し、変数の値を変更できます。
    ksh
    #!/bin/ksh
    let count=10
    let count=count+5
    let count++
    let "result = (count * 2) / 3" # スペースを含む場合はクォートが必要
    echo "Result: $result"
  • (( )) 構文: let コマンドと同様に算術式を評価しますが、より簡潔な構文です。条件式としても使えます。
    “`ksh
    #!/bin/ksh
    count=10
    (( count = count + 5 ))
    (( count++ ))
    result=$(( (count * 2) / 3 )) # コマンド置換としても使える
    echo “Result: $result”

    条件式として使用

    if (( count > 20 )); then
    echo “Count is greater than 20.”
    fi
    ``(( ))構文は、C言語やJavaのような算術式をほぼそのまま記述でき、変数の先頭に$を付ける必要もありません。kshでは(( ))` 構文を使うのが一般的です。

パラメータ展開 (Parameter Expansion)

変数名の後ろに特殊な演算子を付けて、変数の値を様々に加工・置換する機能です。kshのパラメータ展開は非常に豊富で強力です。

構文は ${parameter op word} または ${parameter op} の形式です。

  • ${parameter:-word}: parameter が未定義または空文字列の場合に word を使用します。元の parameter の値は変更されません。
    ksh
    $ VAR="" ; echo ${VAR:-default} # 出力: default
    $ VAR="value" ; echo ${VAR:-default} # 出力: value
  • ${parameter:=word}: parameter が未定義または空文字列の場合に word を使用し、かつその値を parameter に代入します。
    ksh
    $ unset VAR ; echo ${VAR:=default} # 出力: default
    $ echo $VAR # 出力: default
    $ VAR="value" ; echo ${VAR:=default} # 出力: value
    $ echo $VAR # 出力: value
  • ${parameter:+word}: parameter が未定義でも空文字列でもない場合に word を使用します。
    ksh
    $ VAR="" ; echo ${VAR:+alt} # 出力: (何も表示されない)
    $ VAR="value" ; echo ${VAR:+alt} # 出力: alt
  • ${parameter:?word}: parameter が未定義または空文字列の場合にエラーメッセージ word を表示してスクリプトを終了します。word を省略すると標準のエラーメッセージが表示されます。必須のパラメータが存在するかどうかを確認するのに便利です。
    ksh
    $ unset VAR ; echo ${VAR:?"Error: VAR is not set"}
    # 出力: Error: VAR is not set
    # スクリプトはここで終了

  • ${#parameter}: 文字列 parameter の長さ(文字数)を返します。配列や連想配列の場合は要素数を返します。
    ksh
    $ STR="hello" ; echo ${#STR} # 出力: 5
    $ typeset -a arr=(a b c) ; echo ${#arr[*]} # 出力: 3

  • 文字列の削除 (Pattern Removal):

    • ${parameter#pattern}: parameterの先頭からpatternにマッチする最短の部分を削除します。
    • ${parameter##pattern}: parameterの先頭からpatternにマッチする最長の部分を削除します。
    • ${parameter%pattern}: parameterの末尾からpatternにマッチする最短の部分を削除します。
    • ${parameter%%pattern}: parameterの末尾からpatternにマッチする最長の部分を削除します。
      ksh
      $ FILE="/path/to/myfile.txt"
      $ echo ${FILE#*/} # 出力: path/to/myfile.txt (最初の/を削除)
      $ echo ${FILE##*/} # 出力: myfile.txt (最後の/までの全てを削除 - ファイル名のみ抽出)
      $ echo ${FILE%.*} # 出力: /path/to/myfile (最後の.txtを削除 - 拡張子なし)
      $ echo ${FILE%%.*} # 出力: /path/to/myfile (最後の.txtを削除 - 同じ例)
      $ PATH="/usr/local/bin:/usr/bin:/bin"
      $ echo ${PATH%:*} # 出力: /usr/local/bin:/usr/bin (最後の:binを削除)
      $ echo ${PATH%%:*} # 出力: /usr/local/bin (最初の:以降を全て削除)

      これらの機能は、ファイルパスからディレクトリ部分やファイル名、拡張子を抽出するなど、文字列処理で頻繁に利用されます。
  • 文字列の置換 (Substitution):

    • ${parameter/pattern/string}: parameter内で最初に見つかったpatternをstringに置換します。
    • ${parameter//pattern/string}: parameter内で見つかったpatternを全てstringに置換します。
    • ${parameter/#pattern/string}: parameterの先頭がpatternにマッチした場合にstringに置換します。
    • ${parameter/%pattern/string}: parameterの末尾がpatternにマッチした場合にstringに置換します。
      ksh
      $ STR="hello world hello again"
      $ echo ${STR/hello/hi} # 出力: hi world hello again (最初のhelloのみ置換)
      $ echo ${STR//hello/hi} # 出力: hi world hi again (全てのhelloを置換)
      $ echo ${STR/#hello/hi} # 出力: hi world hello again (先頭がhelloなので置換)
      $ echo ${STR/%again/now} # 出力: hello world hello now (末尾がagainなので置換)

      パターンには、拡張ワイルドカード(glob)が使用できます。例えば ${FILE//\//_} でパス区切り文字 /_ に置換できます。

これらのパラメータ展開機能を使うことで、外部コマンド(sed, awkなど)を呼び出すことなく、シェル内で効率的に文字列処理を行えます。

デバッグ (set -x, set -v)

スクリプトのデバッグに便利なオプションです。

  • set -x: スクリプトが実際に実行するコマンドを、実行前にプラス記号 + とともに標準エラー出力に表示します。変数が展開された後のコマンドを確認できます。
  • set -v: スクリプトの入力行を、読み込んだそのままの形で標準エラー出力に表示します。

“`ksh

!/bin/ksh

set -x # ここからデバッグトレースが有効になる

VAR=”test”
echo “Value is: $VAR”
ls non_existent_file # エラーを発生させる

set +x # ここからデバッグトレースが無効になる
echo “Debugging finished.”
``set -xset -vはスクリプトの一部でのみ有効/無効にすることも可能です。スクリプト全体に対して有効にしたい場合は、シバン行の後に記述するか、スクリプトの実行時にksh -x your_script.ksh` のようにオプションを指定します。

kshの特徴とbashとの比較

kshの主要な特徴を改めてまとめ、多くの人が使っているbashと比較してみましょう。

kshの主な特徴のまとめ:

  • 歴史と互換性: Bourne Shell (sh) と高い互換性があり、既存のshスクリプトを実行しやすい。
  • コマンドライン編集: viモード/emacsモードをサポートし、高度なコマンド編集が可能。
  • typeset による変数属性: 変数の型(整数、配列、連想配列)や表示形式、読み取り専用などを詳細に制御できる。特に連想配列と名前参照はbashより後のバージョンで取り込まれた。
  • [[ ]] 構文: 安全で表現力豊かな条件式。パターンマッチや正規表現マッチ(ksh93以降)が可能。
  • (( )) 構文: 簡潔で強力な算術式評価。Cスタイルforループと合わせて使うと便利。
  • select 文: 対話的なメニューを簡単に作成できる独自機能。
  • コプロセス (|&): 双方向通信が可能なプロセス間通信機能。
  • パラメータ展開: Bourne Shell由来の基本的な機能に加え、文字列の削除/置換など豊富な機能を持つ。
  • パフォーマンスと堅牢性: 特にシステム管理などの用途で重視される特性。

bashとの比較:

bash (Bourne-Again Shell) は、GNUプロジェクトによって開発されたシェルで、Linux環境では最も広く普及しています。bashはkshの機能の多くを取り込んでおり、特にbash 2.0以降はksh88の機能をほぼ網羅しています。bash 3.x以降はksh93の機能(連想配列、名前参照など)も部分的に取り込んでいます。

機能 ksh (主に ksh93) bash (現代のバージョン) 備考
基本互換性 sh互換 sh互換 どちらもBourne Shellのスーパーセット
コマンドライン編集 vi/emacsモード vi/emacsモード ほぼ同等
変数属性/型 typeset (豊富) declare/typeset (豊富だが一部違いあり) kshのtypesetは多機能
連想配列 -A (ksh93 vetch+) -A (bash 4.0+) kshの方が先に実装
名前参照 -n (ksh93u+) -n (bash 4.3+) kshの方が先に実装
数値演算 let, (( )) let, (( )) 構文はほぼ同じ
条件式 [ ], [[ ]] (=~ 正規表現) [ ], [[ ]] (=~ 正規表現) bashの[[ ]]はkshを参考に実装
拡張ワイルドカード @(...), *(...), ?(...), +(...), !(...) extglob有効時 (shopt -s extglob) どちらも強力、有効化方法が異なる
コプロセス |&, print -p, read -p なし kshのユニークな機能
select あり あり どちらも利用可能
シグナル処理 trap trap ほぼ同等
パラメータ展開 豊富 kshの多くを取り込み済み 多くの共通機能を持つ
デバッグオプション set -x, set -v set -x, set -v ほぼ同等
設定ファイル .profile, .kshrc .bash_profile, .bashrc, など ファイル名が異なる

どのシェルを選ぶべきか?

  • 既存のシステム環境: 多くのUNIX系商用OSではkshが標準です。これらの環境で作業する場合や、既存のkshスクリプトをメンテナンスする場合は、kshの知識が必須です。
  • 学習リソースとコミュニティ: Linux環境ではbashが圧倒的に普及しており、オンラインの情報やコミュニティサポートはbashの方が豊富です。入門者にとっては学習しやすいかもしれません。
  • 特定の機能要件: コプロセスのようなksh独自の機能が必要な場合は、kshを選択することになります。
  • スクリプトの移植性: sh互換性を重視する場合、bashとkshはどちらも有力な選択肢です。ただし、ksh93やbash独自の拡張機能([[ ]]=~、連想配列など)を使う場合は、その機能がターゲット環境のシェルバージョンで利用可能か確認が必要です。

多くのLinuxユーザーにとってはbashで十分な機能が揃っていますが、UNIXシステムを深く理解したい、ksh独自の便利機能を活用したい、あるいは特定のシステム環境で作業する必要がある、といった場合にはkshを学ぶ価値は非常に大きいと言えます。

実践的な例

kshスクリプトを使って、いくつかの実践的なタスクを実行してみましょう。

例1: ファイルのバックアップスクリプト

指定したファイルをタイムスタンプ付きのファイル名で別のディレクトリにバックアップします。

“`ksh

!/bin/ksh

ファイルバックアップスクリプト

引数: バックアップ元のファイルまたはディレクトリ

SOURCE=”$1″
BACKUP_DIR=”/tmp/backup” # バックアップ先ディレクトリ

引数が指定されているか確認

if [ $# -eq 0 ]; then
echo “Usage: $0
exit 1
fi

バックアップ先ディレクトリが存在しない場合は作成

if [ ! -d “$BACKUP_DIR” ]; then
echo “Creating backup directory: $BACKUP_DIR”
mkdir -p “$BACKUP_DIR”
if [ $? -ne 0 ]; then # mkdirが失敗した場合
echo “Error: Failed to create backup directory.”
exit 1
fi
fi

ソースが存在するか確認

if [ ! -e “$SOURCE” ]; then
echo “Error: Source ‘$SOURCE’ does not exist.”
exit 1
fi

タイムスタンプを生成 (YYYYMMDD_HHMMSS形式)

ksh93ではprintfの拡張機能で日時フォーマットが可能

TIMESTAMP=$(printf ‘%(%Y%m%d_%H%M%S)T’)

または、dateコマンドを使用 (互換性が高い)

TIMESTAMP=$(date “+%Y%m%d_%H%M%S”)

バックアップファイル/ディレクトリの名前を生成

BACKUP_NAME=”${SOURCE##*/}.${TIMESTAMP}” # ソースのファイル名/ディレクトリ名にタイムスタンプを追加
DESTINATION=”$BACKUP_DIR/$BACKUP_NAME”

echo “Backing up ‘$SOURCE’ to ‘$DESTINATION’…”

cpコマンドでバックアップを実行

-r はディレクトリの場合に必要

if [ -d “$SOURCE” ]; then
cp -r “$SOURCE” “$DESTINATION”
else
cp “$SOURCE” “$DESTINATION”
fi

cpコマンドの終了ステータスを確認

if [ $? -eq 0 ]; then
echo “Backup successful.”
exit 0
else
echo “Error: Backup failed.”
exit 1
fi
``
**実行例:**
ksh backup_script.ksh /home/user/documents/report.txt`

例2: 対話的なファイル操作メニュー

select文を使って、ユーザーに操作を選択させるメニューを作成します。

“`ksh

!/bin/ksh

ファイル操作メニュー

echo “Select a file operation:”

select operation in “List files” “View file” “Edit file” “Remove file” “Exit”
do
case “$operation” in
“List files”)
echo “— Files in current directory —”
ls -l
echo “———————————-”
;; # selectループはbreakしない限り繰り返されるので;;で区切るだけ

    "View file")
        read -p "Enter file name to view: " filename
        if [ -f "$filename" ]; then
            echo "--- Content of $filename ---"
            cat "$filename"
            echo "----------------------------"
        else
            echo "Error: '$filename' is not a regular file or does not exist."
        fi
        ;;

    "Edit file")
        read -p "Enter file name to edit: " filename
        if [ -f "$filename" ]; then
            # デフォルトのエディタでファイルを開く
            # $EDITOR 環境変数が設定されていなければviを使用
            ${EDITOR:-vi} "$filename"
        else
             echo "Error: '$filename' is not a regular file or does not exist."
        fi
        ;;

    "Remove file")
        read -p "Enter file name to remove: " filename
        # 削除前に確認を求める(rm -iのエイリアスが設定されていなければ)
        if [ -f "$filename" ]; then
            read -p "Are you sure you want to remove '$filename'? (y/N): " confirm
            if [[ "$confirm" = [yY]* ]]; then # y または Y で始まるかチェック
                rm "$filename"
                if [ $? -eq 0 ]; then
                    echo "'$filename' removed."
                else
                    echo "Error removing '$filename'."
                fi
            else
                echo "Removal cancelled."
            fi
        else
             echo "Error: '$filename' is not a regular file or does not exist."
        fi
        ;;

    "Exit")
        echo "Exiting."
        break # selectループを終了
        ;;

    *) # Invalid input (empty REPLY or invalid number)
        echo "Invalid selection. Please enter a number from 1 to 5."
        # selectは無効な入力で自動的にループを継続
        ;;
esac
echo # 見やすくするために空行を表示
echo "Select next operation:" # 再度プロンプトを表示

done

echo “Script finished.”
``select文とcase`文を組み合わせることで、ユーザーフレンドリーなメニュー操作を実現できます。

例3: シンプルなログ解析スクリプト (パラメータ展開、連想配列)

ログファイルから特定のキーワードを含む行を抽出し、キーワードごとの出現回数を集計します。

“`ksh

!/bin/ksh

シンプルなログ解析スクリプト

ログファイル名と検索キーワードを指定

LOG_FILE=”$1″
KEYWORDS=”$2″ # キーワードをカンマ区切りで指定する想定 (例: “ERROR,WARNING,INFO”)

引数チェック

if [ $# -ne 2 ]; then
echo “Usage: $0
exit 1
fi

ログファイルが存在するか確認

if [ ! -f “$LOG_FILE” ]; then
echo “Error: Log file ‘$LOG_FILE’ not found.”
exit 1
fi

キーワードを配列に分割 (IFSを一時的に変更)

typeset -a keyword_list
OIFS=”$IFS” # 元のIFSを保存
IFS=, # IFSをカンマに変更
keyword_list=($KEYWORDS) # カンマで区切られた文字列を配列に展開
IFS=”$OIFS” # IFSを元に戻す

連想配列で出現回数をカウント (ksh93 vetch+ が必要)

typeset -A count_map

初期化

for key in “${keyword_list[@]}”
do
count_map[“$key”]=0
done

echo “Analyzing log file: $LOG_FILE”

ログファイルを一行ずつ読み込み

while read -r line
do
# 各キーワードで検索
for keyword in “${keyword_list[@]}”
do
# [[ ]]とパターンマッチまたは正規表現マッチで検索
if [[ “$line” = “$keyword” ]]; then # パターンマッチで検索
# または、正規表現マッチ (ksh93u+):
# if [[ “$line” =~ “$keyword” ]]; then
(( count_map[“$keyword”]++ )) # 対応するキーワードのカウントをインクリメント
fi
done
done < “$LOG_FILE” # 標準入力をファイルにリダイレクト

echo “— Analysis Results —“

結果を表示

for keyword in “${keyword_list[@]}”
do
echo “Keyword ‘$keyword’: ${count_map[“$keyword”]} occurrences”
done
echo “———————-“

exit 0
``
**実行例:** ダミーのログファイルを作成し、
ksh analyze_log.ksh my.log “Error,Warning”` のように実行します。

これらの例は、kshの基本的な構文、制御構造、変数属性、そしてksh独自の強力な機能(select、連想配列など)をどのように組み合わせて実践的なスクリプトを作成するかを示しています。

まとめ

本記事では、KornShell (ksh) の基本的な概念から、インストール、日々のコマンド操作、そしてスクリプト作成における変数、制御構造、関数、さらにはtypeset[[ ]]select、コプロセスといったksh独自の強力な機能までを詳細に解説しました。また、bashとの比較を通じて、kshがどのような位置づけにあるのかについても触れました。

kshは、特にUNIX系のエンタープライズ環境で重要な役割を果たしており、その堅牢性や豊富な機能は、システム管理や高度なスクリプト作成において今なお大きな価値を持っています。bashなどの他のシェルしか使ったことがない方にとっても、kshを学ぶことはシェルスクリプトの理解を深め、対応できるシステム環境を広げる上で非常に有益です。

kshは、Bourne ShellのシンプルさとC Shellの便利さを融合させ、さらに独自の革新的な機能を追加した、まさにシェルスクリプト言語の歴史における重要なマイルストーンです。ksh93以降のバージョンで追加された連想配列や名前参照といった機能は、より複雑なタスクをシェルスクリプトで効率的に記述することを可能にします。

kshの利点:

  • Bourne Shellとの高い互換性
  • 強力なコマンドライン編集機能
  • 豊富な変数属性 (typeset) による詳細な変数制御
  • [[ ]] 構文による安全で表現力豊かな条件式
  • (( )) 構文による効率的な数値演算
  • select 文による簡単な対話メニュー作成
  • コプロセスによる双方向通信
  • 堅牢で高性能

kshの欠点:

  • Linux環境ではbashほど普及していないため、情報源が限られる場合がある
  • 一部の最新機能(特にksh93u+以降)は、古いシステムでは利用できない可能性がある
  • bashと比較して、インタラクティブな使い勝手(プロンプトカスタマイズの容易さなど)や、ユーザー向けの補助機能(補完機能の豊富さなど)で劣ると感じられる場合がある(ただしこれは個人的な好みに依るところも大きい)

kshの学習は、単に新しいシェルを覚えるだけでなく、シェルスクリプトの根本的な概念や、異なるシェル間での機能の進化と差異を理解することにも繋がります。

さらなる学習のために:

  • manページ: man ksh コマンドは、kshの最も正確で詳細なリファレンスです。最初は難解に感じるかもしれませんが、特定の機能の正確な構文や挙動を知りたいときに非常に役立ちます。
  • オンラインドキュメント: “ksh tutorial” や “ksh reference” で検索すると、多くのチュートリアルや解説ページが見つかります。ksh88とksh93で機能が異なる場合があるので、参照しているドキュメントのバージョンに注意してください。
  • 既存のkshスクリプトを読む: 実際に動作しているkshスクリプト(特にシステム管理系のスクリプトなど)を読むことは、実践的なkshの使い方を学ぶ上で非常に効果的です。

約5000語にわたる詳細な解説でしたが、これでkshの基本的な使い方から、その強力な機能まで、全体像を掴むことができたはずです。ぜひ実際にkshを使ってみて、その特徴を体感してください。kshの世界へようこそ!

コメントする

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

上部へスクロール