Shellの基礎知識(基礎編)

【Shellの基礎知識】組み込みコマンドの活用法|最適化テクニック

組み込み関数

シェル組み込みコマンドは、シェルが内部で直接実行する軽量なコマンド群であり、システム操作やスクリプト作成を効率化するうえで非常に重要です。本記事では、組み込みコマンドの基礎から応用までを徹底解説し、作業効率を最大限に高める方法をご紹介します。

組み込みコマンドの概要と基礎

シェル組み込みコマンドの基礎を理解することは、効率的にシステムを操作するための第一歩です。このセクションでは、組み込みコマンドの概要と、外部コマンドとの違い、メリットとデメリットを詳しく見ていきます。

組み込みコマンドとは?

組み込みコマンド(built-in command)とは、シェルそのものが内部で直接サポートしているコマンドのことです。これらのコマンドは、システム外部の実行ファイルに依存せず、シェル内で実行されるため、速度と効率性の面で優れています。

Linuxで使用可能なコマンドは、/binや/sbin、/usr/binなどのディレクトリに収められているプログラムのほかに、組み込みのコマンドが存在します。どのような組み込みコマンドがあるのかを調べるには、helpコマンドを使用することで表示されます。

コマンドは大きく分けて下記の2種類存在し、いずれもシェルスクリプト中で使用できます。

組み込みコマンド

  • 組み込みコマンド
  • 外部コマンド

OSのインストール時などに導入されるコマンドを外部コマンドと呼びます。OSによっても細かい内容は異なりますし、サードパーティーが用意したものも数多く存在します。

外部コマンドとの違い

  • 実行速度の違い
    組み込みコマンドはシェル内部で処理されるため、外部コマンドと比較して起動時間が短く、高速に動作します。一方、外部コマンドは実行時に別プロセスとして起動するため、若干のオーバーヘッドが発生します。
  • 使用例
    組み込みコマンド:cd(ディレクトリ移動)やecho(文字列表示)
    外部コマンド:ls(ファイルリスト表示)やgrep(文字列検索)
  • 制限の有無
    組み込みコマンドはシェルに依存するため、使用できるコマンドはシェルの種類(Bash、Zshなど)によって異なります。一方、外部コマンドは基本的にシェルに関係なく動作します。

組み込みコマンドを使用するメリットとデメリット

シェル組み込みコマンドには、いくつかの重要なメリットがあります。まず第一に、組み込みコマンドはシェル内で直接実行されるため、別プロセスを起動する必要がなく、高速に動作します。この高速性は、特に大量のコマンドを処理するスクリプトにおいて、大きなパフォーマンス向上をもたらします。

また、組み込みコマンドはシンプルな構文で使用できるため、スクリプトが簡潔になり、可読性が向上します。さらに、外部プログラムに依存しないため、移植性が高く、異なるシステム間での利用が容易です。

一方で、組み込みコマンドにはいくつかのデメリットも存在します。外部コマンドと比較すると、機能が制約されている場合があり、複雑な処理を行う際には不十分なことがあります。

また、組み込みコマンドは使用するシェルの種類に依存するため、異なるシェル間での挙動が異なることがあり、スクリプトの互換性に影響を与える可能性があります。さらに、柔軟性の面では外部コマンドに劣る場合があり、カスタマイズや拡張が難しいこともデメリットの一つです。

このように、シェル組み込みコマンドはその効率性やシンプルさから多くの利点を提供しますが、使用するシナリオや目的に応じて、外部コマンドとの使い分けを検討することが重要です。

使用するメリットとデメリット

  • メリット
    • 高速性:組み込みコマンドは別プロセスを起動しないため、リソース消費が少なく高速に動作します。
    • シンプルな構文:シェルスクリプト内で直接使用でき、コードが簡潔になります。
    • 依存関係の削減:外部プログラムを必要としないため、スクリプトの移植性が向上します。
  • デメリット
    • 機能の制約:外部コマンドに比べて、機能が限定される場合があります。
    • シェル依存性:組み込みコマンドはシェル固有であり、異なるシェル間で動作が異なることがあります。
    • 柔軟性の欠如:カスタマイズや拡張が難しい場合があります。

ここから、各組み込みコマンドについて、動作と使い方を見ていきましょう。

基本組み込みコマンドの動作例

シェルの組み込みコマンドは、シェルスクリプトの基礎を学ぶ上で欠かせない要素です。ここでは、代表的な組み込みコマンドであるechoとcdを取り上げ、それぞれの基本的な動作と使用例を解説します。

ヌルコマンド「:」

