004 レポート Homebrew 2.0.0リリース
005 レポート runcの脆弱性問題
006 NEWS FLASH
008 特集1 Linuxパーフェクトカスタマイズ/麻生二郎 コード掲載
020 特集2 AWS Lambdaを好きな言語で使おう/岡本秀高 コード掲載
028 特別企画 リレー式コンピュータプログラミング/若菜魁、入口雄也、八木武尊
036 ラズパイセンサーボードで学ぶ 電子回路の制御/米田聡
040 試み/桑原滝弥・イケヤシロウ
042 バーティカルバーの極意/飯尾淳
046 円滑コミュニケーションが世界を救う!/濱口誠一
048 中小企業手作りIT化奮戦記/菅雄一
056 法林浩之のFIGHTING TALKS/法林浩之
058 漢のUNIX/後藤大地
064 人間とコンピュータの可能性/大岩元
066 香川大学SLPからお届け!/竹原一駿 コード掲載
071 姐のNOGYO
072 UNIXの歴史を振り返る/古寺雅弘
078 Node.js/Expressで楽々Webアプリ開発/しょっさん コード掲載
089 ユニケージ新コードレビュー/坂東勝也 コード掲載
096 Techパズル/gori.sh
098 コラム「ユニケージお作法のココロ」/シェル魔人
シェルスクリプトマガジンvol.59 Web掲載記事まとめ
Vol.59
オープンソースのソフトウエアを組み合わせて、好みのOSが作れるのが「Linux」です。特に、好みに近いLinuxディストリビューションがあれば、それをカスタマイズするだけで自分だけのLinuxが作れます。特集1では、軽量で高速なLinuxディストリビューション「Lubuntu」をインストールメディアからカスタマイズして自分好みに仕立てる方法を紹介しています。
特集2では、AWSのサーバーレスサービス「AWS Lambda」に追加された新機能「AWS Lambda Custom Runtimes」を扱っています。AWS Lambda Custom Runtimesが登場する以前は、サービスとして実行される「Lambda」関数は、Node.jsのJavaScriptやPython、Rubyで記述する方法しかありませんでした。AWS Lambda Custom Runtimesの登場により、言語環境となるランタイムを用意すれば、その言語でLambda関数を記述できるようになりました。
特別企画では、1960年代に制作された国産リレー式コンピュータである「FACOM138A」のプログラミングに挑戦した大学生の奮闘記を紹介しています。
このほか、「センサーボードで学ぶ 電子回路の制御」「姐のNOGYO」などの人気連載も掲載しています。
今回も読み応え十分のシェルスクリプトマガジン Vol.59。お見逃しなく!
※読者アンケートはこちら
ユニケージ新コードレビュー(Vol.59掲載)
著者:坂東勝也
ユニケージでは、小さな道具の「コマンド」をシェルスクリプトで組み合わせて、さまざまな業務システムを構築しています。本連載では、毎回あるテーマに従ってユニケージによるシェルスクリプトの記述例を分かりやすく紹介します。第6回は、仕入伝票処理システムを例に最新マスターを取得するコードをレビューします。
シェルスクリプトマガジン Vol.59は以下のリンク先でご購入できます。
図11 伝票番号で並べ替え
1 2 3 4 5 6 7 8 9 10 |
145 # L3取得 146 if [ -s $tmp-check_data.1 ] ; then 147 cat $lv3d/SIRE_HEAD/SIRE_HEAD.$ymonth | 148 # 同じ伝票番号の情報を古い順にソート(最も新しいデータが一番下に来るように) 149 msort key=1@NF@NF-1r > $tmp-sire_header_l3 150 ERROR_CHECK 151 else 152 :> $tmp-sire_header_l3 153 ERROR_CHECK 154 fi |
図12 更新された仕入伝票を調べる
1 2 3 4 5 |
130 # 仕入伝票ヘッダーのLV1データの取得 131 # LV1データがあるかチェック 132 ls $lv1d/$today/SIRE_HEAD_* | 133 gyo > $tmp-check_data.lv1 134 ERROR_CHECK |
図13 指定月の仕入伝票の取得
1 2 3 4 5 6 7 8 9 10 11 12 13 |
156 # INPUT(L1)取得 157 if [ -s $tmp-check_data.2 ] ; then 158 cat $lv1d/SIRE_HEAD_* | 159 #dayslash --input yyyy/mm/dd --output yyyymmdd 2 - | 160 # 指定の日付の範囲内のデータを抽出 161 uawk '$2>='$range_from' && $2<='$range_to'' | 162 # 同じ伝票番号の情報を古い順にソート(最も新しいデータが一番下に来るように) 163 msort key=1@NF@NF-1r > $tmp-sire_header_in 164 ERROR_CHECK 165 else 166 :> $tmp-sire-header_in 167 ERROR_CHECK 168 fi |
図14 L1とL3のデータをマージ
1 2 3 4 5 6 7 |
170 # L1とL3をマージ 171 # 同じ伝票番号の中で最も新しいもののみを残す 172 upl key=1 $tmp-sire_header_l3 $tmp-sire_header_in | 173 # 削除フラグの立っている伝票を削除 174 delr NF-1 1 | (略) 186 ERROR_CHECK |
特集2 AWS Lambdaを好きな言語で使おう(Vol.59掲載)
著者:岡本秀高
カンファレンス「AWS re:invent 2018」にて発表された新サービス「AWS Lambda Custom Runtimes」。これまではサポートをアナウンスした言語でしか「Lambda関数」を作成できませんでした。このサービスの登場により好きな言語で作成可能となりました。本特集では、AWS Lambda Custom Runtimesの使い方から、ランタイム(実行環境)を実際に作って利用するところまでを分かりやすく紹介します。
シェルスクリプトマガジン Vol.59は以下のリンク先でご購入できます。
図3 bootstrapファイルの内容
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
#!/bin/sh set -euo pipefail # Initialization - load function handler source $LAMBDA_TASK_ROOT/"$(echo $_HANDLER | cut -d. -f1).sh" # Processing while true do HEADERS="$(mktemp)" # Get an event EVENT_DATA=$(curl -sS -LD "$HEADERS" -X GET "http://${AWS_LAMBDA_RUNTIME_API}/2018-06-01/runtime/invocation/next") REQUEST_ID=$(grep -Fi Lambda-Runtime-Aws-Request-Id "$HEADERS" | tr -d '[:space:]' | cut -d: -f2) # Execute the handler function from the script RESPONSE=$($(echo "$_HANDLER" | cut -d. -f2) "$EVENT_DATA") # Send the response curl -X POST "http://${AWS_LAMBDA_RUNTIME_API}/2018-06-01/runtime/invocation/$REQUEST_ID/response" -d "$RESPONSE" done |
図5 function.shファイルの内容
1 2 3 4 5 6 7 8 |
#!/bin/sh function handler () { EVENT_DATA=$1 echo "$EVENT_DATA" 1>&2; RESPONSE="Request: '$EVENT_DATA'" echo $RESPONSE } |
図6 template.yamlファイルの内容
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
AWSTemplateFormatVersion: 2010-09-09 Description: My PHP Application Transform: AWS::Serverless-2016-10-31 Resources: myRuntime: Type: AWS::Serverless::Function Properties: FunctionName: !Sub ${AWS::StackName}-myRuntime Description: My fisrt custom runtime Runtime: provided Handler: function.handler MemorySize: 3008 Timeout: 30 Layers: - !Sub 作成したランタイムのLayerVersionArn |
香川大学SLPからお届け!(Vol.59掲載)
著者:竹原一駿
USB接続のバーコードリーダーを用いてバーコード化されたデータを読み取り、その結果をサーバーに送って集計するデータ収集システムを開発しました。今回は、同システムについて紹介します。クライアントはGo、集計サーバーは主にPerlで記述しています。クライアントもサーバーもDocker環境で簡単に動かせますので、ぜひ試してみてください。
シェルスクリプトマガジン Vol.59は以下のリンク先でご購入できます。
図8 バーコードリーダーで読み取ったデータを取得する関数
1 2 3 4 5 |
func ScanStdin() string { stdin := bufio.NewScanner(os.Stdin) stdin.Scan() return stdin.Text() } |
図9 意見を示す数値を文字列に変換するコード
1 2 3 4 5 6 7 8 9 10 11 |
const AGREE = "100" // 賛成 const OPPOSITE = "200" // 反対 const NEUTRAL = "300" // 中立 if sData == AGREE { sData = "agree" // 意見の文字列を代入 } else if sData == OPPOSITE { sData = "opposite" } else if sData == NEUTRAL { sData = "neutral" } |
図10 データを集計サーバーに送るためのコード
1 2 3 4 5 6 |
resp, err := http.Get(baseurl) if err != nil { fmt.Println(err) return err } defer resp.Body.Close() |
図11 HTTPサーバー機能を提供するMyWebServerパッケージの記述
1 2 3 4 5 6 7 8 9 10 |
#!/usr/bin/perl { package MyWebServer; use HTTP::Server::Simple::CGI; use base qw(HTTP::Server::Simple::CGI); #継承 use strict; use warnings; # 以降の処理はここに書く. } my $pid = MyWebServer->new(80)->run(); |
図12 CGIで日本語を表示するための記述
1 2 3 4 |
print $cgi->header(-charset=>"utf-8"), $cgi->start_html(-lang => 'ja','Not found'), $cgi->h1('Not found'), $cgi->end_html; |
図13 ハッシュ変数にサブルーチンのリファレンスを保存
1 2 3 4 5 6 |
my %dispatch = ( '/index' => \&resp_index, # index '/insert' => \&resp_insert, # データの挿入 '/operate' => \&resp_operate, # 質問番号の調整 '/analy' => \&resp_analy, # 解析 ); |
図14 insertページを表示するコード
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
sub resp_insert { my $cgi = shift; return if !ref $cgi; my $user = $cgi->param('user'); # ユーザーID my $opi = $cgi->param('opi'); # 意見 my $dbh = DBI->connect( "dbi:mysql:database=opinidb;". "host=mysql;port=3306", 'user','password' ); #データベースの接続 my $sth = $dbh->prepare( "INSERT INTO opinidb.opinion(USER,NUM,OPI)". " VALUES (?,?,?);" ); $sth->execute($user,$ques_num,$opi); # SQL生成,実行 $sth->finish; $dbh->disconnect; #接続終了 print $cgi->header(-charset=>"utf-8"), $cgi->start_html("insert"), $cgi->h1("Question number is $ques_num"), "ユーザ:$user, 意見$opi", $cgi->end_html; } |
図15 analyページを表示するコード
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
my @opilist = ('agree','opposite','neutral'); print $cgi->header(-charset=>"utf-8"); print $cgi->start_html(-lang => 'ja',"analy"); foreach(@opilist){ print $cgi->h2("count : ".$_); my $sth = $dbh->prepare( "SELECT NUM AS ques_num , ". "COUNT(*) AS countopi ". "FROM opinidb.opinion ". "WHERE OPI LIKE '\%".$_."\%' ". "GROUP BY ques_num" ); $sth->execute(); # SQL生成,実行 print '<table border=1>'; print '<tr><th>質問番号</th><th>選んだ人数</th></tr>'; while(my $datahash = $sth->fetchrow_hashref){ print '<tr><td>'. $datahash->{'ques_num'}.'</td><td>'. $datahash->{'countopi'}.'</td></tr>'; } print '</table>'; $sth->finish; } |
特集1 Linuxパーフェクトカスタマイズ(Vol.59掲載)
著者:麻生二郎
Linuxには、「ディストリビューション」と呼ばれるさまざまな種類があります。しかし、その中から自分の好みに合ったものを見つけるのは意外と困難です。そこで、軽量なLinuxディストリビューション「Lubuntu」のインストールメディアをカスタマイズして、自分好みのLinuxディストリビューションを手に入れましょう。
シェルスクリプトマガジン Vol.59は以下のリンク先でご購入できます。
図27 スライドショーの設定を書き換える箇所
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
Slide { Image { anchors.centerIn: parent id: image1 x:0 y:0 width: 810 height: 485 fillMode: Image.PreserveAspectFit smooth: true source: "shmag1.png" } } Slide { Image { anchors.centerIn: parent id: image2 x: 0 y: 0 width: 810 height: 485 fillMode: Image.PreserveAspectFit smooth: true source: "shmag2.png" } } } |
Node.js/Expressで楽々Webアプリ開発(Vol.59掲載)
著者:しょっさん
プログラミング言語「JavaScript」の実行環境「Node.js」と「Express」フレームワークを使って、基本となるWebアプリの開発手法を習得しましょう。最終回は、サンプルの「蔵書管理アプリケーション」の課題を解決し、「SPA」(Single Page Application)として実装します。
シェルスクリプトマガジン Vol.59は以下のリンク先でご購入できます。
図2 認証時にアクセストークンを発行する部分のプログラム
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
var express = require('express'); var router = express.Router(); const jwt = require('jsonwebtoken'); const models = require('../models'), User = models.user; var app = express(); // パスワードハッシュ化 const hashPassword = (password, salt) => { if (password) { var bcrypt = require('bcrypt'); return bcrypt.hashSync(password, salt); } else { return null; } }; // ログイン処理 router.post('/', (req, res) => { const username = req.body.email; const password = req.body.password; User.findOne({ where: { email: username } }) .then(user => { if (!user) { res .status(401) .json({ errors: { message: 'Incorrect email.' } }); } else if (hashPassword(password, user.salt) !== user.password) { res .status(401) .json({ errors: { message: 'Incorrect password.' } }); } else { const opts = { issuer: process.env.ISSUER, audience: process.env.AUDIENCE, expiresIn: process.env.EXPIRES, }; const secret = process.env.SECRET; res .status(200) .json({ 'token': jwt.sign({ id: user.id }, secret, opts) }); } }); }); |
図3 URLへのアクセスしたときのアクセストークンの検査部分のプログラム
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
const passport = require('passport'); const passportJWT = require('passport-jwt'); const ExtractJWT = passportJWT.ExtractJwt; const JWTStrategy = passportJWT.Strategy; // using authentication strategy passport.use(new JWTStrategy( { jwtFromRequest: ExtractJWT.fromAuthHeaderAsBearerToken(), issuer: process.env.ISSUER, audience: process.env.AUDIENCE, secretOrKey: process.env.SECRET }, (jwt_payload, done) => { User.findOne({ where: { id: jwt_payload.id } }) .then(user => { if (user) { done(null, user); } else { done(null, false); } }) .catch(err => { return done(err, false); }); } )); |
図4 カスタムコールバックを作成している部分のプログラム
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
// passport-jwt: custom callback // Tokenを持っていなかったり、Invarid Tokenの場合、Json形式で返答しなかったりするので、カスタムコールバックで準備する必要がある const jwt = (req, res, next) => { passport.authenticate('jwt', { session: false }, (err, user, info) => { if (err) { return next(err); } if (!user) { return res.status(401).json({ 'errors': { 'message': info || 'user unknown' } }).end(); } req.user = user; next(); })(req, res, next); }; // ルーティング: 認証が必要なURIには、jwt関数コールを追記するだけでよい app.use('/api/auth', auth); app.use('/api/books', jwt, books); |
図5 表現に合わせてJSON形式で返却する
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
// 1冊の本の情報を取得する _get_book(book_id) { return libraries.findOne({ where: { user_id: this._user_id, id: book_id }, include: [{ model: comments, required: false }] }); } // 1冊の本の詳細を表示する find(req, res) { this._get_book(req.params.id) .then(result => { res .status(200) .json(result.get()); }).catch(() => { res .status(200) .json({}); }); } |
図6 エラー画面をJSON形式に変更する
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
// catch 404 and forward to error handler app.use((req, res, next) => { var err = new Error('Not Found'); err.status = 404; next(err); }); // error handler app.use((err, req, res, next) => { // set locals, only providing error in development res.locals.message = err.message; res.locals.error = req.app.get('env') === 'development' ? err : {}; // render the error page res.status(err.status || 500); res.json({ success: false, errors: { message: err.message } }); }); |
図7 テストコードを変更する
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
describe('with Login', () => { // store a jwt token let jwt_token; beforeAll((done) => { request(app) .post('/api/auth/') .send(userCredentials) .end((err, res) => { jwt_token = res.body.token; done(); }); }); describe('GET /api/books/', () => { it('respond with REST', (done) => { request(app) .get('/api/books/') .set('Accept', 'application/json') .set('Authorization', `Bearer ${jwt_token}`) .expect('Content-Type', /json/) .expect(200, done); }); }); }); |
shell-mag ブログの 2019年3月 のアーカイブを表示しています。