Quantcast
Channel: Bashタグが付けられた新着記事 - Qiita
Viewing all articles
Browse latest Browse all 2825

シェルプログラミング入門4

$
0
0

はじめに

この記事は
シェルプログラミング入門1
シェルプログラミング入門2
シェルプログラミング入門3
の続編である。

ファイルディスクリプタ

ファイルディスクリプたはプロセスと使用するファイルを結びつけるものである。プログラムを書き込むとき、プロセスはファイルディスクリプタによってファイルへアクセスしている。

ファイルディスクリプタは数値で表されており、0~2番は予約されている。

ファイルディスクリプタ 0番標準入力(普通はキーボードから入力される)
ファイルディスクリプタ 1番標準出力(普通は端末画面に出力される)
ファイルディスクリプタ 2番標準エラー(普通は端末画面に出力される)

リダイレクション

リダイレクションは普通の入力元や出力先を換えること。

リダイレクト記号>,>>

現在の作業ディレクトリにファイルがないものとする。

% echo abcdefg #標準出力に書き込まれる
abcdefg       #結果は画面に
% echo abcdefg > abc  #出力をファイルabcに向ける
% cat abc       #ファイルabcに書き込まれている。
abcdefg

ちなみにリダイレクトは変数も使える。

STDOUT=/dev/null
command>$STDOUt

>>>の違いは上書きするか追加されるかである。

% echo 123456 > abc  #abcに123456を書き込む
% cat abc    #内容が書き換えられた
123456
% 
% echo xyz >> abc  #abcにxyzを書き込む
% cat abc    #内容が追加された
123456
xyz

ここで

% cat abc xxx # abcの中身の表示、xxxはないのでエラー
123456  
xyz
cat: xxx: No such file or directory 

# abcとxxxをnnnに書き込む# xxxのエラーはnnnに書き込まれず画面に表示される
% cat abc xxx > nnn  
cat: xxx: No such file or directory

このときエラーメッセージは標準エラーに出力されたものである。標準出力の出力はnnnに書き込まれた。
リダイレクト記号>標準出力の向きを換えることができる。

標準出力はファイルディスクリプタの1番なので

# echo abcdef > abcと同じ
% echo abcdef 1> abc

と書き換えらる。1は省略可能。

また、先ほどの例で

% cat abc xxx 2> nnn
123456
xyz
% cat nnn           
cat: xxx: No such file or directory

とすると、標準エラー(ファイルディスクリプタ2番)の向きが変わり、出力先がnnnとなる。標準出力の向きは変わらないので普通に画面に表示される。

標準出力, 標準エラーの両方の向きを変えるときは以下のようにする。

% cat abc xxx > nnn 2>&1
% cat nnn 
123456
xyz
cat: xxx: No such file or directory

リダイレクト記号<

標準入力をリダイレクトするときは<を使う。

以下のようなxyzを作成する。

xyz
ls-l abc nnn
% ls-l abc nnn #普通にls -lを実行する-rw-r--r--  1 Tomoki  staff  11  5  8 17:18 abc
-rw-r--r--  1 Tomoki  staff  36  5  9 16:46 nnn

#xyzファイルをshの標準入力としてリダイレクトする
% sh < xyz 
-rw-r--r--  1 Tomoki  staff  11  5  8 17:18 abc
-rw-r--r--  1 Tomoki  staff  36  5  9 16:46 nnn

#標準入力の0を省略せずに書くと、
% sh 0< xyz
-rw-r--r--  1 Tomoki  staff  11  5  8 17:18 abc
-rw-r--r--  1 Tomoki  staff  36  5  9 16:46 nnn

リダイレクト記号の記述法のまとめ

以下にリダイレクト記号の記述法をまとめてみた。

>file  #標準出力をfileにかく(1>file)>>file  #標準出力をfileに追加書きするく(1>>file)>&m  #標準出力をmばんのファイルディスクリプタにかく(1>&m)>&-  #標準出力をクローズする(1>&-)<file  #標準入力をmというファイルディスクリプタから読み込む<&m  #標準入力をmというファイルディスクリプタから読み込む<&-  #標準入力をクローズする(0<&-)<<word  #標準入力をヒアドキュメントから読み込む(0<<word)

パイプ |

|は標準入出力を取り扱う。

command1 | command2

これは以下と同じである。

command1 > file
command2 < file

リダイレクトを使った書き込み

echoコマンドは標準出力にメッセージを表示するので、エラーが発生したときにechoを使っても、標準エラーには書き込まれない。
そういうときには以下のようにすればいい。

echo"Error" 1>&2

ファイルディスクリプタの3番以上の番号を使うときはexecコマンドで明示的に指定しなければならない。

% echo"Error" 1>&8 #8番を指定してみる
sh: 8: Bad file descriptor #8番は使われていないと出る
% exec 8> hhh      #8番を使ってhhhに書き込む
% echo abcdef 1>&8 #ファイルhhhに書き込まれた

基本的に3以上を使用することはほとんどない。

リダイレクトを使った読み込み

<<を使うとファイルを使わずにそこに書いてあるテキストをそのまま入力できる。これをヒアドキュメントという。

command<<word・・・・
・・・・
word

標準出力と同じように、ファイルディスクリプタの番号を変えることができる。

command n<&m

execコマンド

execコマンドで現行のシェルに対して、リダイレクト処理を行うことができる。