「:(ヌルコマンド)」は、何もしないがいつも成功する(真の状態を返す)コマンドです。実行終了ステータスで「0」を返します。いつも真の値を返すので、条件判定を必ず真にしたい場合によく使われます。

while文の場合には以下のように書き表わします。

while : 👈 「:」コマンドにより終了ステータス0が返り、無限ループになる
do
<処理コマンド>
if [<停止条件>]; then
 continue
else
 break
fi
done

ここでは『while』文での実行例を挙げていますが、ヌルコマンドは、while文に限らず、if文などでも使用可能です。

停止条件を記述せずに実行すると、無限ループに入ってしまうため注意してください!

「if」文における処理無しの例

if [ <条件式> ]; then
  : 👈 「:」コマンドにより終了ステータス0が返り、処理を何も実行しない
fi

「:(ヌル)」コマンドは、パラメータ展開やリダイレクトだけを行って、コマンドを実行したくない場合や、if/for/while文のリストで何もコマンドを実行したくない場合にも使用されます。

$ : > test_file  👈 test_fileという名前のファイルサイズゼロのファイルができる

ドットコマンド「.」

普通のコマンドとは異なり、新しくプロセスを作らずに現行のシェルプロセスを使って指定されたファイルを読み込み実行します。その結果、指定されたファイルで記述されている変数や関数が、現行のシェルで有効に使えるようになります。

「.」コマンドの終了ステータスは、読み込んだファイル中で実行された最後のリストの終了ステータスになります。ただし、ファイル中にリストが1つもない場合は、終了ステータスは「0」になります。

 簡単なシェルスクリプト「outer_file.sh」を作成し、コンソール上から「.(ドット)」で読み込ませ、実行させてみます。

[ outer_file.sh ]

!/bin/sh
echo "これは読み込まれた外部ファイルから出力しています。"

下記の「.(ドット)」コマンドを使って、外部ファイルをコンソール上へ読み込ませます。

$ . outer_file.sh

実際に、「.(ドット)」コマンドを使ってコンソール上で実行してみます。

読み込んだシェルスクリプト「outer_file.sh」内部の「echo」コマンドから、直接コンソール上へ出力されているのが分かります。

breakコマンド「break」

「for」文 or「while」文でループを扱っている場合、ループを抜けて次の処理に移行できます。break n (n=1,2,3,....)と入力すると、n回入れ子になったループを抜けられます。nの数値を省略するとbreak 1と同じになります。

breakコマンドによってループを抜けると、終了ステータスは「0」になります。

for i in a b c d
do
    if [ "${i}" = "b" ]; then
      break
    fi
    echo "変数[ ${i} ]のループ"
done

実行例

変数${ i }の値が「b」になった時点でforループを抜けてしまうため、コンソール上へは「a」のみ出力されています。
変数の${i}の値が「b」に変わった後の判定文でforループを抜けてしまうため、「変数bのループ」メッセージは出力されません。

continueコマンド「continue」

breakとは異なり、「for」文 or「while」文でループを扱っている場合、ループを抜けずに次のループに移行する場合に用います。breakと同様の記載方法ですが、 continue n (n≧2) とした場合は、その分だけ外側のループまで戻り、そちらで次のループに移行します。

continueコマンドによって次のループに進むと、終了ステータスは「0」になります。

for i in a b c d
do
    if [ "${i}" = "b" ]; then
      continue
    fi
    echo "変数[ ${i} ]のループ"
done

実行例

一見「break」コマンドの動作に似ていますが、「continue」コマンドでは、ループを抜けることはありません。
変数${i}の値が「b」の時、次のループへ移行している為、「変数bのループ」メッセージだけが出力されていません。

cdコマンド「cd」

実行中のシェルの作業ディレクトリを変更します。引数のディレクトリ名を省略した場合は、"$HOME"が指定されたものとみなされます。

# カレントディレクトリの一階層上に移動する
$ cd ..

# 直前の作業ディレクトリに移動する
$ cd -

下記のページで「cd」コマンドを使った処理を行っています。ご参考まで!

evalコマンド「eval」

引数に与えた文字列をコマンドとして実行します。引数が変数の場合、展開した結果をコマンドとして実行します。解釈の結果、実行されたコマンドの終了ステータスが、evalコマンドの終了ステータスになります。

これはちょっとややこしいので注意してください!

例えば、まず「var1=date」を宣言して、さらに「var2=${var1}」として変数${var2}の中に変数${var1}を代入したとします。目的としては「echo ${var2}」とし場合に「現在の日時(yyyy年 mm月 dd日 土曜日 hh:mm:ss JST)」を出力させることです。

要するにシェルコマンドの2重展開がしたい場合です。実際にシェルとして記述すると下記になります。

$ var1=date
$ var2=${var1}

$ echo ${var2}

実行例

上記の実行例では、「var2=${var1}」として変数${var2}の中に変数${var1}を代入したにも関わらず、結果が文字列「date」として出力されてしまっています。

目的は、この文字列として出力された「date」をコマンドとして認識させたい!

そこで「eval」コマンドを使用します。

$ var1=date
$ var2=${var1}

$ eval ${var2}

一部の「echo」コマンドを「eval」コマンドへ修正して、再実行します。

「eval」コマンドを使って、文字列として出力された「date」を、無事にコマンドとして認識させることが出来ました。

下記のページで「eval」コマンドを使った処理を行っています。ご参考まで!

execコマンド「exec」

「exec」コマンド実行後は、プロセスが置き換わる為、以降に記述されたコマンドは実行されない

execコマンドは、新しくプロセスを作らず現行のカレントシェルのプロセスと置き換えて、引数のコマンドを実行させます。
exec以降に記述されたコマンドは実行されません。execコマンドにリダイレクト先のみ記述すると、現行シェルすべてに対してリダイレクト処理を行わせることが出来ます。このコマンドを実行したら、もう元のシェルに戻ることはありません。

引数で指定したコマンドが正常に起動できた場合、シェルには戻らないため、終了ステータスはありません。コマンドが起動できなかった場合は終了ステータスは「0」以外になります。引数を指定せず、リダイレクトのみを行った場合、正常にリダイレクトが行われれば終了ステータスは「0」になります。

# 現行シェルの標準出力/標準エラー出力がログに出力される
exec >> /var/log/messages 2>&1

#(※1)
exec cd /usr/bin/
exec echo hello

1:execコマンドで「cd」が起動された時点でシェルスクリプトのプロセス自体が「cd」のプロセスと置き換わります。よってそのあとの「exec echo hello」コマンドは実行されません。「cd」が終了したらこのシェルスクリプトは終了してしまいます。

exitコマンド「exit」

終了コードの値に関わらず、現在のプロセスを終了する

exitコマンドを実行すると、その時点でシェルスクリプトが終了します。引数の終了ステータスで終了ステータスを指定できます。引数に整数を与えると、終了コードがその値になります(デフォルトは0)。

#!/bin/sh

終了コード1で終了する
exit 1

「exit」と「return」の違い

  • exit」:
    終了コードの値に関わらず、現在のプロセスを終了させます。プロセスを閉じてしまうため、実行中のコンソール自体が終了します。
  • return」:
    関数内のみで使用可能です。「return」コマンド実行後は、元の命令に戻ります。実行中のコンソールは終了しません。

 終了ステータスコードの一覧 (bash)

終了ステータス意味
1一般的なエラー
2ビルトインコマンドの誤用
126実行不可(実行権不足)等
127未検知(対象が存在しないコマンド)等
128「exit」コマンドへ不正な値(文字列)等が設定された
128+nシグナル「n」で終了
255範囲外の終了ステータス

exportコマンド「export」

環境変数は、プログラムから起動された子プロセスへも引き継がれる

exportコマンドを実行すると、引数の変数名で指定されたシェル変数が環境変数としてエクスポートされます。exportコマンドを引数なしで実行した場合は、現在エクスポート中の環境変数の一覧が表示されます。

環境変数?

環境変数とは、シェルから起動されたすべてのプロセスに有効な変数を指します。一旦、特定の変数を環境変数としてエクスポートすると、プログラムから起動された子プロセスでも有効となります。つまり、変数の宣言をしなくても参照することが可能となります。

exportコマンドの終了ステータスは「0」になります。ただし、変数名の指定が正しくないなどexportコマンド自体がエラーになった場合は、終了ステータスは「0」以外になります。

今後コマンドで下記の環境変数を用いると、以降は変数宣言無しで値が参照できるようになります。プログラムから起動された子プロセスにも、環境変数は引き継がれます。

BASE_PATH=${BASE_PATH:-"/root/scripts"}

DTIME=$(date)
export BASE_PATH DTIME

主な使用用途としては、システム単位で使用する「規定ディレクトリ」や「実行ディレクトリ」等を設定することが一般的です。

getoptsコマンド「getopts」

シェルに対して"-"と"アルファベット一文字"でオプションを指定された場合、それを解析します。オプションによって挙動を変えたい時にcase文と共に用います。

