入出力

キーボードからの入力

入力には「read」コマンドを使用します。

read 変数

早速スクリプトを書いてみましょう。

#!/bin/bash

## キーボードからの入力を読み取る
echo "キーボードからの入力を読み取る"

read V_KEY


echo "読み取った文字を画面出力する"
echo "${V_KEY}"

exit 0

実行するとこうなります。

# ./read01.sh 
キーボードからの入力を読み取る
テスト入力
読み取った文字を画面出力する
テスト入力
# 

キーボードからの入力を変数「V_KEY」に格納し、その変数を出力しているだけの簡単なスクリプトですが、これが基本となります。

readコマンドは基本的に行単位のデータを扱いますので、改行されるまで入力を待ち続けます。そしてスクリプトの処理も入力を待つことによって、そこで止まります。
改行が入力される事で、1行が終わりますので、処理が再開され、最終的に変数に格納された入力が画面出力されているという訳です。

 

ファイルからの入力

ファイルの内容を読み取る方法にも「read」コマンドが使われます。
先述の通り、「read」コマンドは行単位のデータを扱いますので、ファイルから1行ずつ読み込み、それを繰り返すループ処理の形になります。

while read 変数
do
    ファイルからの入力に対する処理
done <ファイル名

というのが一般的な形です。
この処理の形は配列や変数の処理説明で既に出てきてるので見たことがあると思います。

早速つかってみましょう。
まず処理対象のファイルを作っておきます。

・test.txt

aaa
bbb
ccc

このテキストファイルを以下のスクリプトで処理します。

#!/bin/bash

while read V_LINE
do
    case ${V_LINE} in
        aaa)
            echo "文字列はaaaです"
            ;;
        bbb)
            echo "文字列はbbbです"
            ;;
        ccc)
            echo "文字列はcccです"
            ;;
        *)
            echo "謎の文字列です"
            ;;
    esac
done < test.txt

exit 0

実行するとこうなります。

# ./read02.sh 
文字列はaaaです
文字列はbbbです
文字列はcccです
# 

スクリプトとしてはちょっと冗長ですが、処理の流れはわかりましたでしょうか。
よく使う処理ですので頭に叩き込んでおきましょう。

 

 

画面への出力

bashスクリプトでは今まで使ってきた「echo」コマンド以外にも多数の
出力方法があります。ご存知の方も多いでしょうが、ここでいくつか取り上げて
説明したいと思います。

 

echoコマンド

まずは「echo」コマンドからです。
今までの例文でも扱ってきましたので、もう皆さんご存知でしょう。

echo "出力文字列"

変数に格納する値を出力する時はこんな感じです。

echo "${変数名}"

変数に入った文字列と連結して出力する場合は以下の様になります。

echo "出力文字列${変数名}"

上記のようにすると、出力文字列の後ろに変数の値が付加されて出力されます。

配列の例題で扱いましたが、出力文字列の後ろの改行コードを出力しないオプションもあります。

echo -n "出力文字列"

上記の「-n」オプションをつける事により、出力文字列の後ろには改行が入りません。
以下の様に実行すると、

echo -n "今日は"
echo "晴れです"

このように出力されます。

今日は晴れです

これ以外にも「echo」コマンドには「-e」オプションが存在します。
「-e」オプションを使用することにより、文字列内に埋め込まれたエスケープシーケンスを認識するようになります。
以下のエスケープシーケンスを使用できます。

\a     警告(ベル)
\b     バックスペース
\c     行末に改行を付けない
\e     エスケープ文字
\f     フォームフィード文字
\n     改行
\r     復帰文字
\t     水平タブ
\v     垂直タブ
\\     バックスラッシュ
\nnn   ASCII コードの 8 進値が nnn である文字 (1 文字につき数字 3 桁)。
\xnnn  ASCII コードの 16 進値が nnn である文字 (1 文字につき数字 3 桁)。

