シェルスクリプトマガジン

test

シェルスクリプトマガジンvol.59 Web掲載記事まとめ

投稿日:2019.03.25 | カテゴリー: コード

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

投稿日:2019.03.25 | カテゴリー: バックナンバー

 オープンソースのソフトウエアを組み合わせて、好みの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掲載)

投稿日:2019.03.25 | カテゴリー: コード

著者:坂東勝也

ユニケージでは、小さな道具の「コマンド」をシェルスクリプトで組み合わせて、さまざまな業務システムを構築しています。本連載では、毎回あるテーマに従ってユニケージによるシェルスクリプトの記述例を分かりやすく紹介します。第6回は、仕入伝票処理システムを例に最新マスターを取得するコードをレビューします。

シェルスクリプトマガジン Vol.59は以下のリンク先でご購入できます。

図11 伝票番号で並べ替え

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 更新された仕入伝票を調べる

130 # 仕入伝票ヘッダーのLV1データの取得
131 # LV1データがあるかチェック
132 ls $lv1d/$today/SIRE_HEAD_* |
133 gyo                         > $tmp-check_data.lv1
134 ERROR_CHECK

図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のデータをマージ

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掲載)

投稿日:2019.03.25 | カテゴリー: コード

著者:岡本秀高

カンファレンス「AWS re:invent 2018」にて発表された新サービス「AWS Lambda Custom Runtimes」。これまではサポートをアナウンスした言語でしか「Lambda関数」を作成できませんでした。このサービスの登場により好きな言語で作成可能となりました。本特集では、AWS Lambda Custom Runtimesの使い方から、ランタイム(実行環境)を実際に作って利用するところまでを分かりやすく紹介します。

シェルスクリプトマガジン Vol.59は以下のリンク先でご購入できます。

図3 bootstrapファイルの内容

#!/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ファイルの内容

#!/bin/sh
function handler () {
  EVENT_DATA=$1
  echo "$EVENT_DATA" 1>&2;
  RESPONSE="Request: '$EVENT_DATA'"

  echo $RESPONSE
}

図6 template.yamlファイルの内容

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

Vol.59 補足情報

投稿日:2019.03.25 | カテゴリー: コード

訂正・補足情報はありません。

情報は随時更新致します。

香川大学SLPからお届け!(Vol.59掲載)

投稿日:2019.03.25 | カテゴリー: コード

著者:竹原一駿

USB接続のバーコードリーダーを用いてバーコード化されたデータを読み取り、その結果をサーバーに送って集計するデータ収集システムを開発しました。今回は、同システムについて紹介します。クライアントはGo、集計サーバーは主にPerlで記述しています。クライアントもサーバーもDocker環境で簡単に動かせますので、ぜひ試してみてください。

シェルスクリプトマガジン Vol.59は以下のリンク先でご購入できます。

図8 バーコードリーダーで読み取ったデータを取得する関数

func ScanStdin() string {
  stdin := bufio.NewScanner(os.Stdin)
  stdin.Scan()
  return stdin.Text()
}

図9 意見を示す数値を文字列に変換するコード

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 データを集計サーバーに送るためのコード

resp, err := http.Get(baseurl)
if err != nil {
  fmt.Println(err)
  return err
}
defer resp.Body.Close()

図11 HTTPサーバー機能を提供するMyWebServerパッケージの記述

#!/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で日本語を表示するための記述

print $cgi->header(-charset=>"utf-8"),
      $cgi->start_html(-lang => 'ja','Not found'),
      $cgi->h1('Not found'),
      $cgi->end_html;

図13 ハッシュ変数にサブルーチンのリファレンスを保存

my %dispatch = (
  '/index' => \&resp_index, # index
  '/insert' => \&resp_insert, # データの挿入
  '/operate' => \&resp_operate, # 質問番号の調整
  '/analy' => \&resp_analy, # 解析
);

図14 insertページを表示するコード

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ページを表示するコード

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掲載)

投稿日:2019.03.25 | カテゴリー: コード

著者:麻生二郎

Linuxには、「ディストリビューション」と呼ばれるさまざまな種類があります。しかし、その中から自分の好みに合ったものを見つけるのは意外と困難です。そこで、軽量なLinuxディストリビューション「Lubuntu」のインストールメディアをカスタマイズして、自分好みのLinuxディストリビューションを手に入れましょう。

シェルスクリプトマガジン Vol.59は以下のリンク先でご購入できます。

図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掲載)

投稿日:2019.03.25 | カテゴリー: コード

著者:しょっさん

プログラミング言語「JavaScript」の実行環境「Node.js」と「Express」フレームワークを使って、基本となるWebアプリの開発手法を習得しましょう。最終回は、サンプルの「蔵書管理アプリケーション」の課題を解決し、「SPA」(Single Page Application)として実装します。

シェルスクリプトマガジン Vol.59は以下のリンク先でご購入できます。

図2 認証時にアクセストークンを発行する部分のプログラム

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へのアクセスしたときのアクセストークンの検査部分のプログラム

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 カスタムコールバックを作成している部分のプログラム

// 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冊の本の情報を取得する
  _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形式に変更する

// 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 テストコードを変更する

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月 のアーカイブを表示しています。

  • -->