解釈の結果、オプションが見つかった場合は終了ステータスは「0」になります。オプションではない引数まで解釈が進んだ場合は終了ステータスは「0」以外になります。

--helpなどのいわゆるロングオプションを使う場合は、外部コマンドのgetoptを使うことが多いです。

#!/bin/sh

# -a /-b オプションを指定した場合に出すメッセージを変更する
while getopts a:b: opts
do
 case $opts in
  a) echo "引数:${OPTARG}" ;;
  b) echo "引数:${OPTARG}" ;;
  *) rc=1 ;;
 esac
done;

上記のスクリプトを、シェルスクリプト「func.sh」に記述して実行してみます。

シェルスクリプトを作成する上で、「getoptsコマンドなくしてシェルとは呼べず」と言われるほど、重要な組み込みコマンドです。

下記のページで「getopts」コマンドを使った処理を行っています。ご参考まで!

readコマンド「read」

キーボードなどデバイスからの入力を変数にセットします。対話式スクリプトを作る際に用います。標準入力がEOF(EndOfFile)にならないかぎり、終了ステータスは「0」になります。

#!/bin/sh

# ユーザーが入力したデータをstrに代入
read str1 str2 str3

# strに代入されたデータを表示
echo "str1:$str1" "str2:$str2" "str3:$str3"

上記のスクリプトを、シェルスクリプト「func.sh」に記述して実行してみます。

readonlyコマンド「readonly」

変数名で指定された変数をリードオンリー(書き換え不可)にします。これを設定した後では、その変数の操作(set or unset)が不可能となります。

readonlyコマンドの終了ステータスは「0」になります。ただし、変数名の指定が正しくないなど、readonlyコマンド自体がエラーになった場合は終了ステータスは「0」以外になります。

# この記述以降にVALUE変数を変更しようとするとエラーメッセージが出る
$ VALUE="Not OverWrite"
$ readonly VALUE

シェル変数${VALUE}へ「readonly」を付与した後に、文字列”aaa”の代入を行ってみます。

returnコマンド「return」

「return」コマンド実行後は、元の命令に戻る。実行中のコンソールは終了しない

シェルの関数から抜 けるコマンドです。関数の終了ステータスを決定して終了させます。終了ステータス番号が指定されなかった場合には、関数が終了する直前のコマンドの実行終了コードが返ります。

func() {
 echo $(date)
 return 255
}

リターンコードが「255」で返却されていることが分かります。

「exit」と「return」の違い

  • exit」:終了コードの値に関わらず、現在のプロセスを終了させます。プロセスを閉じてしまうため、実行中のコンソール自体が終了します。

  • return」:関数内のみで使用可能です。「return」コマンド実行後は、元の命令に戻ります。実行中のコンソールは終了しません。

上記の例の場合、dateを実行するだけの関数だとほぼ終了ステータスコードは「0」ですが、returnコマンドを使っているので必ず255が返されます。

終了ステータスコードの一覧 (bash)

終了ステータス意味
1一般的なエラー
2ビルトインコマンドの誤用
126実行不可(実行権不足)等
127未検知(対象が存在しないコマンド)等
128「exit」コマンドへ不正な値(文字列)等が設定された
128+nシグナル「n」で終了
255範囲外の終了ステータス

shiftコマンド「shift」

位置パラメタの値を($2を$1に、$3を$2にというように)左にずらします。

shift [n]

引数として[n]のところに指定した数値の分だけずらします。

func() {
  echo $0 $1 $2 $3 $4 $5 $6 $7 $8 $9
  shift
  echo $0 $1 $2 $3 $4 $5 $6 $7 $8 $9
  shift 3
  echo $0 $1 $2 $3 $4 $5 $6 $7 $8 $9
}

9個の引数を与えてこのシェルスクリプトを実行してみます。

8行目:1から9までの数値を引数にして、スクリプトを実行しています。
9行目:すべての引数がそのまま位置パラメタの値として表示されています。
10行目:位置パラメタを1個シフトしたために、$1(数値2)から$8(数値9)までが表示されました。 値もそれぞれずれていき、最初に$1に代入されていたものがなくなっています。
11行目:次に3個シフトさせたので、$1には最初に$5に代入されていた値が入り、残りの数値が表示されました。ちなみに、シェルスクリプトの最初で位置パラメタを全部クリアさせたい場合には、以下のように記述します。

# 全パラメータのクリア
shift $*

trapコマンド「trap」

シェルスクリプト内で、指定した(複数書けます)シグナルを受け取ったときにどういう処理をするかを指定します。