ただ、画面出力の為だけでエスケープシーケンスを使用するのは余りお勧めできません。
毎度の言っている事ですが、「可読性が著しく落ちる」からです。
ですが、凝った出力をする際はどうしてもエスケープシーケンスを使う事になります。
どうせエスケープシーケンスを使用するのであれば、後述する「printf」や「ヒアドキュメント」の使用をお勧めします。
こちらの方が「echo」コマンドよりも高機能です。

 

printfコマンド

「printf」コマンドも出力を行いますが、「echo」コマンドよりも柔軟性が高いです。
「echo」コマンドと使い方は若干違っていて、まずフォーマット文字列を定義し、そこに文字列を埋め込む形になります。
フォーマットが定まっているので、定型的な出力などで威力を発揮します。
基本的な使い方はC言語の「printf」関数と一緒です。
ここではその中でも簡単な物を扱います。

まず、「printf」コマンドの書式です。

printf "フォーマット文字列" 埋め込み文字列...

上記書式に従って書くのですが、「フォーマット文字列」と埋め込み文字列の対応がありますので、もう少し細かく書いてみます。
たとえば、Linuxのシスログの日付フォーマットを考えてみましょう。
一般的には以下の様になってるはずです。

 月 日 時:分:秒
Mar 31 00:00:00

月は3文字、日は2文字で一桁の日は十の位はスペース、時分秒もそれぞれ2文字で一桁の場合は十の位が0、といった感じです。
このフォーマットを「printf」コマンドのフォーマット文字列だとこう表します。

%3s %2d %02d:%02d:%02d

なんだこりゃ、と思った方も多いかもしれません。
ですが、覚えるとそんなに難しくないので簡単な物は覚えましょう。

まず、「月」部分の3文字を表す部分が「%3s」になります。
「日」部分の2文字を表す部分が「%2d」になります。
「時」「分」「秒」を表す部分がそれぞれ「%02d」になります。

最初は全て共通で「%」になっています。ここからパターンが始まります。
ポイントは最後の文字です。これがどの様な文字なのかを表しています。
この文字を「変換指定子」と言いますが、覚えていても役立たないので覚えなくていいです。
覚えなければいけないのは最低限、「文字列」を表す「s」と「整数」を表す「d」です。
最低限、この2つは覚えておきましょう。その他にもさまざまな物がありますが、これは必要な時に
調べれば問題ないと思います。bashスクリプトレベルでは早々使うものではありません。