% echo abc 
abc #普通に表示される
% exec> /dev/null #標準出力先を変える
% echo abc   #出力がリダイレクト先にいった
% cat nonexist  #標準エラーは出力されるcat: nonexist: No such file or directory
exec< file #fileから入力するexec 1>&2  #標準出力を標準エラーに向けるexec 2> /dev/null #標準エラーを捨てて画面に出さないようにするexec> /dev/null 2>&1  #標準エラーと標準出力を全て捨てるexec>>file  #標準出力をfileに追加書きするexec n> file #ファイルディスクリプタn番を使って書き込むexec n>> file #ファイルディスクリプタn番を使って追加書きexec n< file #ファイルディスクリプタn番から読み込む

readコマンド

readコマンドは例えば以下のように(普通はキーボードからの)入力を読み込んで変数にセットする。普通はキーボードからの入力だがリダイレクト記号を使ってファイルから読み込ませることも可能である。

read ANSWER
if["$ANSWER"="yes"];then   ・・・・
else・・・・
fi

ファイルから読み込ませるとき普通はファイルの1行のみreadコマンドに渡るが、
以下のようにwhile文のときに使用すると行が続く限り1行ずつreadに処理させられる。

while  read  LINE
do・・・・
done< file

while内で変数を設定したり変更したりしても、whileを抜けると全て元に戻ってしまう。(ちなみに現在ではSolarisのsh以外はほとんどwhile内の変数の値はそのまま利用可能になっている。)

while内で変更した値をwhileが終了したときに使いたいときは以下のように書く。

exec<  file
while  read  LINE
do・・・・
done

しかし、これだとファイルのみの入力しか受け付けない。
そこでキーボードからの入力をするときに別のファイルディスクリプタを使う

exec  3<&0  <file #3番を標準入力にする,標準入力をファイルからの入力にするwhile  read  LINE 
do・・・・
done
exec  0<&3  3<&- #標準入力を3番にして、3番を閉じる

また、ファイルからの読み込みを3番で行うこともできる。

exec 3< file
while  read  LINE  0<&3
do・・・・
done
exec  3<&-

readコマンドは空白やタブが何個か続いても1つの空白とみなされる。
そういうときにはIFS(単語の切れ目)変数の値をクリアする。

OLDIFS=$IFSIFS=while  read  LINE
do
    echo"$LINE"done
IFS=$OLDIFS

リダイレクションのクローズ

#標準入出力のクローズexec>&-
exec<&-

#ファイルディスクリプたn番のクローズexec n>&-
exec n<&-

ただし、コマンドの出力を捨てたい時はcommand > /dev/nullとした方がよい。

ファイルのゼロリセット

ファイルの大きさを0(ファイルの中身をクリア)にしたいときや中身のないファイルを作成したい時は次の2通りの方法がある。

>file
: >file

また、/dev/nullファイルを使用する方法もある。

cat /dev/null > file
cp /dev/null file

ちなみにエディタでファイルを開いて、中身を削除しても1byteは残る。完全に0にするには上記の方法しかない。

ヒア・ドキュメント(Here Documents)

あるコマンドに渡すキーボードからの入力文字列をそのコマンドの直後に指定することができる。

ヒアドキュメントは以下のような形式でwordにはどんな文字を使ってもよい。

<<word

wordには「ここまでを入力のデータとして扱う」を明示するために、よくEOFやENDを使う。

次の例は・・・・がcommandの入力として扱われる。

command<<END・・・・
   ・・・・
END

catは引数なしだと、標準入力からの入力を画面にそのまま表示する。

% cat 
This is a
This is a
input data. 
input data.
#^Dで終了

これを<<を使うと、

% cat<<END> This is a
> here document.
> END
This is a
here document.

このようになる。
入力に変数を入れることもできる。

% DOC=document
% cat<<END> This is a
> here $DOC.
> END
This is a
here document.

<<の後の文字列の前に\もしくは'を使うと、$DOCをそのまま表示させられる。

% DOC=document
#\ENDの代わりに'END'としても同じ
% cat<<\END> This is a
> here $DOC.
> END
This is a
here $DOC.

変数の前に\$DOC\をつければ変数だけクウォートできる。

<<の後に-をつけると行頭のtabは無視される。

% cat<<-END<tab>  This is a
  here document.
END
This is a
  here document.

例えば、シェルプログラムのエラーメッセージを出力する時、以下のようにUsage関数を作り、catコマンドの出力を標準エラーに向ける。

Usage(){cat 1>&2 <<-EOF
        Usage: $0 [-options][etc..]
         ・・・・
         ・・・・
    EOF
}

入力をヒアドキュメントから、出力をファイルにリダイレクトこともできる。

command<<EOF>stdout 2>stderr
    ・・・・
EOF

パイプに渡すのも可能

command1 <<EOF | command2
    ・・・・
EOF

edコマンド

edコマンドは-オプションを指定すると画面にメッセージを出力しない。
読み込んだファイルの行を逆順に並び替える。

ed - file <<- !#fileを開く。次の!までがヒアドキュメント
g/^/m0 
w  #上書き保存
q  #edコマンドの終了!#ヒアドキュメントの終了

g/^/m0gはファイルの中で一致するパターンを探す。
gの後の/で囲まれた文字列が探す対象である。ちなみに^は行頭を表す。
すなわち、g/^/は全ての行頭で処理を行うという意味。
m0は処理の内容で0行目に移動する(moveのm)ということ。
1行目から順番に最初のファイルの最初に移動させていくと行が全て逆さまになる。

終わりに

前回までのリンク

シェルプログラミング入門1
シェルプログラミング入門2
シェルプログラミング入門3


Viewing all articles
Browse latest Browse all 2825

Latest Images

Trending Articles