引数のコマンドとして空文字列を指定した場合は、指定のシグナルがシェルによって無視されるようになります。引数のコマンドを省略した場合は、指定のシグナルの設定が解除されます。エラーが発生しない限り、終了ステータスは「0」が返ります。

msg="トラップを拾いました。"
trap echo ${msg} 1 2 3 15

シグナル番号一覧

番号シグナル名意味主な用途
1SIGHUPハングアップ設定ファイルの再読み込みなど
2SIGINT割り込み (Ctrl+C)プロセスの中断
3SIGQUIT終了 (ダンプ生成)プロセス終了とコアダンプ生成
9SIGKILL強制終了プロセスの即時終了
11SIGSEGVセグメンテーション違反メモリアクセス違反
13SIGPIPEパイプ切断書き込み先が閉じられた場合
15SIGTERM終了要求優雅なプロセス終了
18SIGCONT停止から再開一時停止プロセスの再開
19SIGSTOPプロセス停止強制停止 (再開はSIGCONT)
20SIGTSTP端末停止 (Ctrl+Z)一時停止
30,10,16SIGUSR1ユーザー定義シグナル1カスタム処理
31,12,17SIGUSR2ユーザー定義シグナル2カスタム処理
34SIGRTMINリアルタイムシグナル (最小値)リアルタイム処理向け

補足説明

  • シグナルの送信:
    シグナルを送信するには kill コマンドを使用します。例:
    $ type cd echo
  • カスタムシグナル (SIGUSR1, SIGUSR2):
    ユーザー定義の動作を割り当て可能。アプリケーションやスクリプトで柔軟に利用できます。
  • リアルタイムシグナル:
    リアルタイム処理向けに使用され、標準のシグナルより高い優先度を持ちます。

下記のページで「trap」コマンドを使った処理を行っています。ご参考まで!

typeコマンド「type」

「type」コマンドは、引数で指定したコマンドが、どういう取り扱いなのかを出力します。つまり、組み込みコマンドかどうかや、コマンド本体のある場所を教えてくれます。

$ type cd echo

実行例

umaskコマンド「umask」

「umask」コマンドは、ファイルを生成するときにどういうモード(読み・書き・実行)で作るかを決定します。

umaskコマンドを実行すると、引数で指定されたマスク値(8進数)がシェル自身のumask値として設定されます。umaskコマンドを引数なしで実行すると、現在のumask値を表示します。

$ umask マスク値

下記のページで「umask」コマンドを使った処理を行っています。ご参考まで!

unsetコマンド「unset」

「unset」コマンドは、指定した変数や関数を消去します。現在セットされている変数や関数の名称を引数に指定します。複数指定することも可能です。

$ var="これはテスト変数です。"
$ echo ${var}

# 変数${var}を削除
$ unset var
$ echo ${var}

実行例

ここまでざっと紹介してきましたが、他にも色々な使い方があったり、別の組み込みコマンドが存在するので、ぜひmanコマンド(これも組み込みコマンド)を使って調査してみる事をお勧めします。

外部組み込みコマンドの動作例

一部の組み込みコマンドについては、シェルが直接コマンド機能を実行して処理を高速化させるため、外部コマンドにも同じものがあるにもかかわらず組み込みコマンドとして実装されているものがあります。

POSIXの下では外部コマンドにない物は組み込みコマンドに存在しないというのが基本になりますが、例外もあります。細かい挙動が異なってくることから、目的の処理が出来ない事があるのでそれぞれ見ていきましょう。

基本的に組み込みコマンドの挙動はbashの物をもとにしていますが、一部別のシェルを参照するものもあります。

echoコマンド「echo」

標準出力に引数の文字列を出力するコマンドです。「echo」コマンドはシェルの組み込みコマンドとしても普通のコマンドとしても提供されています。終了ステータスは「0」になります。

コマンドの書式

echo [オプション] メッセージ

コマンドの主なオプション

  • -e:最後の改行が出力される。
  • -n:最後の改行が出力されない。

$ echo "Beエンジニア"

代表的な違いとして、一部シェルの組み込みコマンドで使用可能な「-e(エスケープシーケンスを有効にする)」オプションが、組み込みコマンド(/bin/echo)においては使用不可能です。

FreeBSDの外部コマンドのechoや、SunOS4.xのshのechoなど、-nオプションのみが使えて-eオプションが使えないechoも存在します。

「echo」コマンドは、シェルの操作において、非常に多用されるコマンドです。メッセージを出力する場合は、「'(シングルクォート)」で囲み、引数を一つにまとめて渡すことが推奨されます。

trueコマンド「true」とfalseコマンド「false」

「true」コマンドは、常に終了コードを0で返すコマンドです。「false」コマンドは、常に終了コードを「1」で返すコマンドです。両者とも引数はすべて無視されます。

組み込みコマンドにはオプションがありませんが、外部コマンド版には--help(ヘルプの表示)と、--version(コマンドのバージョンの表示)が存在します。

「true」コマンド「false」コマンドを使用して「0」「1」が返るか確認してみましょう。

# trueコマンドの実行
$ true
$ echo $?

# falseコマンドの実行
$ false
$ echo $?

コンソールへ直接手入力して、終了ステータスコードを確認します。

「true」コマンドや「false」コマンドの直後に特殊パラメータ$?を参照すると、それぞれ「0」や「1」の値が表示されることがわかります。

killコマンド「kill」

引数として持たせたプロセスにシグナルを送信するコマンドです。シグナルが正常に送信できた場合、または「kill -l」を実行した場合は、終了ステータスは「0」になります。

デフォルトでは終了のシグナルを送るため、ハングアップしたプロセスを強制終了させるなどしたいときに使う事が多いコマンドです。

コマンドの書式

kill [オプション] プロセスID

コマンドの主なオプション

  • -s:プロセスに送るシグナル名または番号 -シグナル名、-番号でも指定可能
  • -l:指定可能なシグナルの一覧を表示する

組み込みコマンドでは-s オプションで送るシグナル番号の指定、-l オプションでシグナル名の指定、プロセスIDの指定以外に、ジョブIDでの指定が可能ですが、外部コマンド版ではこれらの機能はついていません。

# シグナルの一覧を表示します。
$ kill -l

上記のコマンドを実行すると、指定可能なシグナルの一覧が表示されます。具体的な表示内容はOSにより異なります。

シグナル番号一覧

「-s」オプション指定時のシグナル番号については、下記参照!
通常は下記テーブル中のシグナル番号、シグナル名から選択されることが多いです。

番号シグナル名意味主な用途
1SIGHUPハングアップ設定ファイルの再読み込みなど
2SIGINT割り込み (Ctrl+C)プロセスの中断
3SIGQUIT終了 (ダンプ生成)プロセス終了とコアダンプ生成
9SIGKILL強制終了プロセスの即時終了
11SIGSEGVセグメンテーション違反メモリアクセス違反
13SIGPIPEパイプ切断書き込み先が閉じられた場合
15SIGTERM終了要求優雅なプロセス終了
18SIGCONT停止から再開一時停止プロセスの再開
19SIGSTOPプロセス停止強制停止 (再開はSIGCONT)
20SIGTSTP端末停止 (Ctrl+Z)一時停止
30,10,16SIGUSR1ユーザー定義シグナル1カスタム処理
31,12,17SIGUSR2ユーザー定義シグナル2カスタム処理
34SIGRTMINリアルタイムシグナル (最小値)リアルタイム処理向け

printfコマンド「printf」

入力されたデータを整形して表示する事が可能なコマンドです。エラーが発生しない限り終了ステータスは「0」になります。

一部のシェルの組み込みコマンドでのprintfコマンドでは\xHH(Hは数字)などの16進数表記を受け付け、対応した文字を出力しますが、外部コマンドにそのような機能はありません。

8進数の表記を用いるか、16進数の表記をしないようにしましょう。また、8進数での記述でも、一部のシェルで異なるコードを指定したと認識する場合があります。

とはぁ、この辺はごちゃごちゃ言っても理解はむずかしいと思います。主な使用用途は、文字列の整形と思っていただいて結構です。

「慣れるより慣れろ!」です。直接触ってみましょう。

echo "aaaa" "bbbb" "cccc"

上記コマンドの出力結果は、「aaaa bbbb cccc」となります。目的としては、2つ目の文字列「bbbb」を「[ ](鉤括弧)」等を使って”[ bbbb ]”(こんな感じ)で整形したい場合があります

# 文字列を「[ ](鉤括弧)」を使って整形する
$ printf "%s [%-4s] %s\n" "aaaa" "bbbb" "cccc" 

見た方が速いと思います。実行してみましょう。

見事に整形できました!

つまり「printf」を使ってその後の引数で整形ロジックを組み立てているのです。

「printf」コマンドの直後の暗号みたいな「%s」は文字列を表します。一番初めに記述されているので"aaaa"、"bbbb"、"cccc"のうち、一番目の文字列"aaaa"に該当します。

次の呪文”[%-4s]”は、2番目に記述されているので、2番目の文字列"bbbb"を対象としています。つまり2番目の文字列(4文字)を「[ ](鉤括弧)」で括れという呪文です。

