ウェブサイト検索

Bashシェルでselect文を使って選択メニューを作成する方法


私たちは、退屈で繰り返しの作業を自動化するために非常に頻繁に bash スクリプトを使用します。時々、スクリプト内でユーザーに対して、1つまたは複数の選択肢をインタラクティブに行うよう求める必要があります。このチュートリアルでは、Bash シェルの select 文を使用して、そのような操作を非常に少ないコード行で行う方法を見ていきます。

このチュートリアルでは、あなたは学びます:

  • Bashのselect文の使い方
  • 選択メニュープロンプトをカスタマイズする方法

Bashシェルでselect文を使って選択メニューを作成する方法

ソフトウェアの要件と使用される規約

SELECT文

スクリプティングは、システム管理者が持つべき最も一般的なスキルの一つであり、反復作業を自動化し、エラーの発生を減らすために必要です。それほど複雑でないケースでは、シェルはその作業に最適なツールです。Bash(ボーン再生シェル)は、GNU/Linuxの世界で最も使用されるシェルです。このシェルは非常に便利なselectステートメントを提供します。その目的は次のように説明されます:

リストから単語を選択し、コマンドを実行します

説明はかなり自明です。このステートメントは、ユーザーからの入力をインタラクティブに受け取ることを可能にし、選択肢の番号付きリストを生成し、ユーザーにその中から一つを選ぶよう促します。使い方を見てみましょう:

select NAME [in WORDS ... ;] do COMMANDS; done

構文はBashの従来のfor loopのものと非常に似ていますが、selectキーワードの使用を除きます。しかし、select文は異なる方法で機能します。例を見てみましょう。ユーザーにディレクトリに含まれるファイルの1つを選択してもらうとします。以下のように書くことができます:

files="$(ls -A .)"
select filename in ${files}; do echo "${file}"; done
1) file1
2) file2
3) file3
#?

私たちが何をしたかを説明しましょう。まず最初に、ls コマンドを使用して、すべてのファイルのリストを取得しました。この場合、カレントワーキングディレクトリを指定し、-A オプション(--almost-all の短縮形)を指定して、返されるリストから暗黙的な . と .. を除外しました。そして、このリストを files 変数に割り当てました。

次に、select文を使用することにしました。上記は非常に基本的な例です:私たちはfile変数の展開結果を選択肢として提供しました。$ {files}の展開を意図的に引用符で囲まないようにしたことに注意してください。これはワード分割を得るためです:展開が二重引用符で囲まれると、ワード分割が抑制されるため、空白は単語の区切りとして使用されず、展開の結果は一つのまとまりとして扱われます。これはこの場合に望んでいることではありません:生成されたメニューで各単語を選択肢として使用する必要があります。

変数の展開から得られた単語は、stderr(標準エラー)に出力されます。それぞれには番号が付けられており、ユーザーがそれを参照するために使用します。すべての要素が表示された後、ユーザーは選択を入力するように促されます。表示されるのはPS3のプロンプトで、デフォルトでは#?に設定されています。

ユーザーが選択を行うとどうなりますか?見てみましょう:

files="$(ls -A .)"
select filename in ${files}; do echo "You selected ${filename}"; done
1) file1
2) file2
3) file3
#? 1
You selected file1
#?

私たちが入力した選択肢、1は、stdin(標準入力)から読み取られ、入力された数字が利用可能なものの中にあれば、対応する単語(例では「file1」)がfilename変数に割り当てられます。私たちの例では、選択が行われた後に実行されるコマンドとしてecho "${filename}"を指定しました。その結果、選ばれた単語が画面に印刷されます。私たちが選択を指定するために入力する番号も変数REPLYに保存されます。

上の例で奇妙なことが起こったことに気づくかもしれません:選択を行った後、コマンドの実行後に再度選択を促されました。なぜでしょうか?これは、break コマンドを提供しなかったからです。この問題は簡単に修正できます:

files="$(ls -A .)"
select filename in ${files}; do echo "You selected ${filename}"; break; done
1) file1
2) file2
3) file3
#? 1
You selected file1

もし私たちが選択を提供しなかったらどうなりますか?プロンプトは私たちが提供するまで単純に繰り返されます。

select filename in ${files}; do echo "You selected ${filename}"; break; done
1) file1
2) file2
3) file3
#?
1) file1
2) file2
3) file3
#?

もしリストにない番号を入力した場合はどうなりますか?この場合、私たちが使用する変数、私たちの例ではfilenameはnullに設定されます。

選択プロンプトの変更

既に見たように、select文の文脈でシェルによって使用されるプロンプトはPS3プロンプトであり、デフォルトでは#?という文字列の値を持っています。これはあまりユーザーフレンドリーではないので、より説明的なものに変更したいと思います。それをどうすれば良いでしょうか?とても簡単です:PS3パラメータの値を変更する必要があります。

PS3="Please enter your choice: "

次回 select 文を使用する際には、変化に気づくことになります:

select filename in ${files}; do echo "You selected ${filename} ${REPLY}"; break; done
1) file1
2) file2
3) file3
Enter your choice:

変数に加えられた変更は現在のシェルセクションにのみ適用され、もし私たちがその変数をexportすると、そのすべての子プロセスにも適用される。

export PS3="Please enter your choice: "

しかし、この変更は、.bashrcファイル内にPS3変数を設定するまで一時的なものです。PS3はBashで使用されるプロンプトの一つに過ぎません。この件についてもっと知りたい場合は、私たちのBashプロンプトに関する記事を確認してください。

結論

このチュートリアルでは、Bashのselectステートメントの使い方を学びました。どのように機能するか、そしてユーザーに対して選択を促すインタラクティブな選択メニューを作成する方法を見ました。最後に、シェルのPS3パラメータを変更することによって、選択メニューに表示されるプロンプトをカスタマイズする方法を見ました。

関連記事: