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

test

Markdownを活用する(Vol.98掲載)

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

著者:藤原 由来

本連載では文書の装飾・構造付けを手軽に行える記法であるMarkdownを用いて、さまざまな文書や成果物を作成する方法を紹介します。前回に引き続き、文書変換ツール「Pandoc」の文書作成方法を紹介します。今回はPandocを用いて、EPUB形式電子書籍を作成する方法を紹介します。

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

図1 Markdown形式にした「銀河鉄道の夜」の冒頭部分

---
title: 銀河鉄道の夜
author: 宮沢賢治
---

# 一、午后《ごご》の授業

「ではみなさんは、そういうふうに川だと云《い》われたり、乳の流れたあとだと云われたりしていたこのぼんやりと白いものがほんとうは何かご承知ですか。」先生は、黒板に吊《つる》した大きな黒い星座の図の、上から下へ白くけぶった銀河帯のようなところを指《さ》しながら、みんなに問《とい》をかけました。
 カムパネルラが手をあげました。それから四五人手をあげました。ジョバンニも手をあげようとして、急いでそのままやめました。たしかにあれがみんな星だと、いつか雑誌で読んだのでしたが、このごろはジョバンニはまるで毎日教室でもねむく、本を読むひまも読む本もないので、なんだかどんなこともよくわからないという気持ちがするのでした。
 ところが先生は早くもそれを見附《みつ》けたのでした。
「ジョバンニさん。あなたはわかっているのでしょう。」
 ジョバンニは勢《いきおい》よく立ちあがりましたが、立って見るともうはっきりとそれを答えることができないのでした。ザネリが前の席からふりかえって、ジョバンニを見てくすっとわらいました。ジョバンニはもうどぎまぎしてまっ赤になってしまいました。先生がまた云いました。
(略)

図12 本文中の画像を挿入するために追記

(略)
 そして教室中はしばらく机《つくえ》の蓋《ふた》をあけたりしめたり本を重ねたりする音がいっぱいでしたがまもなくみんなはきちんと立って礼をすると教室を出ました。

![](img/Gemini_Generated_Image.png){height=90%}

# 二、活版所

 ジョバンニが学校の門を出るとき、同じ組の七八人は家へ帰らずカムパネルラをまん中にして校庭の隅《すみ》の桜《さくら》の木のところに集まっていました。それはこんやの星祭に青いあかりをこしらえて川へ流す烏瓜《からすうり》を取りに行く相談らしかったのです。
(略)

※ 「&#91」の箇所は「[」です。

図14 オプションをまとめたデフォルトファイル

input-files:
  - gingatetsudono_yoru_image.md
from: markdown+hard_line_breaks
output-file: gingatetsudono_yoru_06.epub
css: style.css
epub-cover-image: img/Copilot_Generated_Cover.png

Pythonあれこれ(Vol.98掲載)

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

著者:飯尾 淳

本連載では「Pythonを昔から使っているものの、それほど使いこなしてはいない」という筆者が、いろいろな日常業務をPythonで処理することで、立派な「蛇使い」に育つことを目指します。その過程を温かく見守ってください。皆さんと共に勉強していきましょう。第28回では、インタラクティブなグラフを手軽に作成できる可視化ライブラリ「Plotly」を使って、さまざまなグラフを作成します。

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

図2 散布図を描くコード

fig = px.scatter(df, x='sepal_width', y='sepal_length', 
                 color='species', size='petal_length')
fig.update_layout(width=800, height=600)
fig.show()

図5 花弁の長さのデータを表示できるようにしたコード

fig = px.scatter(df, x='sepal_width', y='sepal_length', 
                 color='species', size='petal_length',
                 hover_data=['petal_width'])
fig.update_layout(width=800, height=600)
fig.show()

図7 がくの幅の分布をヒストグラムで表示するコード

fig = px.histogram(df, x='sepal_width', color='species')
fig.update_layout(width=800, height=600)
fig.show()

図11 カラースキームを変更するコードの例

# 離散的なデータ向けのカラースキーム設定
px.defaults.color_discrete_sequence = px.colors.qualitative.T10
# 連続的なデータ向けのカラースキーム設定
px.defaults.color_continuous_scale = px.colors.sequential.GnBu

図13 四つのヒストグラムをまとめて表示するコードの例

from plotly.subplots import make_subplots
 
fig = make_subplots(rows=2, cols=2, shared_yaxes=True)
figs = [px.histogram(df, x='sepal_width', 
                     color='species', barmode='overlay'),
        px.histogram(df, x='sepal_length', 
                     color='species', barmode='overlay'),
        px.histogram(df, x='petal_width', 
                     color='species', barmode='overlay'),
        px.histogram(df, x='petal_length', 
                     color='species', barmode='overlay')]