もっと詳しく

「%」の後に表示する桁数を指定できます。桁数を指定した場合、通常は右寄せになります。左寄せにしたい場合はマイナスを付けて指定します。今回は左寄せを行いたいので"%-4"と指定しています。つまり、2番目の文字列"bbbb"を左から遡って4文字後に"]"で括る様に指示しているのです。

最後の"%s\n"は、残りの文字列を出力して「改行」を指示している呪文です。

ちなみに”%-4”を”%-5”へ変更してみます。(2番目の文字列"bbbb"を左から遡って文字後に"]"で括る様に指示)

結果は"[bbbb ]"となり、文字列の右側1文字分空白になって不格好ですが、指示通りに整形されているのが分かります。

使う機会はあまり多くはありませんが、「ここぞ!」と言う時に、非常に役に立つので覚えておいて損はありません。

pwdコマンド「pwd」

現在の作業ディレクトリ(カレントディレクトリ)を表示するコマンドです。コマンドの実行がエラーにならない限り終了ステータスは「0」になります。

シェルの組み込みコマンドの場合は、-Lオプションか、-Pオプションが指定できますが外部コマンドにはそのような機能はありません。

コマンドの主なオプション

  • -P:物理的なディレクトリの位置を表示する。元のファイルまで表示しない
  • -L:変数${PWD}の値を表示。シンボリックリンクの元のファイルまで表示する

testコマンド「test」

引数として持たせた条件式の真偽を判定するコマンドです。主に条件に合うファイルの有無の判定などに使われますが、外部コマンドの場合はサポートされている-nt(newer than)、-ot(older than)オプションは、shの組み込みコマンドではサポートされていません。

条件式の評価結果が真ならば終了ステータスは「0」に、偽ならば終了ステータスは「1」になります。

コマンドの書式

test 条件式(1)
[ 条件式 ]2)

※1:testコマンドを使用して記述した場合の表記。

# testコマンドでの判定表記(※1)
$ a="文字列1"
$ b="文字列2"
$ echo $( test "$a" =" $b" && echo "同じ文字列ですね" || echo "違う文字列ですね" )

※2:「[ ]」を使用して記述した場合の表記。

# [ ] での判定表記(2)
$ a="文字列1"
$ b="文字列2"
$ echo $( [ "$a" = "$b" ] && echo "同じ文字列ですね" || echo "違う文字列ですね" )

「test」コマンドは、「test」という名前でも「[ ]」という名前でも起動でき、「 [ 」で起動した場合は最後の引数を「 ] 」にするため、そのコマンドラインは「[ ]」という角カッコで囲んだ状態になります。

拡張組み込みコマンドの動作例

builtinコマンド「builtin」

「builtin」コマンドは、bashなどLinuxのシェルが内部に備えているビルトインコマンド(内部コマンド、シェルコマンド)を実行するコマンドです。

実行されたコマンドの終了ステータスが、builtinコマンドの終了ステータスになります。

Linuxのコマンドには、「ll」のような、コマンドの実態「ls -l」と、その実態に文字づくコマンドのエイリアス「ll」が用意されています。

「ll」コマンドは、「ls -l」コマンドのエイリアスです。「type」コマンドで確認してみます。

Unix系のOSでは、常にエイリアスを優先させてコマンドを起動します。

「builtin」コマンドは、「builtin」を付けて実行することで、必ずビルトインコマンドを実行できるようになります。

letコマンド「let」

let 変数=式の形で、式の計算結果を変数にセットするコマンドです。インクリメントやビットシフト、ビット演算など多くの算術演算を行えます。

最後の算術式の評価結果が真(「0」以外)なら、算術式の評価の終了ステータスは真(0)に、最後の算術式の評価結果が偽(0)なら、算術式の評価の終了ステータスは偽(1)になります。

シェルの種類によって特殊な記述が可能なコマンドですが、letはシェル組み込みコマンドのみで、外部コマンドは存在しません。

$ let result=' 100*50 '

「let」コマンドでは、計算式をまとめて一つの引数にする必要があります。また不用意に空白を開けると、区切りとして認識されてしまうため、計算式は「'(シングルクォート)」で囲みます。

localコマンド「local」

シェル関数の中でローカル変数を宣言するために用いるコマンドです。このコマンドを用いると、変数の値が関数の中でのみ保持されます。

「local」コマンドは、関数の外で宣言された時を除いて常に終了コードが0で返ってくるため、スクリプトの記述によっては意図したエラーハンドリングが出来ない事があります。