次に最後の文字(sやd」の一つ前の文字に注目です。これは最大文字数を表してると考えてください。
「%3s」は「3文字の文字列」を表しており、「%2d」は「2文字の整数」を表しています。
でも、日は1桁の時と2桁の時があるはずです。
「printf」では右詰にして表示を行うので一桁の場合は右側に寄ります。
今回のシスログの出力フォーマットの場合、この動きが正しいのでこのままでOKです。

じゃあ、もし違うシチュエーションで、一桁の時は左詰にするフォーマットの場合はどう書くのか?
と言う事になりますが、それを最大文字数の前につける「フラグ文字」で制御します。

時刻の部分は「%02d」というパターンになっていますが、この「0」というのがそれに当たります。
「0」というフラグ文字は「値を0で埋める」という意味があるフラグ文字になります。
ですので、1桁の数字が与えられても十の位は0で埋められる訳です。
ちなみに左詰にするには「-」をつけます。

上記のパターン文字列を使用して「printf」コマンドで出力するには以下になります。

・3月1日 12:09:08の場合

printf "%3s %2d %02d:%02d:%02d\n" Mar 1 12 9 8

上述したパターン文字列を「"」で括ってあげます。ここでポイントなのは、最後に改行コードである「\n」を付加しているところです。
「printf」コマンドを使う上で一番忘れがちなのは「デフォルトでは改行されない」と言う事です。
普通に行出力を行うのであれば、忘れない様にしましょう。

埋め込み文字列の方はと言うと、パターン文字列の左から順に並べただけです。もちろん変数を使う事も可能です。

上記を出力すると以下の様になります。

Mar  1 12:09:08

そして2桁の日はこうなります。

・3月10日 09:30:20の場合

printf "%3s %2d %02d:%02d:%02d\n" Mar 10 9 30 20

 

Mar 10 09:30:20

この様な基本的な出力以外にも、もっとたくさんの表現ができる「printf」コマンドですが、
それは扱いに慣れてから覚えても遅くないでしょう。まずは上記の基本的な物だけでも覚えてください。

 

ヒアドキュメント

「echo」コマンドや「printf」コマンドを使って出力できるのは、基本的には1行だけです。
複数行の出力を行う場合、複数回の出力コマンドを羅列する事になります。
ただ、これも「可読性」が失われる事が多いです。何より美しくありません。
複数行の出力を行う場合は、ヒアドキュメントを使用しましょう。

ヒアドキュメントを使った文字列出力は以下の様にします。

cat <<終了文字列
出力する文字列
     ・
     ・
     ・
終了文字列

上記の様にスクリプト内で記述する事により「出力する文字列」から「終了文字列」の直上の行までを
catコマンドに渡します。

ヒアドキュメントはとても便利なのでいろいろな場面で使われますが、上記の書き方だと可読性が落ちます。
出力する文字列部分はスペースやタブをそのまま出力してしまう為、せっかく字下げをして綺麗に書いたコードもヒアドキュメントを使った瞬間に左に寄ってしまう事になってしまうからです。

一応、この字下げ問題を解決する事ができます。
ヒアドキュメント記号「<<」を「<<-」に変えるだけです。これだけで行頭のタブを無視してくれます。
ですがお勧めはしません。行頭タブは削除してくれるのですが、スペースは削除してくれないからです。
もし、行頭タブがスペースだった場合、思い通りの出力を得る事ができません。
エディタの設定によってはタブを自動的にスペース変換してくれる物もありますし、パッと見ただけではタブかスペースかの判断をつける事はできません。

結論として、「<<-」を使ってタブ文字を行頭に使うぐらいであれば普通に「<<」を使って出した方が間違いが少なくて済みます。

可読性の部分は目を瞑りましょう。
多少の抵抗をするとすれば、メインのフローでは使用しないでヒアドキュメント出力の部分は関数化してしまうのも手でしょう。

メインフローは字下げを崩さないで済みます。

とりあえず使ってみましょう。
以下のスクリプトで試します。

#!/bin/bash

##ヒアドキュメントテスト

echo "ヒアドキュメントに「<<」を利用した場合(行頭タブ)"

cat <<_EOT_

        ぽっぽっぽ
        はとぽっぽ
        豆が欲しいかそらやるぞ
        みんなで仲良く食べに来い

_EOT_

echo "ヒアドキュメントに「<<-」を利用した場合(行頭タブ)"

cat <<-_EOT_

        ぽっぽっぽ
        はとぽっぽ
        豆が欲しいかそらやるぞ
        みんなで仲良く食べに来い

_EOT_

echo "ヒアドキュメントに「<<」を利用した場合(行頭スペース)"

cat <<_EOT_

        ぽっぽっぽ
        はとぽっぽ
        豆が欲しいかそらやるぞ
        みんなで仲良く食べに来い

_EOT_


echo "ヒアドキュメントに「<<-」を利用した場合(行頭スペース)"

cat <<-_EOT_

        ぽっぽっぽ
        はとぽっぽ
        豆が欲しいかそらやるぞ
        みんなで仲良く食べに来い

_EOT_

exit 0

実行結果は以下になります。

# ./heredoc01.sh 
ヒアドキュメントに「<<」を利用した場合(行頭タブ)

        ぽっぽっぽ
        はとぽっぽ
        豆が欲しいかそらやるぞ
        みんなで仲良く食べに来い

ヒアドキュメントに「<<-」を利用した場合(行頭タブ)

ぽっぽっぽ
はとぽっぽ
豆が欲しいかそらやるぞ
みんなで仲良く食べに来い

ヒアドキュメントに「<<」を利用した場合(行頭スペース)

        ぽっぽっぽ
        はとぽっぽ
        豆が欲しいかそらやるぞ
        みんなで仲良く食べに来い

ヒアドキュメントに「<<-」を利用した場合(行頭スペース)

        ぽっぽっぽ
        はとぽっぽ
        豆が欲しいかそらやるぞ
        みんなで仲良く食べに来い

# 

※エディタの都合上、行頭タブがスペースに変換されてます。
上記スクリプトを実行する際は適宜置き換えてください。

画面への出力方法は以上です。
いろんな方法がありますが、こんな感じで使い分けてみましょう。

・簡単な出力→「echo」コマンド
・ちょっと凝った出力→「printf」コマンド
・複数行出力→ヒアドキュメント

 

ファイルへの出力

基本的には画面への出力と一緒です。
出力先を標準出力である「画面」から「ファイル」へ切り替えるだけです。
切り替えには「リダイレクト」という機能を使います。
この「リダイレクト」という機能は、詳しく説明し始めると大変な事になるのでそれはまた別の機会として、ここでは簡単な物を紹介しておきます。

たったの2つです。

>   ファイルに出力(上書き)
>>  ファイルに出力(追加)

これだけです。
上の「>」は画面に出力されるものをファイルに出力します。
この時に出力先のファイルに何が書いてあろうと、一度クリアして先頭から書き始めます。
またファイルが存在しない場合はファイルを自動的に作成してくれます。

下の「>>」は画面に出力されるものをファイルに出力しますが、この時に出力先ファイルが存在するとそのファイルの一番最後の行から「追記」してくれます。
ファイルが無かった時の動作は「>」と一緒で自動的に作成してくれます。

最低限、この2つだけは覚えておきましょう。

では早速使ってみましょう。

#!/bin/bash

## リダイレクト処理

## 画面出力
echo "これは画面に出力されます"

## echoコマンドの出力
echo "これはechoコマンド出力です"

## printfコマンドの出力
printf "これは%sコマンド出力です\n" printf

## ヒアドキュメントの出力
cat <<_EOT_
これはヒアドキュメントでの出力1行目です
これはヒアドキュメントでの出力2行目です
これはヒアドキュメントでの出力3行目です
_EOT_


## ファイル出力
echo "これはtest01.txtに出力されます" > test01.txt
echo "これはtest01.txtに追記されます" >> test01.txt

echo "ところがtest01.txtへはこれが上書きされます" > test01.txt
echo "test01.txtへの追記です" >> test01.txt
echo "以下コマンドもtest01.txtへ追記されます" >> test01.txt

## echoコマンドの出力
echo "これはechoコマンド出力です" >> test01.txt

## printfコマンドの出力
printf "これは%sコマンド出力です\n" printf >> test01.txt

## ヒアドキュメントの出力
cat  test01.txt
これはヒアドキュメントでの出力1行目です
これはヒアドキュメントでの出力2行目です
これはヒアドキュメントでの出力3行目です
_EOT_

exit 0

実行するとこうなります

# ./redirect01.sh 
これは画面に出力されます
これはechoコマンド出力です
これはprintfコマンド出力です
これはヒアドキュメントでの出力1行目です
これはヒアドキュメントでの出力2行目です
これはヒアドキュメントでの出力3行目です
# cat test01.txt 
ところがtest01.txtへはこれが上書きされます
test01.txtへの追記です
以下コマンドもtest01.txtへ追記されます
これはechoコマンド出力です
これはprintfコマンド出力です
これはヒアドキュメントでの出力1行目です
これはヒアドキュメントでの出力2行目です
これはヒアドキュメントでの出力3行目です
# 

ポイントは掴めましたか?
このリダイレクト処理もよく使います。
コツだけは掴んでおきましょう。