Linux

シェルスクリプト (Bash) では組み込みコマンド set を活用しましょう

投稿日:2018年6月12日 更新日:

Linux や MacOS、Windows の WSL でシェルスクリプト(Bash)を書く場合は、組み込みコマンド set を活用しましょう。より完成度の高い処理を書くことができます。

スポンサードリンク

1. シェルスクリプトとは?

コンピュータでは、ターミナル上でコマンドを実行していろいろな操作を行うことができますが、このコマンドを解釈して意図した処理を実行してくれているプログラムを シェル と言います。

Linux や MacOS などの UNIX系OS であれば、「Bourne Again Shell(Bash)」「KornShell(Ksh)」「C shell(Csh)」「Z shell(Zsh)」などがありますし、Windows には「PowerShell」があります。

Linux でのコマンド実行例
Linux でのコマンド実行例

この例で実行している figlet コマンドは、引数として与えた文字列をアスキーアートにして表示してくれるコマンドです(かなりマイナーな半分お遊び用コマンドです。詳細はこちらをご覧ください → FIGlet – hosted by PLiG)。

そして、複数(1つでもよいですが)のコマンドを記述して何かの処理をさせるプログラムのことを シェルスクリプト と言います。

例えば、hello.sh というファイルに以下の内容を記述します。

#!/usr/bin/bash

figlet Hello,

figlet World!

1行目に「bash」という文字がある通り、これは Bash というシェルを使ったシェルスクリプトになります。(この場合、システムに Bashがインストールされており、/usr/bin/bash というパスで bash プログラムが配置されている必要があります)

このファイルの中では、figletコマンドを2回実行しています。

このファイルを実行するには、chmodコマンドで実行権限を与えて、

$ chmod u+x hello.sh

以下のようにして実行します。

$ ./hello.sh

実際に実行した結果が以下になります。

シェルスクリプトを実行!!
シェルスクリプトを実行!!

figletコマンドが順番に実行されました。これは意図した通りの挙動です。

以上、シェルスクリプトがどういったものかについて説明しました。

このページでは、引き続き「Bash」について説明します。

※ 「シェルスクリプト」と書いただけでは、どのシェルに向けたスクリプトなのか分かりませんが、UNIX系OS でシェルスクリプトと言えば、Bash用のスクリプトと考えてよいです。

2. Bash のマニュアルと組み込みコマンド

Bash の詳細については、manコマンドで読むことができます(オンラインマニュアル)。

$ man bash
man コマンドについては、以下のページも参考にしてください。

Linux

【Linux】man コマンドの基本的な使い方

2018.06.08

このマニュアルは内容が多くて読むのが大変ですが、この内容のうち「Bashの組み込みコマンド (shell builtin commands)」の部分については、help という組み込みコマンドを使って読めるようになっています。こちらであれば、一度に表示される文章が限られていますので読むのがラクです。

例えば、help 自体の説明を見てみます。

$ help help

と入力して Enter キーを押して下さい。私の環境では、以下のように表示されました。

$ help help
help: help [-dms] [pattern ...]
    組み込みコマンドの情報を表示します。

    組み込みコマンドに関する簡潔な要約を表示します。もし PATTERN が
    指定された場合は、PATTERN に一致する全てのコマンドに対する詳細な
    ヘルプを表示します。それ以外はヘルプトピックを表示します。

    オプション:
      -d        各ヘルプトピックに対して短い説明を出力します
      -m        使用法を擬似的な man ページ形式で表示します
      -s        一致した各トピックに対して簡単な使用法のみを表示します
        PATTERN

    引数:
      PATTERN   ヘルプトピックを指定するパターン

    終了ステータス:
    PATTERN が見つからないか無効なオプションが与えられない限り成功を返します。

念の為説明しておきますと、help 組み込みコマンドが使えるのは、この時のシェルが Bash だからです。

今どのシェルの中にいるのかを知るには、次のコマンドを実行して下さい。

$ echo $SHELL
実行例

また、そのシステムにどのシェルがインストールされているかは、以下のコマンドで知ることができます。

$ cat /etc/shells
/bin/sh
/bin/bash
/sbin/nologin
/usr/bin/sh
/usr/bin/bash
/usr/sbin/nologin
/bin/tcsh
/bin/csh

