あるみにメモ

技術的なメモをかくかもしれない

シェルスクリプトで実行プログラムの引数を変えながらまとめて実行して1つのファイルにまとめたい(バッチ処理)[1]

題のまんまの事がしたいあるみにの話.

ちょっとやらないとすぐ忘れてしまうので試したこととかを残す

引数を要求する自作プログラムを引数を変えながら代わりに実行させて,

シェルコマンドを上手く使って出力結果をそのまままとめたりしたいという話.

こういう処理のことをバッチ処理っていうらしい.



やりたいこと

  • 作成したプログラムを実行させる
  • ↑の引数を変えた複数の命令セットにして扱う
  • 同時に実行を指定のファイルに出力させる

初歩です.

前提条件

まず,実行したいプログラムコードを,

program

という名前の実行ファイルにコンパイルしたとしよう.


実行するときは,

./program

とすればいいし,引数が付くのなら

./program 0 10 200

という風になる.


例えば引数を2つとり,2数の結果を出力するcalc.cというプログラムを作成した.

#include <stdio.h>

int main(int argc, char *argv[])
{
    int a, b;
    a = atoi(argv[1]);//引数1番目をaに代入
    b = atoi(argv[2]);//引数2番目をbに代入

    printf("%d%dの和は%dです。\n", a, b, a + b);
    return 0;
}

このプログラムを用いて,様々な2数の計算結果を調べたいとする.


1+1を調べたいのであれば,

./calc 1 1

と実行する.


出力は,

1+1の和は2です。

となる.


1~100まで全ての組み合わせで全ての値を計算せよと言われたとき, 全パターンを1回ずつ入力,実行し,結果をコピペしたりするのは流石にキツイ.

まあこの例であればそもそも1~100の組み合わせ1つのプログラムの中で計算して逐一出力すればよいのだけど.

今回の目的はプログラムをいじるのではなく,プログラムの実行やその出力結果先を コントロールするシェルスクリプトを書くことで効率化を図ることである.

複雑なシミュレーション結果を出力するものであるとか,入出力機構をあまりいじるわけにはいかないプログラム(闇)だとか,とにかく引数を使って得るタイプのプログラムでも,いちいち入力,実行,記録のステップを踏みたくないので,シェルスクリプトをつかって自動化を試みる.


シェルスクリプトで「Hello,World」

まずはシェルスクリプトを書いてみる.

echo "Hello,World!!"

シェルスクリプトは拡張子を「.sh」にして保存する.

(つけないことが実は普通だとか)

「echo」は出力命令で(厳密にはそれだけでないが),このシェルスクリプトを実行すれば, 「Hello,World!!」と出力してくれるはずである.


シェルスクリプトの実行は他のプログラムの実行と同様に,

./test.sh 

とする.


シェルスクリプトは命令のまとまりを逐次解釈して実行するインタプリタなので,

コンパイルは必要としない.


シェルスクリプトの実行権限


./test.sh: 許可がありません

なんて言われて実行出来なかったかもしれない。

詳しい説明は省くが,ファイルには属性がある.

そのうち,実行権限という属性がある.

ls -a なんかをして調べられるが,シェルスクリプトに 実行権限を与えていない場合実行できないので, 信頼できるシェルスクリプトを作れたなら実行権限を与えよう.


chmod +x シェルスクリプト名

で実行権限を与えられる.


実行可能なファイルはlsなどをした時も色が変わって見えるようになっていると思う.

実行したいシェルスクリプトが存在するディレクトリで,

./test.sh 

とすれば,

Hello,World!

と出力される.

シェルスクリプトに実行権限を与える事を忘れないように.


改行コードの注意

もしかして

実行形式エラー.間違ったアーキテクチャです.

なんて言われている人もいるかもしれない.


f:id:aluminum_pepe:20190917155628p:plain

自分は先ほどの一行コード,こんな風にecho文の前に空行があると上のように怒られてしまった.

もっと言えば,

f:id:aluminum_pepe:20190916150423p:plain

上は空いてないでecho文のあとに空行がいっぱいあるようなコードだと,

f:id:aluminum_pepe:20190916145049p:plain

のように,謎のコマンド見つかりませんがたくさん出てくる.


我々捜索隊は原因を調査するためアマゾンの奥地に向かった-------------------



試しに空行とecho文を織り交ぜる.

f:id:aluminum_pepe:20190916145306p:plain

とすると,

f:id:aluminum_pepe:20190916145322p:plain

こうなる.

空行の数だけ,というより,命令が記述されていない行の改行に反応しているっぽい.

この問題,改行コードが「CRLF」になっていることが問題らしい.

というわけで,エディタの設定で「LF」にすると,改行をコマンドとして 検知しなくなり,上記の二つの問題が解決する.

f:id:aluminum_pepe:20190916150148p:plain

としても....


f:id:aluminum_pepe:20190916150210p:plain

ヨシ!!


他のプログラムを実行させる

シェルスクリプトでは他の実行ファイルを実行する事もできる

例えば,

echo "calcを実行します"

./calc 1 1

として実行すれば,

calcを実行します
1+1の和は2です。

となる.


test.sh内で先ほど作ったcalcプログラムに1と1を引数に与えて計算させることができる.

もちろん同じディレクトリにない実行ファイルも,パスさえ正しければ実行出来るはず.

引数をいちいち与える手間だけを省略したいのであれば,

echo "calcをいっぱい実行します"

