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

test

シェルスクリプトの書き方入門(Vol.68記載)

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

著者:大津 真

本連載ではシェルスクリプトの書き方をやさしく紹介します。対象とするシェルは、多くのLinuxディストリビューションが標準シェルとして採用する「Bash」です。第4回は、繰り返し処理を実現する制御構造を中心に解説します。

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

図1 リストにある果物名を一つずつ表示するシェルスクリプトの例

for name in メロン バナナ イチゴ ミカン
do
    echo $name
done

図2 シェルスクリプト「showArgs1.sh」の内容

#!/bin/bash
for name in $@
do
    echo $name
done

図3 シェルスクリプト「showArgs2.sh」の内容

#!/bin/bash
for name in "$@"
do
    echo $name
done

図4 シェルスクリプト「showArgs3.sh」の内容

#!/bin/bash
for name
do
    echo $name
done

図5 シェルスクリプト「colon_to_comma2.sh」の内容

#!/bin/bash
if [[ $# -eq 0 ]]; then
  echo "引数でファイルを指定してください"
  exit 1
fi
if [[ ! -f $1 ]]; then
  echo "$1が見つかりません"
  exit 1
fi
fname=$1
cp "$fname" "${fname}~"
tr ":" "," < "${fname}~" > "$fname"

図6 シェルスクリプト「colon_to_comma3.sh」の内容

#!/bin/bash
if [[ $# -eq 0 ]]; then
    echo "引数でファイルを指定してください"
    exit 1
fi
for file in $@
do
    if [[ ! -f $file ]]; then
        echo "$fileが見つかりません"
        exit 1
    fi
    fname=$file
    echo "変換中: $file"
    cp "$fname" "${fname}~"
    tr ":" "," < "${fname}~" > "$fname"
done

図7 シェルスクリプト「hello10.sh」の内容

#!/bin/bash
for i in $(seq 10)
do
    echo "$i: こんにちは"
done

図8 シェルスクリプト「case1.sh」の内容

#!/bin/bash
case $1 in
    [a-z]*)
        echo "アルファベット小文字で始まります"
        ;;
    [A-Z]*)
        echo "アルファベット大文字で始まります"
        ;;
    [0-9]*)
        echo "数字で始まります"
        ;;
    *)
        echo "その他"
esac

図9 シェルスクリプト「case2.sh」の内容

#!/bin/bash
for file
do
    case $file in
        *.txt)
            echo "テキスト: $file"
            ;;
        *.htm | *.html)
            echo "HTML: $file"
            ;;
        *)
            echo "その他: $file"
    esac
done

図10 シェルスクリプト「pat1.sh」の内容