例えば、ここに表示された /bin/csh にシェルを変更する場合は、以下のコマンドを実行してからログインし直して下さい。

$ chsh -s /bin/csh

bash の組み込みコマンドには、以下のようなコマンドがあります(主なものを抜粋しました)。

  • source
  • alias
  • cd
  • echo
  • exec
  • export
  • history
  • jobs
  • kill
  • pwd
  • set
  • test
  • type
  • umask

この中の set は、Bash によるシェルスクリプトにおいて、挙動を変更することができる大変重要な組み込みコマンドです。

以下、この set について説明します。

3. Bash の組み込みコマンド set

オプションを指定して set を実行することにより、シェルスクリプトの挙動を変更することができます。

先程説明した help を使って、set の説明を読んでみましょう。

$ help set

私の環境では以下のように表示されました。この部分は日本語化されていないのか、英語になっています。

$ help set
set: set [-abefhkmnptuvxBCHP] [-o option-name] [--] [arg ...]
    Set or unset values of shell options and positional parameters.

    Change the value of shell attributes and positional parameters, or
    display the names and values of shell variables.

    Options:
      -a  Mark variables which are modified or created for export.
      -b  Notify of job termination immediately.
      -e  Exit immediately if a command exits with a non-zero status.
      -f  Disable file name generation (globbing).
      -h  Remember the location of commands as they are looked up.
      -k  All assignment arguments are placed in the environment for a
          command, not just those that precede the command name.
      -m  Job control is enabled.
      -n  Read commands but do not execute them.
      -o option-name
          Set the variable corresponding to option-name:
              allexport    same as -a
              braceexpand  same as -B
              emacs        use an emacs-style line editing interface
              errexit      same as -e
              errtrace     same as -E
              functrace    same as -T
              hashall      same as -h
              histexpand   same as -H
              history      enable command history
              ignoreeof    the shell will not exit upon reading EOF
              interactive-comments
                           allow comments to appear in interactive commands
              keyword      same as -k
              monitor      same as -m
              noclobber    same as -C
              noexec       same as -n
              noglob       same as -f
              nolog        currently accepted but ignored
              notify       same as -b
              nounset      same as -u
              onecmd       same as -t
              physical     same as -P
              pipefail     the return value of a pipeline is the status of
                           the last command to exit with a non-zero status,
                           or zero if no command exited with a non-zero status
              posix        change the behavior of bash where the default
                           operation differs from the Posix standard to
                           match the standard
              privileged   same as -p
              verbose      same as -v
              vi           use a vi-style line editing interface
              xtrace       same as -x
      -p  Turned on whenever the real and effective user ids do not match.
          Disables processing of the $ENV file and importing of shell
          functions.  Turning this option off causes the effective uid and
          gid to be set to the real uid and gid.
      -t  Exit after reading and executing one command.
      -u  Treat unset variables as an error when substituting.
      -v  Print shell input lines as they are read.
      -x  Print commands and their arguments as they are executed.
      -B  the shell will perform brace expansion
      -C  If set, disallow existing regular files to be overwritten
          by redirection of output.
      -E  If set, the ERR trap is inherited by shell functions.
      -H  Enable ! style history substitution.  This flag is on
          by default when the shell is interactive.
      -P  If set, do not follow symbolic links when executing commands
          such as cd which change the current directory.
      -T  If set, the DEBUG trap is inherited by shell functions.
      --  Assign any remaining arguments to the positional parameters.
          If there are no remaining arguments, the positional parameters
          are unset.
      -   Assign any remaining arguments to the positional parameters.
          The -x and -v options are turned off.

    Using + rather than - causes these flags to be turned off.  The
    flags can also be used upon invocation of the shell.  The current
    set of flags may be found in $-.  The remaining n ARGs are positional
    parameters and are assigned, in order, to $1, $2, .. $n.  If no
    ARGs are given, all shell variables are printed.

    Exit Status:
    Returns success unless an invalid option is given.

8行目くらいに「Options」とあり、その下に「-a」とか「-b」とか書いてあります。これがset のオプションです。この1つ1つのオプションによって、Bash によるシェルスクリプトの挙動を変更することができるのです。例えば、-a だけ適用したいなら、set -a というコマンドを実行すればよいですし、-a と -b の両方を適用したいのであれば、set -ab というコマンドを実行します。そうすると、それ以降そのオプションの効果が有効になった状態になります。