localはシェル組み込みコマンドのみで、外部コマンドは存在しません。また、シェルの中でもlocalコマンドが存在しないものがあります。

組み込みコマンド利用時の注意点

シェルの組み込みコマンドは便利で効率的ですが、適切に使用するためにはいくつかの注意点を理解しておく必要があります。このセクションでは、外部コマンドとの併用時の注意点や、特定のシェル環境での挙動の違い、さらに組み込みコマンドをデバッグするための手法を解説します。

外部コマンドとの併用時の注意点

組み込みコマンドと外部コマンドを併用する場合、それぞれの特性を理解し、適切に使い分けることが重要です。

併用時の注意点

  1. コマンド名の競合
    外部コマンドと同じ名前の組み込みコマンドが存在する場合、シェルは通常、組み込みコマンドを優先して実行します。このため、外部コマンドを実行したい場合はフルパスを指定する必要があります
    /bin/echo "This is an external command"
  2. パフォーマンスの考慮
    簡単な操作では組み込みコマンドを使用する方が効率的ですが、複雑な処理では外部コマンドの方が豊富な機能を提供する場合があります。用途に応じて適切な選択を行いましょう。
  3. エラーハンドリングの違い
    組み込みコマンドはシェルの設定に依存して動作するため、エラー時の挙動が外部コマンドと異なることがあります。set -eやtrapを活用してエラー処理を明確にすることが推奨されます。

特定のシェル環境での挙動の違い

組み込みコマンドの動作は、使用しているシェル(Bash、Zsh、Dashなど)によって異なる場合があります。

挙動の違い

  1. コマンドの有無
    一部の組み込みコマンドは、特定のシェルでのみ利用可能です。例えば、pushdやpopdはBashやZshで利用できますが、Dashでは利用できません。
  2. オプションや機能の違い
    同じ名前の組み込みコマンドでも、シェルごとにサポートされるオプションや挙動が異なる場合があります。使用するシェルのマニュアル(man bashやman zshなど)を確認する習慣をつけましょう。
  3. デフォルト設定の影響
    シェルごとに異なるデフォルト設定が存在し、組み込みコマンドの挙動に影響を及ぼすことがあります。例えば、setコマンドのデフォルトオプションはシェル間で異なります。

まとめ

シェル組み込みコマンドは、システム操作やスクリプト作成の効率を大幅に向上させる強力なツールです。本記事では、組み込みコマンドの基礎知識から応用テクニック、利用時の注意点までを解説しました。

組み込みコマンドは、外部コマンドと比較して高速に動作し、シンプルな構文を持つため、初心者から上級者まで幅広く活用できます。一方で、シェル環境による挙動の違いやエラー処理の留意点があるため、これらを理解し適切に対応することが重要です。

組み込みコマンドを駆使して効率的なシェルスクリプトを作成しよう

組み込みコマンドを効果的に使用することで、以下のようなメリットを得られます:

組み込みコマンドのメリット

  • スクリプトの実行速度が向上し、システムリソースの節約が可能。
  • コードの可読性と保守性が向上し、スクリプトの再利用が容易。
  • 外部コマンドに依存しないことで、スクリプトの移植性が高まる。

特に、cd や echo のような基本的なコマンドから、trap や eval のような応用的なコマンドまでを組み合わせることで、柔軟で効率的なスクリプトを構築できます。

最後に、組み込みコマンドを活用する際には、シェルのマニュアルやデバッグ手法を活用し、実際のプロジェクトに応じた最適な方法を模索してください。日々の作業効率化やスクリプトの品質向上を目指し、組み込みコマンドを最大限に活用してみましょう。


この記事を読んだら、次は 「【Shellの基礎知識】クォートとコマンド置換の違いと使い分け」を読むのがおすすめです!

よく読まれている記事

1

IT入門シリーズ 🟢 STEP 1: ITの基礎を知る(ITとは何か?) 📌 IT初心者が最初に学ぶべき基本知識。ITの概念、ネットワーク、OS、クラウドの仕組みを学ぶ ...

2

「私たちが日々利用しているスマートフォンやインターネット、そしてスーパーコンピュータやクラウドサービス――これらの多くがLinuxの力で動いていることをご存じですか?無料で使えるだけでなく、高い柔軟性 ...

3

この記事は、Linuxについて勉強している初心者の方向けに「Shellスクリプト」について解説します。最後まで読んで頂けましたら、Shellスクリプトはどのような役割を担っているのか?を理解出来るよう ...

-Shellの基礎知識(基礎編)
-, ,