エンジニアの副業は週1からでも可能?副業の例や探し方も解説
- ITエンジニア
- 副業
「シェル」や「シェルスクリプト」という用語を聞いたことはありますか? 「シェル」とは、CLI上で動作してOSを操作できるソフトウェアのこと。そのシェルを組み合わせて記述する、簡単なプログラムを「シェルスクリプト」と言います。
この記事では、「シェル」についての前提知識をご紹介した上で、入門者向けにシェルスクリプト(Bash)のできることや基礎文法、実際のコードについて解説していきます。
作業を効率化したいエンジニアの方は、ぜひこの記事を参考にしてみてください。
シェルとは、CLI(Command Line Interface)という黒い画面上で動作するソフトウェアのひとつです。「コマンド」と呼ばれる命令文を利用してOSを操作できます。
シェルにはさまざまな種類があり、もっとも標準的なのは「Bash(Bourne-Again Shell)」です。Bash以外に代表的なシェルは、以下のようなものが挙げられます。
この他にもさまざまな種類のシェルが存在しますが、大まかな文法はどのシェルも共通です。そのため、ひとつのシェルについて学習すれば、違うシェルの扱いも容易になります。
今回は、シェルの中でもっとも代表的な「Bash」のシェルスクリプトについて解説していきます。
シェルスクリプトとは、if文/while文/パイプなどの制御構文と、コマンドを組み合わせて動作する簡単なプログラムのことです。
一般的に拡張子「.sh」のファイルとして保存され、一度つくれば何度でも再実行できます。
ファイル移動やコピペ、ログの記録/抽出など、PCでできる基本的な操作はシェルスクリプトで肩代わりできます。
とくにUNIX系のOSであれば、Baahのシェルスクリプトが標準で入っていることがほとんどなので、LinuxやmacOS上の操作をするときに便利です。
シェルスクリプトの「パイプ」という機能をつかえば、複数の異なるコマンドを連携させることが可能です。
たとえば「grep」というコマンドは、文書内から特定の文字列が含まれる箇所をすべて抽出してくれます。このgrepコマンドが抽出した結果のうち、ラスト3つ分の結果のみを抽出したいというときはどうすればよいでしょうか。
答えは「tailコマンドと組み合せる」です。tailコマンドは、入力された文書について、指定した行数分を末尾から読み取ってくれます。そのため、パイプを使ってgrepコマンドとtailコマンドを組み合せると、「文書中から特定の文字列が発見されたラスト3か所を取得する」という処理を実現できます。
このようにシェルスクリプトのパイプを利用すれば、ひとつのコマンドでは実現できない複雑なタスクをこなせるようになるのです。
シェルスクリプトで組み合せられるのは、コマンドだけではありません。JavaやRuby、Goなどの言語で実装されたツールやソフトを組み合せて、さらなる業務自動化を図ることもできます。
組み合せることのできるツール/ソフトに制限はありません。Gitのような開発に欠かせないCLIツールから、Google ChromeのようなGUIアプリまで、さまざまなツールやソフトをシェルスクリプト内で連携させられます。もちろん、自作ツールとの連携も可能です。
このように、シェルスクリプトによってさまざまなツールを組み合せれば、さらなる業務自動化/効率化を実現できます。
シェルスクリプトでできることを踏まえた上で、次は実際にシェルスクリプトを書いていくための基本文法について解説していきます。
シバン(shebang)とは、シェルスクリプトの1行目に記述する文字列です。シバンによって実行するシェルの種類を指定できます。
前述のしたとおり、シェルにはBashやCsh、Zshなどさまざまな種類があります。そのため、シバンを用いて「このシェルスクリプトをどのシェルで動かすのか」を明示的に指定しないと、思わぬ挙動をしてしまう可能性があるのです。
今回はBashのシェルスクリプトを作成したいので、次のようにシバンを書きます。
#!/bin/bash
なおBashに指定せず、デフォルトに設定されているシェルを利用する場合は、以下のようにシバンを書きます。
#!/bin/sh
シバンを書いてしまえば、それ以降はシェルスクリプトの処理そのものの記述を自由に書けます。
ここでは文字列を出力する「echo」コマンドを使って、【Hello world.】をコマンドラインに表示するBashのシェルスクリプトを作成してみましょう。
まずテキストファイル「hello.sh」を新規作成して、以下を書きましょう。
#!/bin/bash
echo ‘Hello world.'
保存をしたら、hello.shに「実行権限」を付与します。実行権限がないと、ファイル名だけでシェルスクリプトの実行を行えません。
実行権限の付与には、chmodコマンドを使います。コマンドラインから以下を入力して、hello.shを実行可能ファイルにしましょう。
# 『+x』で実行権限を付与できる
chmod +x hello.sh
実行権限の付与が完了したら、hello.shをファイル単独で実行できるようになります。実際に、以下をコマンドラインで入力してEnterを押してみましょう。
./hello.sh
【Hello world.】がコマンドライン上に表示されましたね。このようにシェルスクリプトは「1行目にシバンでシェルの種類を指定」「2行目以降に処理の本文を記述」という構成で書いていきます。
なおシェルスクリプト実行時に、最初の「./」がないとエラーが出るので注意してください。(「hello.sh」が実行可能ファイルではなく、コマンドと認識されてしまうため)
一般的なプログラミング言語と同じで、シェルスクリプトにも変数があります。Bashのシェルスクリプトで変数を使用したい場合は、以下のように書いてください。(以降の解説では1行目のシバンを省略します)
# 変数の宣言と値の設定(注意! イコールの両隣はスペース不可)
str='hello'
# 変数の使用(echoコマンドで変数strの中身を出力)
echo $str
また変数展開を使えば、文字列中に変数の中身を出力できます。たとえば、変数$str(中身は’hello’という文字列)を使って【hello world.】という文字列を表示したい場合は、以下のように書きましょう。
# 変数$strが展開されて【hello world.】と表示される
echo “$str world."
変数展開を許可する文字列の範囲をダブルクォーテーションで囲むことで、「記述した変数」が「変数にセットされた値」に置き換えられるのです。
なお、このときダブルクォーテーションではなくシングルクォーテーションだと、変数展開は許可されず【$str world.】と表示されます。
文字列中に変数を展開するだけではなく、コマンドの実行結果を文字列中で展開したいときもありますよね。そのようなときに役立つのがコマンド置換です。
たとえば、現在の日時を出力する「date」というコマンドがあります。
# 実行するとyyyy-MM-dd形式で今日の日付が表示される
date +%F
このdateコマンドを利用して【今日の日付は2020-06-27です】と表示させたい場合は、コマンド置換を利用して以下のように書きましょう。
# 『 $( コマンド ) 』でコマンドの実行結果を文字列中に展開できる
echo "今日の日付は$(date +%F)です"
# あるいは以下のような書き方も可能
echo "今日の日付は`date +%F`です"
このように「 ドルマーク + 括弧( $() ) 」もしくは「バッククォーテーション( “ )」で囲まれた範囲は、コマンドとして解釈/実行された結果の文字列に置き換えられます。
「もし~なら、~する」という、特定の条件に当てはまったときのみ処理を実行する文のことをif文と呼びます。
たとえば、「組み込み変数$USERが”sig”のときだけ、【Hi.】と出力する処理」を実装したいときは、以下のように書きましょう。
# 注意! ifの右にある角括弧の左右にはスペースが必須(スペースがないとエラーになる)
if [ $USER = 'sig' ]
then
echo 'Hi.'
fi
このように以下の4パーツで、「もし~なら、~する」という処理の実装が可能です。各パーツの意味合いや書き方も、下にまとめています。
上記シェルスクリプトの場合、ユーザ名が”sig”なら【Hi.】と表示されますが、ユーザ名が”sig”以外なら、なにも表示されないようになっています。
この処理に、以下の処理を追加したいときにはどうすればいいでしょうか。
結論から述べると「elif句」と「else句」を利用することで実現できます。以下のように記述をしてみてください。
# if句
if [ $USER = 'sig' ]
then
echo 'Hi.'
# 上の『ifの条件句』に当てはまらなかった場合、elif句に処理が移る(記法はifと同じ)
elif [ $USER = 'guest' ]
then
echo 'Welcome.'
# 『すべてのif・elifの条件句』に当てはまらなかった場合、else句に処理が移る(then不要)
else
echo 'Who are you?'
fi
このようにif文を使えば、条件に応じた処理の出し分けができます。
なお、上記のシェルスクリプトでは「イコール記号( = )」で文字列の比較を行っていましたが、比較する値が数値の場合は使うべき演算子が変わるので注意してください。
数値の場合の演算子にはいくつか記法があります。そのなかでも、最も移植性が高い記法は以下のとおりです。
# 『左辺と右辺が等しいなら』
if [ 1 -eq 1 ]
# 『左辺が右辺より大きいなら』
if [ 1 -gt 0 ]
# 『左辺が右辺より小さいなら』
if [ 0 -lt 1 ]
パイプを使えば「コマンドの実行結果」を「異なるコマンドの入力」として渡し、複雑なタスクを処理できるようになります。
たとえば、以下の3つのコマンドがあります。
これらをパイプで以下のように組み合せると、「hoge.logの中で”ERROR”が登場した最後の行」を取得できます。
cat hoge.log | grep ERROR | tail -n 1
なぜこのシェルスクリプトが「hoge.logの中で”ERROR”が登場した最後の行」を取得する処理になるのでしょうか。それを理解するためには、「標準入力」と「標準出力」の理解が欠かせません。
標準入力/標準出力は、プログラミング言語全般において「暗黙的な入出力」を示します。シェルスクリプト中で利用するコマンドにおいては、以下の役割を果たします。
このように標準入出力は、キーボードやコマンドラインの画面といった一般的な入出力のみならず、パイプを利用した情報の受け渡しにも対応しています。
これを踏まえた上で、「cat hoge.log | grep ERROR | tail -n 1」の処理の流れを追いかけてみましょう。
cat hoge.log
cat hoge.log | grep
cat hoge.log | grep ERROR
cat hoge.log | grep ERRPR | tail
cat hoge.log | grep ERRPR | tail -n 1
このようにパイプを利用することで、あるコマンドの実行結果を、次に実行するコマンドの入力情報として渡せます。
while文を使えば、指定した条件を満たす間は処理をループすることが可能です。たとえば、カウントアップしながら「現在のカウントはnです」と表示するループ処理は、次のように書きます。
#!/bin/bash
end=10
i=0
# while [ 条件句 ] で『条件を満たす間以下の【do~done】の処理をループする』という意味になる
while [ $i -lt $end ]
do
# exprコマンドは四則演算を行うコマンド
i=`expr $i + 1`
echo "現在のカウントは$iです"
done
これを実行すると、while [ 条件句 ] で指定したとおり、変数$iが10未満の間は「現在のカウントはnです」とカウントアップをできます。
このときwhile [ 条件句 ] で利用している角括弧は、if文と同じくtestコマンドの省略記法です。そのため、-lt や -gt など比較の種類に応じてさまざまなオプションを利用できます。
またwhileの右隣には、testコマンド以外のコマンドを記述することも可能です。とくに「while + readコマンド」の組み合わせはよく使われるので、次でご紹介します。
以下のようにwhileとreadコマンドをパイプで組み合せると、文書を読み取り、1行ごとに処理を実行できます。
cat hoge.log | while read line
do
echo “内容:$line"
done
このシェルスクリプトを実行すると、hoge.logの最終行まで1行ずつ「内容:xxxxxxx(その行の内容)」と文字列が出力されていきます。このときの処理の流れは以下のとおりです。
cat hoge.log
cat hoge.log | while read
cat hoge.log | while read line
cat hoge.log | while read line
do
echo “内容:$line"
done
このようにwhile文とreadコマンドを組み合せれば、標準入力に渡された文書を1行ずつ処理できるようになります。
なおreadの前に実行するコマンドは、catコマンド以外でも「結果を標準出力するコマンド」なら大丈夫です。
たとえば、連番の数字を出力するseqコマンドを、以下のように「while文 + readコマンド」に渡すこともできます。
# 『seq 5』 で1~5までの数字を1行ずつ出力する
seq 5 | while read line
do
# while read line で『seq 10』の結果を1行ずつ処理する
#(exprコマンドですべての数字を2倍にしている)
echo `expr $line \* 2`
done
# 以下が出力される
# 2
# 4
# 6
# 8
# 10
このように「標準入出力」「while文」「readコマンド」を組み合せることで、さまざまなコマンドの実行結果を1行ずつ処理できるようになります。ぜひ覚えておいてくださいね。
ここまでに紹介してきた基本文法を踏まえて、タスク自動化/効率化を実際にBashのシェルスクリプトで実装してみましょう。
業務自動化と聞くと難しそうに思えるかもしれませんが、安心してください。タスク自動化のコツは、簡単な定型作業から少しずつ自動化/効率化を施していくことです。はじめから難しいことに取り組む必要はありません。
ここでは業務中によくある作業の例として、以下の3つのタスクをシェルスクリプトで自動化してみます。
1.連番ディレクトリ作成
2.ファイルの一括バックアップ
3.ログの監視
業務をしているとき、「No.01」「No.02」「No.03」……のように、連続する番号のディレクトリを作成したい場面があると思います。手作業でも出来るものの、数が多くなるとかなりの時間を取られてしまいますよね。
Bashのシェルスクリプトをつかえば、このような作業を簡単に自動化できます。
「seqコマンド」「while文 + readコマンド」「mkdirコマンド」の3つを使って、No.01~No.20の連番ディレクトリを作成するシェルをつくってみましょう。
#!/bin/bash
# seqで1~20を順に出力. readに渡してひとつずつ処理
seq 20 | while read line
do
if [ $line -lt 10 ]
then
# 10未満なら0埋め
mkdir "No.0$line"
else
# 10以上なら数値そのまま
mkdir "No.$line"
fi
done
このようにseq, readを組み合わせたwhile文中で
とすることで、No.01~No.20のディレクトリを作成できます。(mkdirはディレクトリ作成のコマンドです)
またseqの「wオプション」を利用すれば、自力でif文を利用した0埋めをせずとも、自動的に上位桁の0埋めをしてくれます。そのため上記シェルスクリプトを、以下のように短縮することが可能です。
#!/bin/bash
seq -w 20 | while read line
do
# seqコマンドは『-w』オプションを使えば自動で0埋めしてくれる
mkdir "No.$line"
done
シェルスクリプトは、コマンドのオプションを利用することで短縮できる場合があります。コマンド利用時には、有用なオプションがあるかどうかを知っていると、より短時間で処理を実装できる場合も。便利なオプションがないかを、ときどき確認しておくとよいでしょう。
ファイルの一括バックアップも、Bashのシェルスクリプトを使えば簡単に自動化できます。たとえば、以下の要件を満たすシェルスクリプトを実装してみましょう。
これら要件を満たすには、以下の4つのコマンドを使っていきます。
書き方は以下のとおりです。
#!/bin/bash
# 「bk年月日_時分秒」形式の文字列を作成(dateコマンド + コマンド置換)
DIRNAME="bk`date +%Y%m%d_%H%M%S`"
# バックアップ用のログを置くディレクトリ作成(mkdirコマンド)
mkdir "$DIRNAME"
# 現在のディレクトリから.logファイルをすべて取得(lsコマンド)
ls *.log | while read line
do
# 1ファイルずつコピーして、バックアップ用ディレクトリに配置していく(cpコマンド)
cp "$line" "$DIRNAME/$line"
done
これをシェルスクリプトとして保存して実行すると、.logファイルを”bk年月日_時分秒”形式のディレクトリに、一括でバックアップできます。
なんらかのシステムのログを、リアルタイムで監視したいとき役に立つのが「tailコマンド」です。tailコマンドは「fオプション」を利用することで、監視中のファイルに書き込まれていく内容をリアルタイムで取得できます。
またBashのシェルスクリプトを使えば、特定の文字列が出現したときのみ、コマンドラインに新規行を表示することも可能です。
たとえば、「特定のファイルを監視して、”ERROR”が登場したらその行をリアルタイムで表示するシェルスクリプト」を実装するなら次のように書きます。
#!/bin/bash
# もし第1引数がファイルなら
# (変数$1はシェルスクリプトに渡された第1引数を示す)
if [ -f "$1" ]
then
# grep --line-buffered でtail -f の出力結果をリアルタイムでgrepに渡し、1行ずつ抽出結果を出力する
echo "ファイル [ $1 ] の監視を開始します"
echo '----------------'
tail -f $1 | grep --line-buffered ERROR
else
# もし第1引数がファイルでなければ
echo '引数にファイル名を指定してください'
fi
このように「tail -f」と「grep –line-buffered」を組み合せることで、リアルタイムのログ監視/抽出ができます。
今回はシェルスクリプトでできることと、その中でもっとも代表的な「Bash」のシェルスクリプトの基礎文法について紹介しました。
簡単なタスクでも、ひとつずつ自動化していくことで、1日の業務に余裕を持たせることができます。シェルスクリプトを使って、まず単純な作業から着実に自動化/効率化していきましょう。
(執筆:sig_Left 編集:Kimura Yumi)
PowerShell入門。できることや基礎文法、業務自動化の方法を解説【事例あり】
Workship MAGAZINE