オプションには名前が付いています。

例えば、上の中に以下の記述があります。

allexport    same as -a

これは、-a オプションには「allexport」というオプション名がついていることを表します。

また、この1つ1つオプションのことを「フラグ」とも呼び、有効になっていることを「オン」になっていると表現します。

デフォルト状態で設定されているオプション(オンになっているフラグ)

では、set を使わず、デフォルト状態でどのようなフラグがオンになっている見てみましょう。

それには、-oオプションを使うか、$- を使います。

まず、-oオプションを試すために、default-set-options.sh という名前のファイルに、以下を記述して実行してみましょう。

#!/usr/bin/bash

set -o

ファイルに実行権限を与えて、実行します。

$ chmod u+x default-set-options.sh
$ ./default-set-options.sh

以下のような出力が得られました。オプション名に対して、on もしくは off が表示されています。分かりやすいです。

allexport       off
braceexpand     on
emacs           off
errexit         off
errtrace        off
functrace       off
hashall         on
histexpand      off
history         off
ignoreeof       off
interactive-comments    on
keyword         off
monitor         off
noclobber       off
noexec          off
noglob          off
nolog           off
notify          off
nounset         off
onecmd          off
physical        off
pipefail        off
posix           off
privileged      off
verbose         off
vi              off
xtrace          off

on になっているのは、以下の 3つです。

  • braceexpand
  • hashall
  • interactive-comments

次に、$- を試してみます。この記号は、現在オンになっているフラグを表示することができます。
では、default-set-options2.sh という名前のファイルに、以下を記述して実行してみましょう。

#!/usr/bin/bash

echo $-

こちらも同様に、ファイルに実行権限を与えて実行します。

$ chmod u+x default-set-options2.sh
$ ./default-set-options2.sh

結果はこうなりました。

hB

こちらは、オプション名ではなく -h-B といった表現に使うアルファベットが示されます。

先程、help set で見たオプションの説明の中に、

  • “braceexpand same as -B”(braceexpand は -B と同じ)
  • “hashall same as -h”(hashall は -h と同じ)

とあったことから分かるように、-B と -h についてはどちらの結果でも有効になっていることが分かります。ただ、「interactive-comments」については、後者の結果には表示されていませんね。対応するオプションがありませんでしたので、フラグとして表示されないのかもしれません(コマンドラインで set -o を実行すると、”i” という文字が表示されるのですが、これはこのオプションのことを指しているという情報もあります。ここではこれ以上深い入りするのはやめておきます)。

では、どちらの結果でも「オン」になっていた 2つのオプションの説明を、set のマニュアルから探してみましょう。

-B

-B  the shell will perform brace expansion

-B オプションをつけると、シェルは中括弧の展開を実行すると書いてあります。

これは例えば、

$ ls {a,b}.txt

というコマンドを実行した場合、{a,b} の部分が ab に展開されて、結果的に、a.txtb.txt の2つが ls コマンドの引数になるという機能のことを指しています。

これは普段よく使う機能なので、デフォルトでオンになっているのだと思います。実際よく使います。

-h

-h  Remember the location of commands as they are looked up.

Bashが独自に持っている「コマンドのパス記憶機能」を hash と言いますが、この機能を有効にします。この機能を使うことで、「よく使うコマンドのパスを Bashのレベルでキャッシュするので、動作が速くなる」とか、「既存のコマンドと同名で別のコマンドを一時的に使うことができる」などのメリットがあります。とはいっても、この機能を意識して使う機会はあまりないかもしれません。

今、それぞれのオプションについて少し具体的に説明しましたが、残念ながら help set で表示される説明文だけでは、ここまで知ることはできません。なので、詳細はインターネットや本で補う必要があります。とはいえ、そのための概要や手掛かりはつかめると思います。

以上で、デフォルトで有効になっているオプションについては分かりました。

ちなみに、「-」の代わりに「+」を使うと、そのフラグをオフにすることができます。

4. その他のオプションを試してみる

その他のオプションで、よく使うものを紹介します。

これらのオプションを使うことで、シェルスクリプトの質が上がります。