#!/bin/bash
path="/home/o2/music/sample.test.mp3"
echo '${path#/*/} = ' ${path#/*/}
echo '${path##/*/} = ' ${path##/*/}
echo '${path%.*} = ' ${path%.*}
echo '${path%%.*} = ' ${path%%.*}

図11 シェルスクリプト「cgExt1.sh」の内容

#!/bin/bash
if [[ $# -eq 0 ]]; then
    echo "引数でファイルを指定してください"
    exit 1
fi

for file
do
    case $file in
        *.htm)
            newfile=${file%.*}.html
            echo "$file to $newfile"
            mv $file $newfile
            ;;
        *.jpeg)
            newfile=${file%.*}.jpg
            echo "$file to $newfile"
            mv $file $newfile
            ;;
    esac
done

図12 シェルスクリプト「while1.sh」の内容

#!/bin/bash
read -p "文字列? " str
while [[ -n $str ]]
do
    echo $str | tr "a-z" "A-Z"
    read -p "文字列? " str
done

図13 シェルスクリプト「while2.sh」の内容

#!/bin/bash
while true
do
    read -p "文字列? " str
    if [[ -z $str ]]; then
        break
    fi
    echo $str | tr "a-z" "A-Z"
done

図14 シェルスクリプト「cgExt2.sh」の内容

#!/bin/bash
if [[ $# -eq 0 ]]; then
    echo "引数でファイルを指定してください"
    exit 1
fi

for file
do
    if [[ ! -f $file ]]; then
        echo "${file}が見つかりません"
        continue
    fi
    case $file in
        *.htm)
            newfile=${file%.*}.html
            echo "$file to $newfile"
            mv $file $newfile
            ;;
        *.jpeg)
            newfile=${file%.*}.jpg
            echo "$file to $newfile"
            mv $file $newfile
            ;;
    esac
done

バーティカルバーの極意(Vol.68掲載)

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

著者:飯尾 淳

Twitterのトレンド分析に関する解説も、とうとう3回目に突入しました。今回は、共起ネットワークグラフの描画処理について説明します。描画にはD3.jsというグラフ描画フレームワークを使います。この描画処理では、データの受け渡し方法にちょっとした工夫をしており、それについても解説します。

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

図2 トレンド表示画面を構成するビューのソースコード

<h2>
  <%= link_to @trend.label,
              "https://twitter.com/search?q=#{@trend.label}",
              :target => '_blank' %>
</h2>
<p>
  <%= t('collected') %>
  <%= link_to l(@trend.collected, format: :long),
              "../#{@trend.collected}", :class => 'href' %>
  <%= link_to t('prev_item'), trend_path(@prev),
              :class => 'href' if @prev != nil %>
  <%= link_to t('next_item'), trend_path(@next),
              :class => 'href' if @next != nil %>
</p>
<div id="graph_canvas" data-src="<%= api_trend_path(@trend) %>">
</div> 

図5 サーバー側の処理をするRailsのコントローラのコード

class Api::TrendsController < ApplicationController
  def index
    render json: Trend.where(collected: params[:date])
  end

  def show
    l = []
    @trend = Trend.find(params[:id])
    l.push(@trend.nodes)
    @trend.nodes.each {|n|
      l.push(n.links)
    }
    render json: l
  end
end 

図6 クライアント側の処理をするコード

$(document).on('turbolinks:load', function() {
  if ($('#graph_canvas').attr('data-src') != undefined) {
    $.ajax({
      url:      $('#graph_canvas').attr('data-src'),
      dataType: 'json',
      success:  function(data) { drawGraph(data); },
      error:    function(data) { alert('error'); }
    });
  }
});
function drawGraph(data) {
    "use strict"
    var width, height, chartWidth, chartHeight, margin
    d3.select("#svg").remove()
    var svg = d3.select("#graph_canvas")
                .append("svg").attr("id", "svg")
    var chartLayer = svg.append("g").classed("chartLayer", true)
    setSize()
    drawChart(convertData(data))    
    function convertData(data) {
        var nodes = data.shift()
        var n_ary = nodes.map(function(d) {
                      d['r'] = d.freq / 4 + 15; return d })
        var l_hash = {}
        var ctr = 0
       for (var n_links of data) {
            for (var link of n_links) {
                if (l_hash[link.id] == undefined) {
                    l_hash[link.id] = { line_width: link.corr / 20, 
                                        source: nodes[ctr] }
                } else { l_hash[link.id]['target'] = nodes[ctr] }
            }
            ctr++
        }
        return { nodes: n_ary, links: Object.values(l_hash) }
    }
    function setSize() {
        width = document.querySelector("#graph_canvas").clientWidth
        height = document.querySelector("#graph_canvas").clientHeight
        margin = { top:0, left:0, bottom:0, right:0 }
        chartWidth = width - (margin.left+margin.right)
        chartHeight = height - (margin.top+margin.bottom)
        svg.attr("width", width).attr("height", height)
        chartLayer
            .attr("width", chartWidth)
            .attr("height", chartHeight)
            .attr("transform",
                  "translate("+[margin.left, margin.top]+")")
    }
    function drawChart(data) {
        var STEM_LENGTH=30
        var simulation = d3.forceSimulation()
            .force("link",
             d3.forceLink().id(function(d) { return d.index }))
            .force("collide",
             d3.forceCollide(function(d) { return d.r + STEM_LENGTH })
               .iterations(16) )
            .force("charge", d3.forceManyBody())
            .force("center",
             d3.forceCenter(chartWidth / 2, chartHeight / 2))
            .force("y", d3.forceY(0))
            .force("x", d3.forceX(0))
        var link = svg.append("g")
            .attr("class", "links")
            .selectAll("line")
            .data(data.links)
            .enter()
            .append("line")
            .attr("stroke", "brown")
            .attr("stroke-width", function(d) { return d.line_width })
        var node_label = svg.append("g")
            .attr("class", "nodes")
            .selectAll("g")
            .data(data.nodes)
            .enter().append("g")
            .call(d3.drag()
                .on("start", dragstarted)
                .on("drag", dragged)
                .on("end", dragended));
        var node = node_label.append("circle")
            .attr("r", function(d) { return d.r })
            .attr("fill", function(d) {
                return (d.freq > 60.0) ?
                        "moccasin" : (d.freq > 20.0) ?
                        "lemonchiffon" : (d.freq > 5.0) ?
                        "beige" : "lavender" });
        var label = node_label.append("text")
            .attr("text-anchor", "middle")
            .attr("font-family", "Arial")
            .attr("dy", "0.5em")
            .attr("font-size", function(d) {return d.r / 1.5; })
            .text(function(d) { return d.word; })
        var ticked = function() {
            link.attr("x1", function(d) { return d.source.x; })
                .attr("y1", function(d) { return d.source.y; })
                .attr("x2", function(d) { return d.target.x; })
                .attr("y2", function(d) { return d.target.y; });
            node_label.attr("transform",
                function(d) { return "translate("+d.x+","+d.y+")"; })
        }
        simulation.nodes(data.nodes).on("tick", ticked);
        simulation.force("link").links(data.links);
        function dragstarted(d) {
            if (!d3.event.active) {
                simulation.alphaTarget(0.1).restart();
            }
            d.fx = d.x;
            d.fy = d.y;
        }
        function dragged(d) {
            d.fx = d3.event.x;
            d.fy = d3.event.y;
        }
        function dragended(d) {
            if (!d3.event.active) simulation.alphaTarget(0);
            d.fx = null;
            d.fy = null;
        }
    }
}

Webアプリケーションの正しい作り方(Vol.68記載)

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

著者:しょっさん

ソフトウエアを正しく作るために、エンジニアたちはどんなことを知らなければならないでしょうか。実際のコードを使って、より良くしていくためのステップを考えてみましょう。第8回は、第1回で述べた「クリーンアーキテクチャ」に習って、ロジックと、フレームワークやライブラリ、その他の処理を分離します。

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

図1 経費精算申請を処理しているコード

import { Request, Response, NextFunction } from "express";
import Express from "express";
import { Expense } from "../models/expense";
const router = Express.Router();

// POST 経費の入力
router.post("/", (req: Request, res: Response, next: NextFunction) => {
  Expense.create(req.body)
    .then((result) => {
      res.status(200).json(result);
    })
    .catch((err) => {
      console.log(err);
      res.status(400).json({ id: 20002, message: err });
    });
});

図3 「User Entity」オブジェクト

class User {
  id: number;
  name: string;
  salaray: number;
}

図5 「common/index.ts」ファイル

export enum approval_status {
  minimum,
  unapproved,
  approved,
  reject,
  reimburse,
  maximum,
}

// エンティティ用のオブジェクトの基本構成
export abstract class EntityObject<T> {
  protected props: T;

  protected constructor(props: T) {
    this.props = props;
  }
}

// プリミティブ型のビジネスルール実装のための基本構成
export abstract class PrimitiveObject<T> extends EntityObject<T> {
  get value(): T {
    return this.props;
  }
}

図6 「domains/expenseEntity.ts」ファイル

import { EntityObject, approval_status, PrimitiveObject } from "../common";

export const MAX_LENGTH = 64;
export const MAX_AMOUNT = 1000000;

// 費目名のルール
class Type extends PrimitiveObject<string> {
  static create(value: string): Type {
    if (value.length > MAX_LENGTH || value.length <= 0)
      throw new Error("費目名が長すぎるか、ありません");
    return new Type(value);
  }
}

// 承認コードのルール
class Approval extends PrimitiveObject<approval_status> {
  static create(value: approval_status = approval_status.unapproved): Approval {
    if (value <= approval_status.minimum || value >= approval_status.maximum)
      throw new Error("承認コードがおかしい");
    return new Approval(value);
  }
}

// 請求金額のルール
class Amount extends PrimitiveObject<number> {
  static create(value: number): Amount {
    if (value <= 0 || value >= MAX_AMOUNT)
      throw new Error("請求金額が範囲を超えている");
    return new Amount(value);
  }
}

// 経費精算で利用されるクラスの実態
interface IExpenseProps {
  id?: number | undefined;
  user_id: string;
  user_name?: string;
  date: Date;
  type: Type;
  description?: string | null;
  approval: Approval;
  amount: Amount;
}

// オブジェクトを構成する要素
export interface IExpenseValue {
  id?: number | undefined;
  user_id: string;
  user_name?: string;
  date: Date;
  type: string;
  description?: string | null;
  approval: approval_status;
  amount: number;
}

export class ExpenseEntity extends EntityObject<IExpenseProps> {
  constructor(props: IExpenseProps) {
    super(props);
  }

  set approval(status: approval_status) {
    this.props.approval = Approval.create(status);
  }

  static create(values: IExpenseValue): ExpenseEntity {
    return new ExpenseEntity({
      id: values.id,
      user_id: values.user_id,
      user_name: values.user_name,
      date: values.date,
      type: Type.create(values.type),
      description: values.description,
      approval: Approval.create(values.approval),
      amount: Amount.create(values.amount),
    });
  }

  public read(): IExpenseValue {
    return {
      id: this.props.id,
      user_id: this.props.user_id,
      user_name: this.props.user_name,
      date: this.props.date,
      type: this.props.type.value,
      description: this.props.description,
      approval: this.props.approval.value,
      amount: this.props.amount.value,
    };
  }
}

図7 「usecases/SubmitExpense.ts」ファイル

import { IExpenseRepository } from "./IExpenseRepository";
import { ExpenseEntity, IExpenseValue } from "../domains/expenseEntity";

export class SubmitExpense {
  private _expenseRepository: IExpenseRepository;

  constructor(expenseRepository: IExpenseRepository) {
    this._expenseRepository = expenseRepository;
  }

  execute(expense: IExpenseValue) {
    const e = ExpenseEntity.create(expense);
    return this._expenseRepository.store(e);
  }
}

図8 「usecases/IExpenseRepository.ts」ファイル

import { ExpenseEntity } from "../domains/expenseEntity";

export interface IExpenseRepository {
  findAllApproved(): Promise<ExpenseEntity[]>;
  findAllRejected(): Promise<ExpenseEntity[]>;
  findUnapproval(id: string): Promise<ExpenseEntity[]>;
  updateApproval(id: number, expense: ExpenseEntity): Promise<ExpenseEntity>;
  findById(id: number): Promise<ExpenseEntity>;
  update(expense: ExpenseEntity): Promise<ExpenseEntity>;
  store(expense: ExpenseEntity): Promise<ExpenseEntity>;
}

図9 「interfaces/ExpenseController.ts」ファイル

import { SubmitExpense } from "../usecases/SubmitExpense";
import { IExpenseValue } from "../domains/expenseEntity";
import { ExpenseRepository } from "./expenseRepository";

export class ExpenseController {
  async submitExpenseController(expense: IExpenseValue): Promise<IExpenseValue> {
    const expenseRepository = new ExpenseRepository();

    try {
      const usecase = new SubmitExpense(expenseRepository);
      const result = await usecase.execute(expense);
      return result.read();
    } catch (error) {
      throw new Error(error);
    }
  }
}

図10 「interfaces/ExpenseRepository.ts」ファイル

import { Expense } from "../../models/expense";
import { approval_status } from "../common";
import { ExpenseEntity } from "../domains/expenseEntity";
import { IExpenseRepository } from "../usecases/IExpenseRepository";

export class ExpenseRepository implements IExpenseRepository {
  findAllApproved(): Promise<ExpenseEntity[]> {
    return Expense.findAll({
      where: {
        approval: approval_status.approved,
      },
    }).then((results) => {
      return results.map((value, index, array) => {
        return ExpenseEntity.create(value);
      });
    });
  }
(略)
  store(e: ExpenseEntity): Promise<ExpenseEntity> {
    return Expense.create(e.read())
      .then((result) => {
        return ExpenseEntity.create(result);
      })
      .catch((err) => {
        throw new Error("請求処理が失敗しました");
      });
  }
}

図11 「expense.ts」ファイル

// POST 経費の入力
router.post("/", (req: Request, res: Response, next: NextFunction) => {
  const e = new ExpenseController();

  e.submitExpenseController(req.body!)
    .then((result) => {
      res.status(200).json(result);
    })
    .catch((err) => {
      res.status(400).json({ id: "20201", message: err });
    });
});

図13 「index.ts」ファイル

// API
app.use("/api/auth", auth);
app.use("/api/expense", Authorization.isAuthorized, expense);
app.use(
  "/api/payment",
  Authorization.isAuthorized,
  Authorization.isAccounting,
  payment
);
app.use(
  "/api/approval",
  Authorization.isAuthorized,
  Authorization.isBoss,
  approval
);

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

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

著者:山下 賢治

初めまして。香川大学 工学研究科 修士1年の山下賢治です。今回は、JavaScriptライブラリ「React」と、API向けのクエリー言語「GraphQL」を用いて、GitHubで公開されているリポジトリの検索Webアプリケーションを作成します。リポジトリの絞り込みには、開発言語とスター数を利用します。

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

図3 「src/auth.js」ファイルに記述する内容

import { ApolloClient, createHttpLink, InMemoryCache } from '@apollo/client';
import { setContext } from '@apollo/client/link/context';

const httpLink = createHttpLink({
  uri: 'https://api.github.com/graphql',
});

const authLink = setContext(() => {
  const TOKEN = process.env.REACT_APP_TOKEN;
  return {
    headers: {
      Authorization: Bearer ${TOKEN},
    },
  };
});

export const client = new ApolloClient({
  link: authLink.concat(httpLink),
  cache: new InMemoryCache(),
});

図4 「src/index.js」ファイルに記述する内容

import React from 'react';
import ReactDOM from 'react-dom';
import { ApolloProvider } from '@apollo/client';
import { client } from './auth';
import RepoInfo from './components/RepoInfo';
import * as serviceWorker from './serviceWorker';

ReactDOM.render(
  <React.StrictMode>
    <ApolloProvider client={client}>
      <RepoInfo />
    </ApolloProvider>
  </React.StrictMode>,
  document.getElementById('root')
);

// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://bit.ly/CRA-PWA
serviceWorker.unregister();

図5 「src/graphql/index.js」ファイルに記述する内容

import { gql } from '@apollo/client';
export const SEARCH_REPO = gql
  query getData($queryString: String!) {
    search(query: $queryString, type: REPOSITORY, first: 10) {
      nodes {
        ... on Repository {
          databaseId
          nameWithOwner
          openGraphImageUrl
        }
      }
    }
  }
;

図6 「src/components/RepoInfo.jsx」ファイルに記述する内容

import React, {useState, useEffect, useCallback} from 'react';
import {useLazyQuery} from '@apollo/client';
import { SEARCH_REPO } from '../graphql';

const useRepoData = () => {
  const [getData, {loading, error, data}] = useLazyQuery(SEARCH_REPO)
  const [query, setQuery] = useState('')
  const fetchData = useCallback(() => {
    getData({
      variables: {
        queryString: query
      }
    });
  }, [getData, query]);
  useEffect(() => {
    fetchData()
  }, [fetchData])
  return [setQuery, {loading, error, data}]
}
const RepoInfo = () => {
  const [fetchData, {loading, error,data}] = useRepoData()
  const handleOnClick = () => {
    fetchData(language:python stars:>100)
  }
  if (loading) return <p>Loading Repository</p>
  if (error) return <p>Error while searching Repository</p>
  return (
    <>
      <button onClick={handleOnClick}>
        search
      </button>
      {
        data ? data.search.nodes.map(
          repo =>
            <div key={repo.databaseId}>
              <h2>{repo.nameWithOwner}</h2>
              <img src={repo.openGraphImageUrl} alt='repoImage' />
            </div>
        )
          : <></>
      }
    </>
  )
}
export default RepoInfo;

図9 変更後の「src/components/RepoInfo.jsx」ファイルの内容

import React, {useState, useEffect, useCallback} from 'react';
import {useLazyQuery} from '@apollo/client';
import { SEARCH_REPO } from '../graphql';

const useInput = initialValue => {
  const [value, set] = useState(initialValue)
  return {value, onChange: (e) => set(e.target.value)}
}
const useRepoData = () => {
  const [getData, {loading, error, data}] = useLazyQuery(SEARCH_REPO)
  const [query, setQuery] = useState('')
  const fetchData = useCallback(() => {
    getData({
      variables: {
        queryString: query
      }
    });
  }, [getData, query]);
  useEffect(() => {
    fetchData()
  }, [fetchData])
  return [setQuery, {loading, error, data}]
}
const RepoInfo = () => {
  const stars = useInput(0)
  const language = useInput('')
  const [fetchData, {loading, error,data}] = useRepoData()
  const handleOnClick = () => {
    fetchData(language:${language.value} stars:>${stars.value})
  }
  if (loading) return <p>Loading Repository</p>
  if (error) return <p>Error while searching Repository</p>
 return (
    <>
      <label>
        Language :
        <input type='text' {...language} />
      </label>
      <label>
        More than :
        <input type='text' {...stars} />
        Stars
      </label>
      <button onClick={handleOnClick}>
        search
      </button>
      {
        data ? data.search.nodes.map(
          repo =>
            <div key={repo.databaseId}>
              <h2>{repo.nameWithOwner}</h2>
              <img src={repo.openGraphImageUrl} alt='repoImage' />
            </div>
        )
          : <></>
      }
    </>
  )
}
export default RepoInfo;

レッドハットのプロダクト(Vol.68記載)

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

著者:小杉 研太

前回(第3回)に引き続き、アプリやデータの連携を実現するためのミドルウエア製品「Red Hat Integration」を紹介します。第4回はRed Hat Integrationに含まれる「Red Hat AMQ」と「Red Hat Fuse」のアップストリームとなる「Strimzi」と「Apache Camel」について触れます。

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

図17 KafkaとSlackを統合できるコード

FromKafkaToSlack.java
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.component.slack.SlackComponent;

public class FromKafkaToSlack extends RouteBuilder {

    @Override
    public void configure() throws Exception {
        final SlackComponent slackComponent = (SlackComponent) this.getContext().getComponent("slack");
        slackComponent.setWebhookUrl(Webhook URL);

        from("kafka:my-topic?brokers=my-cluster-kafka-bootstrap:9092")
            .routeId("from-kafka-to-slack")
            .to("slack:#my-kafka-project");
    }
}

Raspberry Piを100%活用しよう(Vol.68掲載)

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

著者:米田 聡

小型コンピュータボード「Raspberry Pi」(ラズパイ)向けにさまざまな拡張ボードが発売されています。その拡張ボードとラズパイを組み合わせれば、ラズパイでいろいろなことが簡単に試せます。第1回は、電子ペーパーディスプレイ搭載の拡張基板を扱います。

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

図3 電子ペーパーディスプレイに文字を表示するサンプルプログラム(text.py)

from inky import Inky
from PIL import Image, ImageFont, ImageDraw

DEFAULT_FONT = '/usr/share/fonts/truetype/fonts-japanese-gothic.ttf'
FONT_SIZE = 24
LINE_HEIGHT = 26

ink = Inky()
# 2値イメージの作成
image = Image.new('P',(ink.width, ink.height))
draw = ImageDraw.Draw(image)
font = ImageFont.truetype(DEFAULT_FONT, FONT_SIZE)

# 文字描画
draw.text((0, 0), "シェルスクリプト"  , font=font, fill=1)
draw.text((0,26), "マガジン"          , font=font, fill=1)
draw.text((0,52), "ゼロ・ワンシリーズ", font=font, fill=1)
draw.text((0,78), "電子ペパーモニタ"  , font=font, fill=1)
# セットして表示
ink.set_image(image)
ink.show()

図5 電子ペーパーディスプレイに画像を表示するサンプルプログラム(logo.py)

from inky import Inky
from PIL import Image

ink = Inky()

img = Image.open("shelllogo.png")
# サイズ変換
img = img.resize((ink.width, ink.height))
# 2値画像への変換
img = img.convert('1', dither=True)
# セットして表示
ink.set_image(img)
ink.show()

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

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

004 レポート 暗号通信のTLS1.2以前に脆弱性
005 NEWS FLASH
008 特集1 Windows 10でWSL 2を使おう/三沢友治
020 特集2 高機能CMS Drupal入門/小薗井康志
034 特別企画 量子コンピュータの基礎を知る/沼田祈史、小林有里
046 Raspberry Piを100%活用しよう/米田聡 コード掲載
049 Hello Nogyo!
050 レッドハットのプロダクト/小杉研太 コード掲載
059 中小企業手作りIT化奮戦記/菅雄一
064 法林浩之のFIGHTING TALKS/法林浩之
066 香川大学SLPからお届け!/山下賢治 コード掲載
072 RPA/桑原滝弥、イケヤシロウ
074 Webアプリの正しい作り方/しょっさん コード掲載
084 円滑コミュニケーションが世界を救う!/濱口誠一
086 バーティカルバーの極意/飯尾淳 コード掲載
092 シェルスクリプトの書き方入門/大津真 コード掲載
100 Techパズル/gori.sh
102 コラム「人生の入り口」/シェル魔人

Vol.68

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

 Linuxを使ってみたいと思っても環境を用意するのはかなりの手間です。しかし、その状況は変わりつつあります。パソコンのOSとしてデファクトスタンダード(事実上の標準)となる「Windows」を開発している米Microsoft社が、Linuxに関連する技術やソフトウエアに対して積極的に取り組んでいるからです。
 そこで、特集1では、Windows 10の標準機能となった、WindowsとLinuxを一緒に動作させるための仕組み「Windows Subsystem for Linux」(WSL)を取り上げます。2020年5月に登場したWSL 2では、Linuxとして使える機能や性能が大幅に向上しています。本特集では、このWSL 2のインストールおよび、Linuxディストリビューション「Ubuntu」の導入、GUIアプリの実行、Dockerの起動などのやり方を初心者にも分かりやすく解説しています。
 特集2では、コンテンツ管理システム(CMS)の「Drupal」を紹介しています。このDrupalは、簡単に導入してそのまま使えるだけでなく、高い拡張性によりカスタマイズして使えます。米国の政府機関、米Johnson & Johnson社などの製薬会社、米IBM社や米Red Hat社といったIT企業などのWebサイトで利用されている本格的なものです。
 特別企画では、最近話題の「量子コンピュータ」を扱いました。量子コンピュータは、現在のコンピュータと異なる仕組みや原理で動いています。簡単な計算の処理を理解するだけでも一苦労です。そこで「Minecraft」風のクイズゲームで楽しみながら、量子コンピュータについて学びましょう。
 このほか、Raspberry Pi(ラズパイ)に関する新連載、詩とアートを組み合わせたIT用語の連載も開始しました。今回も読み応え十分のシェルスクリプトマガジン Vol.68。お見逃しなく!

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

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

Vol.68 補足情報

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

特集1 Windows 10でWSL 2を使おう

WSL 2ですが、Windows 10 May 2020 Updateを適用したWindows 10 バージョン2004以降だけでなく、バージョン1909や1903にもバックポートされて使えるようになりました。詳しくは、こちらを参照してください。

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

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

  • -->