i = 0
for f in figs:
  for g in f.data:
    fig.add_trace(g, row=(i//2)+1, col=(i%2)+1)
  i += 1
fig.update_layout(height=600, width=800,
                  title_text='4種類のヒストグラム',
                  barmode='overlay',
                  xaxis_title=‘sepal_width',
                  xaxis2_title='sepal_length',
                  xaxis3_title='petal_width',
                  xaxis4_title='petal_length')
fig.show()

図15 図13のコードの改良版

from plotly import graph_objects as go
from plotly.subplots import make_subplots
 
fig = make_subplots(rows=2, cols=2, shared_yaxes=True)
i = 0
for types in ['sepal_width', 'sepal_length', 
              'petal_width', 'petal_length']:
  for species, g in df.groupby('species'):
    fig.add_trace(go.Histogram(x=g[types], 
                               name=f'{species} ({types})', 
                               opacity=0.7), 
                  row=(i//2)+1, col=(i%2)+1)
  i += 1
fig.update_layout(height=600, width=800,
                  title_text='4種類のヒストグラム', 
                  barmode='overlay',
                  xaxis_title=‘sepal_width',
                  xaxis2_title='sepal_length',
                  xaxis3_title='petal_width',
                  xaxis4_title='petal_length')
fig.show()

図17 tipsデータセットを読み込んで散布図を描くコード

df = px.data.tips()
fig = px.scatter(df, x='total_bill', y='tip', 
                 color='day', size='size',
                 hover_data=['time', 'sex', 'smoker'])
fig.update_layout(width=800, height=600)
fig.show()

図19 曜日ごとのチップの額を箱ひげ図で描くコード

fig = px.box(df, x='day', y='tip', color='day', 
             category_orders={'day': ['Thur', 'Fri',
                                      'Sat', 'Sun']})
fig.update_layout(width=800, height=600)
fig.show()

図21 二つの箱ひげ図を並べて描くコード

fig = make_subplots(rows=1, cols=2)
figs = [px.box(df, x='day', y='tip', color='day', 
               category_orders={'day': ['Thur', 'Fri',
                    'Sat', 'Sun']}),
        px.box(df, x='day', y='total_bill', color='day', 
               category_orders={'day': ['Thur', 'Fri',
                                        'Sat', 'Sun']})]
i = 0
for f in figs:
  for g in f.data: fig.add_trace(g, row=1, col=(i % 2)+1)
  i += 1
fig.update_layout(width=800, height=600, 
                  title_text='tipsとtotal_billの比較',
                  xaxis_title='tips',
                  xaxis2_title='total_bill')

図23 バイオリン図を描くコード

fig = px.violin(df, x='day', y='tip', color='day',
                category_orders={'day': ['Thur', 'Fri',
                                         'Sat', 'Sun']})
fig.update_layout(width=800, height=600)
fig.show()

図24 ヒートマップを描くコード

fig = px.density_heatmap(df, x='day', y='time', z='tip', 
                         histfunc='avg',
                         category_orders={'day': ['Thur', 'Fri',
                                                  'Sat', 'Sun'],
                                          'time': ['Dinner', 'Lunch']})
fig.update_layout(width=800, height=400, 
                  title_text='曜日と時間帯のヒートマップ(tip)')
fig.show()

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

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

著者:井上竜輔

最終回では、筆者が作成したプログラミング学習アプリの概要を紹介します。このアプリは、AIなどの外部APIを活用して、オンラインで動的に問題を生成、実行、評価できるものです。安全性を考慮して、ユーザーが提出したプログラムは「Judge0 CE」というオンラインコード実行システムで、実行、評価します。

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

図7 「components/CodeEditor.tsx」ファイルのコード(抜粋)

import React from 'react';
import Editor from '@monaco-editor/react';
import { Select, MenuItem, FormControl, InputLabel, Box } from '@mui/material';
(略)
const CodeEditor: React.FC<Props> = ({ language, setLanguage, code, setCode }) => {
    return (
        <Box sx={{ mt: 2 }}>
            <FormControl sx={{ mb: 1, minWidth: 120 }}>
                <InputLabel>Language</InputLabel>
                <Select
                    value={language}
                    onChange={(e) => setLanguage(e.target.value)}
                    label="Language"
                >
                    <MenuItem value="python">Python</MenuItem>
                    <MenuItem value="cpp">C++</MenuItem>
                    <MenuItem value="javascript">JavaScript</MenuItem>
                </Select>
            </FormControl>
            <Editor
                height="40vh"
                language={language}
                value={code}
                onChange={(value) => setCode(value || '')}
                theme="vs-dark"
            />
        </Box>
    );
};

図8 handleSubmitCode関数の定義コード

const handleSubmitCode = async () => {
    if (!problem) return;
    setIsSubmitting(true);
    let finalVerdict = "AC (Accepted)";
    // サンプルケースごとにコードを実行、検証
    for (let i = 0; i < problem.samples.length; i++) {
        const sample = problem.samples[i];
        try {
            // APIを呼び出してコードを実行
            const judgeResult = await submitCode(language, code, sample.input);
            // 結果を検証
            if (judgeResult.status.id === 3) {
                const userOutput = (judgeResult.stdout || '').trim();
                const expectedOutput = sample.output.trim();
                if (userOutput !== expectedOutput) {
                    finalVerdict = "WA (Wrong Answer)";
                }
            } else {
                finalVerdict = judgeResult.status.description;
            }
        } catch (error) {
        }
        // 最初の失敗で処理を中断
        if (finalVerdict !== "AC (Accepted)") {
            break;
        }
        // APIのレート制限を避けるための待ち時間
        if (i < problem.samples.length - 1) {
            await new Promise(resolve => setTimeout(resolve, 1000));
        }
    }
    setResult(finalVerdict);
    setIsSubmitting(false);
};

図9 フロントエンドからのリクエストを受け取るコード

import type { NextApiRequest, NextApiResponse } from 'next';
import axios from 'axios';
// Judge0 CEの言語IDをマッピング
const LANGUAGE_MAP: { [key: string]: number } = {
    python: 71, // Python 3.8.1
    cpp: 54,    // C++ (GCC 9.2.0)
    javascript: 63, // JavaScript (Node.js 12.14.0)
};
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
    if (req.method !== 'POST') {
        return res.status(405).json({ error: 'メソッドはポストではありません。' });
    }
    const { language, code, stdin } = req.body;
    if (!language || !code || !stdin) {
        return res.status(400).json({ error: '要素が足りません。' });
    }
    const languageId = LANGUAGE_MAP[language];
    if (!languageId) {
        return res.status(400).json({ error: 対応していない言語です。 ${language} });
    }
(略)
}

図10 Judge0 CEのAPIにリクエストを送り、結果をフロントエンドに返すコード

const judge0ApiKey = process.env.NEXT_PUBLIC_JUDGE0_API_KEY;
const options = {
    method: 'POST',
    url: 'https://judge0-ce.p.rapidapi.com/submissions',
    params: {
        base64_encoded: 'false',
        wait: 'true', // 実行完了まで待機する
    },
    headers: {
        'x-rapidapi-host': 'judge0-ce.p.rapidapi.com',
        'x-rapidapi-key': judge0ApiKey, // 環境変数から読み込んだAPIキー
        'content-type': 'application/json',
    },
    data: {
        language_id: languageId,
        source_code: code,
        stdin: stdin,
    },
};
try {
    const submissionResponse = await axios.request(options);
    // Judge0 CEからの結果をフロントエンドに返す
    res.status(200).json(submissionResponse.data);
} catch (error) {/* エラーハンドリング*/}

図11 Geminiに送信するプロンプトの例

あなたは優秀な競技プログラミングの問題作成者です。
以下の制約に従って、新しいプログラミング問題を1つ作成してください。

# 制約
- 難易度: AtCoder ProblemsのDifficulty基準で「B」レベル
- トピック: 配列の操作、または文字列の探索
- 入力: 標準入力から受け取ること
- 出力: 標準出力へ書き出すこと
- 形式: 以下のJSONフォーマットに従うこと

# JSONフォーマット
{
  "title": "問題タイトル",
  "statement": "問題文。背景やストーリーを簡潔に記述。",
  "constraints": [
    "Nは1以上100以下の整数",
    "Sは英小文字からなる長さNの文字列"
  ],
  "input_format": "入力形式の説明",
  "output_format": "出力形式の説明",
  "samples": [
    { "input": "入力例1", "output": "出力例1" },
    { "input": "入力例2", "output": "出力例2" },
    { "input": "入力例3", "output": "出力例3" }
  ],
  "solution_code": "Pythonによる解答例のコード"
}

作りながら学ぶVue.js入門(Vol.98掲載)

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

著者:大津 真

JavaScriptフレームワークの中でも、学びやすさと柔軟さで人気を集めている「Vue.js」。本連載では、Vue.jsの基礎から実践的な使い方までを、分かりやすく丁寧に解説していきます。一緒にフロントエンド開発の楽しさを体験してみましょう。第3回では、HTMLやCSS、JavaScriptのコードをまとめた「SFCファイル」を使用したVueアプリケーションの作成について説明します。

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

図6 「index.html」ファイルのbody要素

<body>
  <div id="app"></div>
  <script type="module" src="/src/main.js"></script>
</body>

図7 「src/main.js」ファイルの内容

import { createApp } from 'vue'
import './style.css'
import App from './App.vue'

createApp(App).mount('#app')

図8 デフォルトの「src/App.vue」ファイルの内容(抜粋)

<script setup> 
import HelloWorld from './components/HelloWorld.vue'
</script>

<template>
  <div>
    <a href="https://vite.dev" target="_blank">
      <img src="/vite.svg" class="logo" alt="Vite logo" />
    </a>
    <a href="https://vuejs.org/" target="_blank">
      <img src="./assets/vue.svg" class="logo vue" alt="Vue logo" />
    </a>
  </div>
  <HelloWorld msg="Vite + Vue" />
</template>

<style scoped>
.logo {
  height: 6em;
  padding: 1.5em;
  will-change: filter;
  transition: filter 300ms;
}
(略)
</style>

図9 CDN版のVueアプリケーション「counter1.html」のコード

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="utf-8">
  <title>Vueサンプル</title>
  <style>
    h1 {
      font-size: 3em;
      background-color: #f0f0f0;
      color: green;
    }
  </style></head>
<body>
  <script type="importmap">
    {
      "imports": {
        "vue": "https://unpkg.com/vue@3/dist/vue.esm-browser.js"
      }
    }
  </script>
  <script type="module">
    import { createApp, ref } from 'vue'
    createApp({
      setup() {
        const counter = ref(0)
        function increment() { counter.value++ }
        return { counter, increment }
      }
    }).mount('#app')
  </script>
  <div id="app">
    <h1>カウント: {{ counter }}</h1>
    <button v-on:click="increment">+</button>
  </div>
</body>
</html>

図11 図9のVueアプリケーションの処理をApp.vueファイルに記述した例

<script setup>
  import { ref } from 'vue'
  const counter = ref(0)
  function increment() { counter.value++ }
</script>
<template>  
  <h1>カウント: {{ counter }}</h1>
  <button v-on:click="increment">+</button>
</template>
<style scoped>
  h1 {
    font-size: 3em;
    background-color: #f0f0f0;
    color: green;
  }
</style>

図A 「vite.config.js」ファイルの変更例

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'

// https://vite.dev/config/
export default defineConfig({
  plugins: [vue()],
  base: '/myapp/'
})

特集1 自宅で本格ネットワーク環境構築(Vol.98記載)

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

著者:麻生 二郎

スマートフォンやパソコン、スマート家電をインターネットに接続するだけなら市販のルーターを購入するだけでよいでしょう。サーバーソフト、ネットワーク関連のソフトや機器を使ってみたい,試してみたい場合、普段使うネットワークとは分離した環境を用意しておくと便利です。本特集では、そのようなネットワーク環境を構築します。

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

図16 /etc/hostapd/hostapd.confファイルの設定例

interface=wlan0
driver=nl80211
ssid=shmagtoku1
hw_mode=g
channel=6
auth_algs=1
wpa=2
wpa_passphrase=shmag202510t1
wpa_key_mgmt=WPA-PSK
rsn_pairwise=CCMP
country_code=JP
ieee80211d=1

図17 /etc/dnsmasq.d/dhcp.confファイルの設定例

interface=wlan0
dhcp-range=192.168.2.2,192.168.2.50,12h
domain-needed
bogus-priv

図18 /etc/network/interfacesファイルの設定例

auto lo
iface lo inet loopback

auto eth0
iface eth0 inet dhcp

auto wlan0
iface wlan0 inet static
address 192.168.2.1
netmask 255.255.255.0

図19 /etc/nftables.d/router.nftファイルの設定例

table inet filter {
    chain input {
        type filter hook input priority 0;
        policy accept;
    }
    chain forward {
        type filter hook forward priority 0;
        policy accept;
    }
    chain output {
        type filter hook output priority 0;
        policy accept;
    }
}

table ip nat {
    chain postrouting {
        type nat hook postrouting priority 100;
        oifname "eth0" masquerade
    }
}

図20 /etc/dnsmasq.d/host.confの設定例

dhcp-option=option:dns-server,192.168.2.1
domain=home.localnet
local=/home.localnet/

dhcp-host=00:11:22:33:44:55,web,192.168.2.100
dhcp-host=00:22:33:44:55:66,file,192.168.2.120

レポート2(Vol.98掲載)

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

著者:末安 泰三

Pythonソフトウエア財団が2025年10月1日にリリース予定のPython 3.14では、性能向上の足かせだった大域ロック(GIL)を無効化できる。それにより、マルチスレッド処理を大幅に高速化可能だ。マルチスレッド処理のコードを使って、実際にどの程度高速化するのかを検証する。

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

図1 検証に使用したPythonコード

import time, threading

N = 100000000
data = list(range(N))
def work(a, b): return sum(x*x for x in data[a:b])
def bench(threads=4):
  # シングルスレッド
  t=time.perf_counter()
  print("single:", work(0,N), 
        "in", time.perf_counter()-t)
  # マルチスレッド
  step=N//threads; res=[0]*threads; th=[]
  t=time.perf_counter()
  for i in range(threads):
    a=i*step; b=N if i==threads-1 else a+step
    th.append(
      threading.Thread(target=lambda j=i: \
                       res.__setitem__(j, work(a,b)))
    )
    th[-1].start()
  [x.join() for x in th]
  print(f"{threads} threads:", sum(res), 
        "in", time.perf_counter()-t)
bench()

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

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

004 レポート GPT-5とgpt-ossが登場
005 レポート Python 3.14が間もなくリリース  コード掲載
006 製品レビュー スキャナ「ScanSnap iX2500」
007 NEWS FLASH
008 特集1 自宅で本格ネットワーク環境構築/麻生二郎 コード掲載
016 特集2 耐量子コンピュータ暗号とPKI/林正人
022 特別企画 Red Hat Enerprise Linux 10の新機能と無料利用/麻生二郎
031 インタビュー SUSEソフトウエアソリューションズジャパン 渡辺元氏
032 作りながら学ぶVue.js入門/大津真 コード掲載
038 Raspberry Pi Pico W/WHで始める電子工作/米田聡
042 香川大学SLPからお届け!/井上竜輔 コード掲載
048 Pythonあれこれ/飯尾淳 コード掲載
058 Markdownを活用する/藤原由来 コード掲載
066 知っておきたいシェル関連技術/麻生二郎
074 Techパズル/gori.sh
075 コラム「DX人材育成支援事業の取り組み」/シェル魔人

Vol.98

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

 近年、便利で安価なクラウドサービスが登場しているため、自宅でサーバーを立てる、サーバーソフトを試すといった需要が減っています。ただし、DockerやKubernetesなどの登場により、サーバー環境の構築や運用管理がかなり複雑になっています。また、インターネットが急速に広がったため、さまざまなセキュリティ対策が求められています。これらの技術を学習・実験・テストするには、自由に使えるネットワーク環境が必要です。特集1では、さまざまなサーバーソフトやセキュリティ対策の学習・実験・テストに使えるように自宅のネットワーク環境を整備する方法を紹介しています。
 特集2では、耐量子コンピュータ暗号(PQC)と公開鍵基盤(PKI)を解説しています。今までの暗号化方式では、将来、量子コンピュータに破られる可能性が出てきています。すでに実装が始まっているPQCの仕組みや動向、公開鍵基盤(PKI)に及ぼす影響などをまとめました。
 特別企画では、2025年5月20日にリリースされた企業向けLinuxディストリビューションの新版「Red Hat Enterprise Linux 10」の新機能と、個人ユーザーにおける無料利用方法を紹介しています。
 このほか、レポートでは米OpenAI社の最新LLM(大規模言語モデル)の「GPT-5」、香川大学の学生が執筆している連載「香川大学SLPからお届け!」ではAI駆動のプログラミング学習アプリを作成方法を扱っています。
 今回も読み応え十分のシェルスクリプトマガジン Vol.98。お見逃しなく!

※記事掲載のコードはこちら。記事の補足情報はこちら

※読者アンケートはこちら

Vol.98 補足情報

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

訂正・補足情報はありません。
情報は随時更新致します。

[イベント]AI博覧会 Summer 2025開催

投稿日:2025.09.22 | カテゴリー: 記事

 AI(人工知能)に関する国内イベント「AI博覧会 Summer 2025」が2025年8月27日と28日の2日間、東京千代田区にある東京国際フォーラムで開催されました(図1)。同博覧会では、100社が出展、200以上の製品が展示されました。また、55名の登壇者による45セミナーも行われました。
 来場者は、27日が5177名、28日が5117名で合計1万294名と盛況でした。ほとんどのセミナーは、ほぼ満席で、立ち見が出るものもありました。国内におけるAIに関する関心の高さが伺えるイベントでした。

図1 AI博覧会 Summer 2025の様子

 AI博覧会 Summer 2025の開催中に、主催者であるアイスマイリー 代表取締役の板羽 晃司氏(図2)に、AI博覧会の開催経緯や狙い、今までの開催を含むAI博覧会で感じたこと、来場者の反応、改善点、今後の計画・目標などの話を聞きました(聞き手は、麻生)。

図2 アイスマイリー 代表取締役の板羽 晃司氏

――AI博覧会を始められた背景を教えてください。

 世界のグローバル企業におけるAI普及率が約7割に達しているのに対し、日本の企業の普及率はわずか1割程度にとどまっていました。この状況に強い危機感を抱いており、ここを何とかしたいという思いからAI博覧会が始まっています。
 私は、2007年にWeb制作会社であるバイタリフィに入社しました。キャンペーンサイトの企画提案、制作事業、アプリ開発、ベトナムでのオフショア開発などに携わってきました。その後、2018年にはIT技術に特化したメディア事業を新たに立ち上げ、現在はバイタリフィとアイスマイリーの2社を運営しています。
 アイスマイリーは、AIに特化したWebメディアを運営していて、AIに関する記事作成やイベント取材などを行っています。このメディア事業は、広告ビジネスだけでなく、AI関連の製品やサービスを掲載し、ユーザー企業からの資料請求をベンダー企業に提供するというビジネスも行っています。
 最初は、Web上だけでビジネスを展開していましたが、顧客であるAIベンダーやAI開発会社から「Webだけだと相手の顔が見えないため、マッチング率が低い」「リアルな場が欲しい」という声が多く寄せられました。そのような要望を受け、2024年3月にAI博覧会を立ち上げました。AI博覧会は、オンラインのメディア事業のリアル版という位置付けになります。

――AI博覧会の具体的な目標や狙いについて教えてください。

 主な狙いはビジネスの拡大ですが、それ以上に日本のAI普及率を高めることを重要視しています。2026年中に企業のAI普及率を50%にしたいという目標を掲げています。

――今回でAI博覧会の開催は5回目とのことですが、開催頻度や、他のAIイベントとの関係性について教えてください。

 開催間隔は、状況によって異なります。前回が3月開催、今回は8月開催といった感じです。同種のイベントには「AI・人工知能EXPO」があります。AI・人工知能EXPOの主催者とは、当社のWebメディアを通じて連携し、集客も支援しています。リアルイベントの開催時期については、お互いの日時や開催地が被らないように調整し、AI業界全体を盛り上げていこうという方針で協力し合っています。

――これまでの開催を振り返り、来場者からの反応や会場規模の拡大について感じたことはありますか。

 来場者からは、新しいAIの技術や製品、サービスを求めているという強い意欲を感じます。「もっと大きな会場でやってほしい」という要望が多く寄せられています。そのため、年々会場の規模を拡大してきました。初回は、御茶ノ水ソラシティで2600人から3000人規模でした。その後、渋谷、大阪、浜松町、今回は東京国際フォーラムに拡大していきました。このような規模の拡大は、出展企業と来場者双方にとってより良いマッチング機会を提供するために不可欠だと考えています。

――今回の東京国際フォーラムでの開催における手ごたえはどうですか。今後の改善点について、何か考えていますか。

 東京国際フォーラムは、会場が広く、駅からも近いため、出展企業も来場者も大変満足されています。出展企業数も増加していることから、これくらいの規模で開催できて本当に良かったと感じています。
 改善点としては、分野ごとに出展企業の明確な分類ができていないことです。今後は、来場者にとって分かりやすいように、出展内容を分類してブースの配置などを見直す必要があると感じています。

――AI博覧会は、展示会と商談会のどちらの性質が強いのでしょうか。

 さまざまなAI技術や製品、サービスが出展されているという展示会、その流れでAI導入意欲の高い来場者と出展企業が商談できる商談会という両方の側面を持っています。

――今後の計画について教えてください。

 次回は、2025年12月11日に「AIエージェント博」というAI博覧会の番外編となるミニイベントを開催する予定です。今年は、AIエージェント元年とも言われていますが、まだその使い方が分からない状況です。このイベントを通じてAIエージェントの普及に貢献したいと考えています。

――東京・大阪以外の開催予定はありますか。また、地域による来場者の違いはありますか。

 今後、名古屋での開催も予定しています。これまでの経験から、東京の来場者は情報収集に来る人が多いのに対し、関西などの来場者は、よりじっくりと話を聞いてくれる人が多いという印象です。東京は情報過多でリテラシが高い一方、その他の地域ではまだそこまでAIが浸透し切れていないと感じています。

――今後のAI博覧会の長期的な目標と運営方針についてお聞かせください。

 先に述べたように、引き続き、日本の企業のAI普及率を引き上げることを目指しています。博覧会の規模については、これ以上大きく広げすぎるとマッチング効果が薄れる可能性があるため、最適な規模感を維持し、企業間のマッチングに最大限貢献できるイベントとして作り上げていきたいと考えています。

ユニケージ通信(最終回)

投稿日:2025.09.4 | カテゴリー: 記事

一般のデータベースとの違い

 業務システム開発手法の「ユニケージ」では、データベース管理システムやフレームワークなどを使わずにシステムを構築します。システムの中身が見えやすい分、仕組みやルールを理解していないと正しい開発ができません。最終回は、一般のデータベース管理システムと、ユニケージのデータベースとの違いを紹介します。


 一般的なデータベース管理システムと、ユニケージのファイルによるデータベースには「汎用型」と「業務特化型」という大きな違いがあります。
 一般的なデータベース管理システムは、24時間365日どのようなアクセスがあってもデータの保管と管理ができなくてはいけません(図1)。そのため、汎用型と呼べるでしょう。

 一方、ユニケージのデータベースは、業務というルールを基にしてデータの保管と管理が可能であればよいといえます。例えば、伝票処理なら平日8時~17時までの登録と参照ができればよいでしょう(図2上)。他のシステムからのデータ集計なら業務終了後の20時過ぎからデータの集計・保管を開始すれ
ばよいわけです(図2下)。このようにルールに即したデータの保管と管理なら、必要がない機能を省いて最適化が比較的容易です。

 ユニケージのデータベースは業務特化型です。特に、ユニケージのデータベースには業務システムで得た長年のノウハウがいろいろと盛り込まれています。

五つのレベルで管理

 ユニケージのデータベースでは、一般的なデータベース管理システムのようにマスターのテーブルだけにデータを蓄積しているわけではありません。L1~L5という五つのレベルでデータを管理しています(図3)。

 L1は入力データ、L2は業務処理で必要な中間データ、L3はマスターとなるデータ、L4は業務で参照が必要となるデータ、L5は帳票やレポートなどの出力データです。L1~L5には重複したデータが保存されています。一見無駄のように見えますが、業務システムにおいて大きなメリットになります。

L1のデータ

 L1では、基本的に一つの入力を一つのファイルで管理しています。
 例えば、一つのファイルが1枚の伝票と考えてください。システム内に入力した伝票の状態を残しておけば、もし入力を間違えてしまってもその伝票を差し替えるだけで入力が正しい状態になります。
 また、ユニケージでは、業務処理ごとに一つのプログラム(シェルスクリプト)を用意します。間違えた伝票がどの業務処理に関係するのかが分かっていれば、対応する一つのプログラムを実行するだけでシステム全体が正しい状態になります。
 日々の業務において伝票入力を間違えたり、他のシステムから異常なデータが流れてきたりすることが意外とよくあります。入力した伝票を読み出して修正して再度登録するだけならよいですが、すでに処理済みの場合は、そのデータから作られたデータがどのテーブルのどのレコード(行)のどのカラム(列)にあるかを調べて修正しなくてはいけないなど、面倒な処理が必要になることも考えられます。先に述べたようにユニケージなら該当する入力データのファイルを差し替えてプログラムを実行するだけで済みます(図4)。

L2のデータ

 L2は前述した、業務処理で必要な中間的なデータです。このようなデータを用意しておくことで、膨大なマスターデータから必要なデータを抽出しなくてよくなります。素早く業務処理を実行するには欠かせない機能です。
 なお、ユニケージのルールでは、このような中間データが不要な場合はL2を作らなくても構いません。

L3のデータ

 L3は、データベース管理システムのマスターテーブルと同じです。しかし、L3は常に最新の状態に保つ必要はありません。L1のデータに最新のL3を形作るデータが保存されているからです。
 L3は、業務が終わった深夜などに最新の状態に作り直して構いません。業務中に最新の状態にしなければ、L3生成のためのシステム負荷を増やすことなく、システムのリソースを業務処理に集中させられます。
 なお、もし業務中に大量の入力があり、それらを加工してL3のデータとして保管する場合、夜間だけでは保管作業が終わらない可能性もあります。ユニケージでは、そのようなことがないように、ユニケージ専用のコマンド群「usp Tukubai」を用意しています。usp Tukubaiでは、インメモリーでCPU固有機能を活用しながら、高速に並べ替えや抽出ができるコマンドを多数提供しています。

L4のデータ

 L4は、前述したように業務で参照が必要となるデータです。ただし、伝票処理なら業務終了後に作成した前日のデータになります。そのため、今日入力されたL1のデータと組み合わせることがあります。
 人や他のシステムがデータを参照する場合、出力が遅いのはシステムとして致命的です。よって、膨大なマスターデータから抽出せずにL4というデータを用意しておくことは、システムの利便性の面で重要です。

L5のデータ

 業務システムで帳票やレポートの出力は大切な機能です。ユニケージでは、その出力をデータベースの中に組み込んでいます。2024年1月から義務化される電帳法などにも対応しやすくなります。

ファイル管理での工夫と優位性

 L1~L5のデータすべてがファイルです。各レベルごとに多くのファイルが存在します。ちなみに、複数のファイル内からデータを抽出するのは遅いと感じるエンジニアは多いでしょう。確かにファイルのオープンとクローズは重たい処理です。前述したusp Tukubaiのコマンドを駆使して高速に検索・抽出
を実施しても満足する性能を発揮できないかもしれません。また、膨大なデータとなれば、大きなシステム負荷も発生します。
 ユニケージではファイルの中身だけでなく、ファイル名やディレクトリ名などもデータとして利用しています(図5)。

 ファイルシステムによるファイルやディレクトリの検索には、Bツリーなどの高速検索アルゴリズムが採用されています。それを活用すれば、ファイルをオープンすることなく高速・低負荷で検索ができ、絞り込んだ後で少なくなったファイルの中身から所望するデータを抽出すればよいわけです。
 また、ファイルにはデータベースのテーブルにはないメリットがあります。それは、テキストエディタで開いて中身が読める点です。ユニケージのデータは半角スペースで区切られた表形式で保存されています。
 もし、システムから出力されたデータなどにおかしな点を見つけた場合、業務を担当している社員が入力データのファイルを直接確認して問題点を見つけて解決できる可能性があります(図6)。これは、とても早い問題解決につながります。

リレーショナルと大福帳型の両立

 もう少しデータベースの仕組みに踏み込んでみます。業務システムのバックエンドで利用されているデータベース管理システムは、一般的に「リレーショナルデータベース管理システム」です。これは、さまざまな属性情報に分けてデータを保存している複数のテーブルを、従業員番号や商品コードなどのキーとなるデータで連携した構造になります。単一のテーブルにすべての情報を登録することなく管理できるので、項目数が多いデータを扱う場合に便利です。ユニケージのデータベースもキーによるデータ連携を実施しています。
 このリレーショナルデータベース管理システムでは、データの重複がないようにするための「正規化」、データの永続性を保証する「CRUD」(クラッド)は重要な機能です。ユニケージのデータベースでは、生のデータを保存・管理する点から正規化もCRUDも実施していません。前述したようにL1~L5では重複するデータを保存しています。
 CRUDは「Create」(生成)、「Read」(抽出)、「Update」(更新)、「Delete」(削除)の頭文字を取った言葉ですが、ユニケージのデータベースにはUpdateとDeleteが存在しません。では、なぜデータの永続性が保証できるのかといえば、入力日時などでどれが更新・削除されたデータなのかの判別や抽出を可能にしているからです。
 ただし、データ量が増えてくると判別や抽出に時間がかかります。そのため、前述のTukubaiコマンド、ファイル名やディレクトリ名、L4のデータなどで高速に処理できるようにしています。
 このようにユニケージのデータベースにはリレーショナルデータベース管理システムとしての側面もありますが、BI(Business Intelligence)に使われる大福帳型データベース的な側面があります。近年、業務にかかわるさまざまなデータを分析し、経営戦略に利用している企業が数多く存在します。BIは、その業務分析に使われている言葉です。分析に使われるデータは、日々の業務で発生する情報、つまり業務システムの生データです。
 リレーショナルデータベース管理システムのように正規化したデータは分析に活用できません。ユニケージのデータベースでは、日々の業務に必要なデータを提供するとともに生データを蓄積しています。
 もし、新しい業務システムを構築するときにBIまで手が回らない、また予算が捻出できないとします。リレーショナルデータベース管理システムを採用した業務システムでは、新たにBIツールを導入してからデータを蓄積、データがある程度たまった時点で分析を開始しなくてはいけません。少なくとも1年間のデータを蓄積してからでないと正しい分析ができないでしょう。

 一方、ユニケージなら業務システム構築時に生データを蓄積できる仕組みが用意されています(図7)。新たにBIツールを導入した時点から分析を開始できます。


 東京国際フォーラムで2023年9月27日~28日に開催されたイベント「日経クロステックNEXT 東京2023」の特別座談会「検証!失敗の本質~一流企業でなぜITトラブルや品質不正が相次ぐのか」で登壇した日経クロステック編集長の榊原 康氏は、最近頻発しているシステムトラブルに対して「レジリエンス」(回復力)が重要と述べていました。
 日々の業務において、人為的トラブルはつきものです。誰でも入力データを理解でき、問題がある箇所を見つけて正しいデータの入力ファイルに差し替えて、プログラムの再実行で修復できるユニケージのデータベースは、レジリエンスが重要な業務システムに合ったものといえるかもしれません。実際に、誤った入力や入力し忘れ、他のシステムからの異常データの受け取りが発生しても入力データの欠落や誤植を修正し、明け方に発生したトラブルも業務開始までには原因究明から完全復旧までを終えている事例が多数存在します。 
 また、BI的な要素も最近話題のDX(デジタルトランスフォーメーション)では重要です。BIにより業務を可視化することは、すべての企業において必要不可欠になってきています。ユニケージならその点も考慮しながら、通常の業務システムとして開発できます。

著者:ぱぺぽ

※本記事は、シェルスクリプトマガジン Vol.87(2023年12月号)に掲載した記事からの転載です。

  • shell-mag ブログの 2025年9月 のアーカイブを表示しています。

  • -->