errexit (-e)

オンラインマニュアルにはこう説明されています。

-e  Exit immediately if a command exits with a non-zero status.
日本語訳: ゼロでないステータスでコマンドが終了したら、即座に終了する。

最初の方で、シェルスクリプトは複数のコマンドを並べたものであると書きましたが、このフラグをオンにすると、途中のコマンドでエラーが起きた場合に、そこで処理を終了させることができます。逆に言うと、デフォルトの状態だと、途中でエラーが起きてもそのまま次のコマンドに進んでしまうのです。これは便利なので、割とよく使います。

例えば、以下のシェルスクリプトを実行すると、ls コマンドでエラーが起きますが、最後のコマンドまで実行されます。

#!/usr/bin/bash

#set -e

echo "Start"

# 存在しないファイル名に対して ls コマンドを実行する → エラーになる
ls aabbccdd

echo "End"

しかし、set -e の先頭のコメントを外して(頭の # を削除します)実行すると、最後の echo は実行されません。これは、errexit フラグがオンになっているからです。

nounset (-u)

-u  Treat unset variables as an error when substituting.
日本語訳:置換処理においてセットされていない変数があればエラーとする。

セットされてない変数を使うとエラーにしてくれます。

この手の機能は、バグの少ないプログラムを書くために、いろいろなプログラミング言語で用意されています。

例えば、以下のシェルスクリプトを実行すると、何も表示されずに終了しますが、set -u のコメントを外してから実行すると、「FOOBAR: 未割り当ての変数です」といったエラーを出力してくれます。

#!/usr/bin/bash

#set -u

echo $FOOBAR

補足

ある変数が定義されているかどうか判定する場合は、-v オプションを使います。

FOOという変数が定義されているか判定するスクリプトの例

if [ -v FOO ]; then
  echo "FOO is defined";
else
  echo "FOO is NOT defined";
fi

xtrace (-x)

-x  Print commands and their arguments as they are executed.
日本語訳: コマンドを実行した時に、そのコマンドと引数を表示する。

これは、シェルスクリプトをデバッグしたい場合、つまりどこでエラーが起きているかをチェックしたい場合などに使います。

例えば、以下のシェルスクリプトをそのまま実行すると、echo に指定された文字列が出力されるだけです。

#!/usr/bin/bash

#set -x

echo "Start"

echo "Now running!"

echo "End"

しかし、set -x のコメントを外してから実行すると、実行した echo の部分まで出力してくれます。

こんな感じです。

$ ./xtrace.sh
+ echo Start
Start
+ echo 'Now running!'
Now running!
+ echo End
End

5. おわりに

コマンドを複数並べれば済む処理をしたい場合、シェルスクリプトとしてファイルに保存しておけば、後から同じ処理をする時にラクですし、記録にも残ります。

そうした時に、set を使うとより安全で確実なシェルスクリプトを書くことができます。

シェルスクリプトを書く際には、是非とも使っておきたい機能です。

📂-Linux

執筆者:labo


comment

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

関連記事

Linux

【Linux】Info マニュアルの基礎知識と、閲覧に便利な pinfoコマンド

目次1. オンラインマニュアル (manコマンド)と Infoマニュアル (infoコマンド)2. Info マニュアルの基礎知識Info マニュアルのデータpinfo コマンドウェブ版もあります3. …

Linux

【Linux】man コマンドの基本的な使い方

Linux をコマンドラインで使う場合、man コマンドでマニュアルを読むことができます(man は manual の略です)。 このページでは、この manコマンドの簡単な使い方について説明します。 …

Linux

パターンにマッチした行を表示する grep コマンド

目次1. はじめに2. 主な使い方3. その他のオプション4. 正規表現について 1. はじめに grep は、対象ファイルの中で、指定した文字列パターンにマッチした行を表示するコマンドです。 ※ 本 …

Let's Encrypt

Let’s Encrypt でワイルドカードを使う

手持ちの CentOS 7 に入れてある certbot パッケージが バージョン 0.22.0 になり、Let’s Encrypt のワイルドカードに対応しましたので試してみました。 目 …

Linux

ssh-agent の代わりに gpg-agent を使う手順

ssh-agent の代わりに gpg-agent を使う手順について説明します。