./calc 1 1
./calc 2 1
./calc 3 1
./calc 4 1
./calc 5 1
./calc 6 1
./calc 7 1
./calc 8 1
./calc 9 1
./calc 10 1
・
・
・

なんてすれば,まとめて実行できる(脳筋).


実行結果の保存先(リダイレクション「>>」)

プログラム実行結果は

./calc > result.txt

などとすることで,「result.txt」に出力させることができる.

(リダイレクションは保存先を変えるという意味)

「>」は完全な上書きなので,

「>>」の追加を使うと良い


もちろんこのリダイレクションも,シェルスクリプト内にそのまま記述でき,その通りに実行させる事ができる

さっきの脳筋シェルスクリプトを,

echo "calcをいっぱい実行します"

./calc 1 1 >>result.txt
./calc 2 1 >>result.txt
./calc 3 1 >>result.txt
./calc 4 1 >>result.txt
./calc 5 1 >>result.txt
./calc 6 1 >>result.txt
./calc 7 1 >>result.txt
./calc 8 1 >>result.txt
./calc 9 1 >>result.txt
./calc 10 1 >>result.txt
・
・
・

とすればresult.txtに結果をどんどん記述していくことができるので,わざわざ黒い画面の結果を1つずつコピペして記録する必要はない.

f:id:aluminum_pepe:20190916152957p:plain

実行のさせ方が強引ではあるが, 必要に応じてこのシェルスクリプトを書き換えることで 様々な命令をまとめて実行し,指定したファイルに結果を出力できる.

また,シェルスクリプトそのものに対してもリダイレクトは有効なので,シェルスクリプト内のリダイレクトを無くし,

test.sh >>result.txt

としてもいいと思う.


全パターン列挙は流石に・・・・という人へ

当然1~100まで全パターンという処理を, シェルスクリプト内で書くことで, 全パターンを列挙する必要をなくすことはできるので, 最後にそれを記述しよう.

#!/bin/sh
for ((i=0 ; i<=100 ; i++))
do
    for ((j=0 ; j<=100 ; j++))
    do
    ./calc $i $j
    done
done
シェルスクリプトにも変数を使って数値計算処理できるということである


シバン行

今回のコード例では記述していないが, 大抵のシェルスクリプトの話題では,コードの最初のほうに

#!/bin/sh
とか
#!/bin/bash

記述してある

これはシバン行といって,

・・・・・・・・

・・・

qiita.com

スクリプトファイルの冒頭を#!にして、その後にファイル名を書いておくと、実行属性を付けてファイルを実行できるようになります。この冒頭行を「シバン」といいます。

・・・・

うーん?()

後ろにオプションを付ける事でいくらかなにかしら設定できるみたい(無理解)

あるとシェルスクリプトとして働くのかな・・・とおもったら無くても動いちゃったから分からん

それとも「.sh」があったからシバン行指定しなくても動いたとかいうオチかな? (無くても動きました)

自分の環境(Linux系サーバ)だと勝手に分かってくれるだけとかそういうこと?

ただ,そういった実効的な意味合いよりも, 「bashのコマンドや記法を含むよ!!」といったような説明的な効果の方が大きいようなイメージを感じている.

コード規約とかに近い感じかな


とりあえず自分も,

#!/bin/sh

を記述しておくことにする.



まとめとつづきのはなし

さて,シェルスクリプトをつかってプログラム(コマンド)の実行を代わりに行わせることができるようになったので, とりあえず「まとめて実行」「まとめて記録」ができるようになった.

今回は計算プログラムに対して引数を変えて指定のファイルに結果を出力させたが, これはCプログラム実行コマンドとUNIX/Linuxの「>>(リダイレクション)」の合わせ技をさせたことになる.

このようにしてシェルスクリプトでは UNIX/Linux上の複数のコマンド実行やファイル操作をまとめて実行できる利点を活かす事もが目的で使われることも多い(バッチ処理っていうんだって).

複数の既存のプログラムを繋げて実行でき,ファイル操作の場面でシェルコマンドもまとめて実行できるので,自動化にはもってこいといった感じ,一回実行しただけで面倒な手順を自動でやってくれると楽

自動化プログラムだけ上手くメンテナンスできれば人為的なミスが減る(保存先間違えたり設定値変え忘れたりしそうだった).

既存のプログラムの本体をいじるより,命令(引数)を入力して実行する工程に工夫をして自動化する方が効率が良い事もけっこうありそう


自動化するにあたってまだまだ改良の余地がありすぎるというか,コマンドの組み合わせをほとんどしてない段階.

続きとして用途に合わせたコマンドの使い方をメモ書きするつもり.

たとえば,ディレクトリを生成するmkdirを使って結果を出力するためのディレクトリをつくったり,出力結果保存用のファイルにリセットをかけられるようにしたり,シェルスクリプト自体が引数をとって扱う・・・・・とか


最後に

適当に検索かけつつ,ざっとシェルスクリプトを使ってみたときのことをまとめてみた

改行コードだったり実行権限だったり, とりあえず使ってみようとしたときに自分がつっかかった箇所を取り上げつつ シェルスクリプトを試せる/思い出せるようになっている気はする

自分もこれで何回忘れても大丈夫だね!!


知ってることをまとめたつもりだけど,初学者のガバガバ知識でうやむやにしてたりする


この記事を見かけた人へ

間違っている事がありましたら是非ともご教授(マサカリを投げて)くださるとありがたいです。