ミドルGPUで35億の女(RINNA AI)のローカル実行 ことはじめ

RINNA社が 5月17日 に、 rinna/japanese-gpt-neox-3.6b-instruction-sft という、対話言語 AI モデルを公開したと、プレスリリースを出した。

この AI モデルは、ご家庭にあるミドルレンジ GPU でもサクサク動く性能だったので、 AI 系に明るくない人にも導入できるように、詳しい導入手順を紹介しようと思う。

こう言うのって、登場して直ぐ情報がホットなうちに記事にすることに価値があって、こんな 1~2週間経ってから記事を書いたって誰にも届かないんだろうけど、とりあえず書いてみる。

この界隈の情報はコンテクストが高いというか、あんまりイチから導入方法を説明している記事って目にしないので、多少は需要があるかなと。

はじめに断っておくが、 Chat-GPT や 新しいBing 等と比べると数段落ちる回答精度となる。
ただ、個人がお手軽に購入できる程度の GPU を使って、完全にローカルで動かす事ができるという点が、一種の浪漫だ。

前提条件

以下のスペックを持った PC。

  • OS は Windows 10 または Windows 11
  • GeForce RTX 3060 以上、 VRAM 12GB 以上が載った NVIDIA の GPU
    • GPU ドライバはインストール済み
    • 具体的には 2023年5月現在 以下のいずれかモデル
      • GeForce RTX 3060 (12 GB)
      • GeForce RTX 3080 (12 GB)
      • GeForce RTX 3090
      • GeForce RTX 3090 Ti
      • GeForce RTX 4060 Ti (16 GB)
      • GeForce RTX 4070
      • GeForce RTX 4070 Ti
      • GeForce RTX 4080
      • GeForce RTX 4090
    • 3070 Ti, 3060 Ti, 3060 (8GB) だと VRAM が足りないので、多分動かすとエラーになる

導入手順

Python のインストール

Python と呼ばれるプログラミング言語(の、「ランタイム」と呼ばれるもの)をインストールする。

こちらのページから、 Python Releases for Windows の Latest Python 3 Release のバージョン (以下のスクショ だと 3.11.3) を確認し、下のリストからそのバージョンの "Windows Installer (64-bit)" をダウンロード。


ダウンロードしたインストーラーを実行し、デフォルトの "Use admin privileges when installing py.exe" にチェックが入っていることを確認して、インストールを実行。

Python 仮想環境の作成

先ほどインストールした Python に、追加で必要なツールをダウンロードする環境(仮想環境と呼ばれるもの)を作成する。

まず、これから環境を作成していく元のフォルダをエクスプローラで開き、アドレスバーに powershell と入力する。

すると、黒い画面が立ち上がるので、以下のコマンドを実行。

py -3 -m venv rinna-japanese-gpt-chat

すると、 rinna-japanese-gpt-chat という名前のフォルダ作成されているはず。

vscode のインストール

Visual Studio Code (vscode) という、プログラム用のテキストエディタ(コードエディタ)をインストールする。

Download for Windows からインストーラをダウンロードし実行する。

途中、インストールタスクの選択で、「エクスプローラーのディレクトリ コンテキスト メニューに [Code で開く] アクションを追加する」を追加しておく。

スクリプトファイルのダウンロード

実行するスクリプトファイルをダウンロード。

以下のページの右上らへんにある、「Download ZIP」をクリックしてダウンロードした ZIP を解凍し、中にある rinna_gradio_chat.py を、 先ほど作成された rinna-japanese-gpt-chat という名前のフォルダの中に置く。

vscode の環境のセットアップ

後半の作業を簡単にするため、 vscode の環境を整える。

先ほど作成された rinna-japanese-gpt-chat という名前のフォルダを右クリックして(Windows 11 の場合は、更に「その他のオプションを表示を」クリックして)、 「Code で開く」 を選択する。

すると、 vscode が立ち上がる (フォルダを信用しますか?的な Trust 何ちゃらの画面が出たら、 yes を選択しておく)。

Ctrl + Shift + E キーを押すと開く vscode 上の Explorer ペインで、先ほどダウンロードした .py ファイルを選択する。
すると、 "Do you want to install the recommented 'Python' Extension..." と聞かれるので、 Install を選択。

インストールが終わったら、再び vscode 上の Explorer ペインで .py ファイルを選択して Ctrl+@ キーを押し、 PowerShell ターミナルを表示させる。

このとき、表示される TERMINAL ウィンドウでは (rinna-japanese-gpt-chat) PS と先頭に表示されるようになっているはずだが、もしそれが表示されない場合は、このターミナル上で .\Scripts\Activate.ps1 と入力しておく。

Python の依存パッケージ (CUDA 対応 CUDA) の確認とインストール

Python で AI を実行するため、 AI 関連ライブラリーを、先ほど作成した Python の仮想環境内にインストールする。

まず、 PyTorch という機械学習ライブラリのページに飛ぶ。

以下のような組み合わせの選択肢を選び、表示されたコマンドを、前述の vscode のターミナル上( (rinna-japanese-gpt-chat) PS と先頭に表示される状態)で実行する。

  • PyTorch Build: Stable
  • Your OS: Windows
  • Package: Pip
  • Language: Python
  • Compute Platform: CUDA の最新版

例えば、 CUDA 11.8 の環境をインストールする場合は、以下のようなコマンドになると思われる。

pip3 install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118

その後、残りの依存ライブラリもインストールしておく。

pip3 install ipython sentencepiece transformers accelerate gradio

pip3 install の実行順を逆にすると、 CUDA に対応していない torch がインストールされてしまい、後々エラーになってしまうので注意。

CUDA ツールキットのインストール

最後に、 CUDA と呼ばれる NVIDIA の GPU 上で AI を効率よく実行するためのツールキットをインストールする。

以下の CUDA Toolkit Archive のページを開き、 前項で "Compute Platform" で選択したバージョンの CUDA Toolkit をダウンロードし、インストールする。

最新版ではなく、前項の PyTorch ライブラリのダウンロード時に選択したバージョンの CUDA をダウンロードする必要がある点に注意が必要だ。

例えば、 2023年5月現在、 CUDA Toolkit の最新版は 12.1.1 だが、 前述の PyTorch ライブラリが対応する CUDA が 11.8 までなので、 CUDA Toolkit 11.8.0 をダウンロード&インストールする。

実行手順

vscode のターミナル上( (rinna-japanese-gpt-chat) PS と先頭に表示される状態)で、以下のように実行する。

python .\rinna_gradio_chat.py

しばらくすると、ターミナルにローカルで立ち上がったサーバーの URL http://127.0.0.1:7860 が表示されるので、 Ctrl キーを押しながらクリックして、ブラウザでアクセスする。

あとはお好きなようにいじくり倒すだけだ。

前述の動画は生成部分も含めて等倍速だが、ミドルレンジの GPU でもそれなりの速度で生成できていることがわかる。

停止させたい場合は、ターミナル上で Ctrl + C キーを押そう。

補足

生成速度

ChatGPT などでは元々生成速度が速いので気になりにくいが、 英語と比べて日本語テキストの生成は遅く、同じ情報量で生成時間も利用料金も数倍かかってしまう。
その理由を雑に説明すると、「トークン」と呼ばれる学習・生成の単位が、英語では単語単位となる一方で、日本語は文字単位となっているためだ。

一方、りんなさんこと japanese-gpt-neox-3.6b では、単語間にスペースが存在しない日本語でも、ある程度単語ごとにトークン化されてから処理されるので、生成も文字単位では無く単語単位となり、処理効率が大幅に向上しているのだろうと考えられる。
ChatGPT などが存在しても、国産ないし国内向けの AI モデルが必要とされる理由の一つに、こういった背景があるようだ。

対応GPU

対応GPUを RTX30 世代以降に限っているのは、VRAM の利用を節約するために bfloat16 量子化して動かしているのだが、この bloat16 での効率的な計算に対応しているのが Ampere 以降世代の GPU となるためだ。

もし、 RTX 10, 20 の GPU で動かしてみたい場合、

https://gist.github.com/advanceboy/717fde162a6f9ccb592f04898f0aacc1#file-rinna_gradio_chat-py-L35

torch_dtype = torch.bfloat16

この部分を、搭載 VRAM に応じて torch_dtype = torch.float16torch_dtype = torch.float32 に書き換えれば上手く動くかもしれない。

CUI版

わざわざブラウザ立ちあげず CUI 上でチャットしたい場合は、上記のスクリプトの替わりに以下のスクリプトを使うと良いだろう。

YAML の細かい仕様の話

YAML の細かい仕様の話

趣味アカウントのほうでバズった… ってほどでは無いけど、割と反響があったので、 YAML に関する話をいくつか。

YAML 1.2 では Boolean として扱うのは true, false だけ

https://yaml.org/type/bool.html

続きを読む

PowerShell で中身がランダムな大きめのファイルを高速に作る

色々テストをしていると、ユニークなランダムの中身で、任意サイズの、ダミーファイルが欲しくなることがままある。

デバイスを全てファイルとして扱う Unix や Linux なら、 /dev/urandom 擬似デバイスを使って dd if=/dev/urandom of=out.bin bs=1M count=64 みたいに任意サイズでファイルをコピーして作れば良い。

しかし API ベースの Windows だとそういうわけにいかないので、何らかのスクリプトから API を呼んでやる必要がある。

呼ばせるスクリプトは、 Windows に標準で搭載されていて追加でインストール不要な、毎度おなじみ PowerShell が良いだろう。
作りたいランダムなファイルのサイズが小さければ、 PowerShell 上で以下のような雑なコードを実行すればよい。

$Size = 16KB;
[byte[]]$bin = &{foreach($i in 1..$Size) { Get-Random -Maximum 255 }};
[System.IO.File]::WriteAllBytes((Join-Path (Get-Location) .\out.bin), $bin);

しかし、 MiB オーダーを越えてくると、上記のコードでは速度が実用的ではなくなる。

と言うことで、出力速度も気にしながら、大きめなランダムなファイルを作成するコードを考えてみる。

コード

続きを読む

Docker run の起動時に任意コードを実行後 bash や ash を終了しない

この記事は、 Docker Advent Calendar 2022 の 23日目の記事だ。
空いていたので埋めちゃうよ。

この記事では、 bash や ash で任意のコードの実行後、ターミナルを終了せずに入力待ちにする方法について紹介する。
特に、 docker run の実行後に、その環境を維持したまま入力待ちにすることを考える。

例えば、 Windows コマンドプロンプトや PowerShell であれば、 CMD /K *** オプションや、 -NoExit -Command *** オプションで実現できるような内容だ。

本来なら、 docker build にてその任意コードの実行後の内容をイメージにするべきだが、 わざわざ build するまでもないとか、 build できない事情などもあるかもしれない。

ということで、 bash の場合と、 alpine などで使われる BusyBox ash それぞれについて、 docker run 実行時に任意コード実行後、ターミナルの入力待ちにする方法を紹介する。

bash の場合

bash の場合、 --rcfile オプションにて、起動時に実行するコマンドを指定できる。

ただし、 --rcfile はファイルを指定する必要があるため、 替わりにプロセス置換で実行コードを与えてやる方法をとる。

user@hostmachine:~$ docker run --rm -it debian:bullseye bash -c "bash --rcfile <(echo 'ls && export '\''FOO=B A R'\'' && MY_TIME=\$(date)')"
bin   dev  home  lib64  mnt  proc  run   srv  tmp  var
boot  etc  lib   media  opt  root  sbin  sys  usr
root@container:/# echo $FOO : $MY_TIME
B A R : Thu Dec 22 15:00:00 UTC 2022
root@container:/# 

プロセス置換はコンテナ内で実行される必要があるため、一旦 bash -c にてコンテナ内で bash 実行させ、その中で改めて --rcfile オプションを指定した bash を起動する流れとなる。

実際に実行したいコマンドは、 echo で文字列として書き出す。
上記例では、 ls && export 'FOO=B A R' && MY_TIME=$(date) と言う文字列を echo させている。

引用符が二重三重になっていて、エスケープが非常に難しくなっているので注意。

BusyBox ash

alpine 3.15 以降に含まれる BusyBox の ash であれば、意外にもプロセス置換が使える。

しかし、 --rcfile に相当するオプションは残念ながら無い。
替わりに、 ash には ENV という環境変数に記載されたファイルを ash 起動時に実行する機能がある。

これを使おう。

user@hostmachine:~$ docker run --rm -it alpine:3.15 ash -c "ash -c 'export ENV=\$1;ash' -s <(echo 'ls && export '\''FOO=B A R'\'' && MY_TIME=\$(date)')"
bin    etc    lib    mnt    proc   run    srv    tmp    var
dev    home   media  opt    root   sbin   sys    usr
/ # echo $FOO : $MY_TIME
B A R : Thu Dec 22 15:00:00 UTC 2022
/ # 

ash-c のコマンドに対して引数を与える -s オプションを使ってプロセス置換のファイルを与え、 それを $1 経由で ENV 環境変数にセット。 その状態で再度 ash を起動させれば、 bash と同様のことが行える。

ENV 環境変数に直接プロセス置換のファイルを指定せず、わざわざ一旦引数を経由させているのは、 入力を受け付ける ash プロセスが動いている間、 プロセス置換のファイルにアクセス可能にする必要があるためだ。
例えば、 export ENV=<(echo 'command');ash と実行しても、 ash 実行の段階ではプロセス置換のファイルが閉じられているので、コマンドは実行されない。

もうちょっと複雑で実用的な例

エスケープが何重にもなっていてややこしいが、書き方さえ気をつければ基本的にどんな内容でも実行可能だ。

apt パッケージマネージャーのリポジトリを書き換えた状態で、 bash を起動する方法 (Ubuntu):

docker run --rm -it ubuntu:22.04 bash -c "bash --rcfile <(echo 'sed -i -E '\''s%^(deb(-src|)\s+)https?://(archive|security)\.ubuntu\.com/ubuntu/%\1http://srv2.ftp.ne.jp/Linux/packages/ubuntu/archive/%'\'' /etc/apt/sources.list && apt update && FooBar=`date -uIs`')"

apk パッケージマネージャーのリポジトリを書き換えた状態で、 ash を起動する方法 (Alpine):

docker run --rm -it alpine:3.15 ash -c "ash -c 'export ENV=\$1;ash' -s <(echo 'sed -i -E '\''s%^https?://dl-cdn\.alpinelinux\.org/alpine/%https://ftp.udx.icscoe.jp/Linux/alpine/%'\'' /etc/apk/repositories && apk update && FooBar=`date -uIs`')"

参考: https://stackoverflow.com/questions/74094552/how-not-to-terminate-after-carried-out-commands-in-bash

GNS3 に VyOS 仮想ルーターを追加する長い道のり③ 実践編

本記事は 【アットホームな現場です】🎄★☆ネットワーク系エンジニア★☆アレコレアウトプット★☆🎄 Advent Calendar 2022 17日目の記事だ。

GNS3 という OSS のネットワークエミュレータを、 Cisco の IOS などの取得なしに、無料のライセンス内で利用できるようにしようという話。

環境導入編、イメージ準備編に続く実践編。
以前の投稿はこちら。


実際に、 GNS3 上で VyOS を使ってみよう。

GNS3 プロジェクトを作成

GNS3 を開き、適当なプロジェクトを新規作成(ないし開く)する。

平行して、 GNN3 VM が起動するまで待とう。

VyOS の起動

GNS3 GUI 上で、テンプレート一覧から VyOS のデバイスをドラッグ&ドロップして、プロジェクトに追加する。

続きを読む

プロキシ環境下で Docker Desktop から Rancher Desktop への移行する

この記事は、 Docker Advent Calendar 2022 の 22日目の記事だ。

Windows 上や mac OS 上で、手軽に Docker や Kubernetes の環境を構築するための、 Docker Desktop というツールが存在する。

この Docker Desktop は、 2021年8月31日以降、営利企業での利用は基本的に有料サブスクリプションが必須 となった。
更に、 2022年10月27日以降、 Docker Team プランにて 100人のユーザー数上限が設けられ 、それ以上の場合は Docker Business プランの契約が必要となっている。

まぁ、営利企業ならそれぐらい払えよって話なんだが、のっぴきならん理由でなかなかそうもいかない諸兄も少なくないだろう。

正直、 Business プランで Docker Desktop 以外の機能を全く使わない(ないし使えない)場合、 $21/user/month はだいぶ高い。

ということで、代替?になるかわからんが、主に Windows での実行をスコープに、 同一コンセプトの Rancher Desktop への移行を紹介してみよう。


Rancher Desktop について

Rancher Desktop は、 Docker Desktop と同様 PC の仮想環境上にコンテナデーモンを起動させ、 デスクトップから手軽に Docker CLI (with Moby/dockerd) や nerdctl (with containerd) を利用できるようにするツールだ。

かつての SUSE Linux の開発元、 現在の openSUSE プロジェクトの主要スポンサーとなっている SUSE 社 が開発している、 Rancher ソリューションの一環として、 オープンソースで開発されているプロジェクトである。

WSL2 上に dockerd や containerd デーモンを立ち上げ、 他の WSL2 ディストリビューションや ホストOS から Docker CLI や nerdctl が使えるようになっている。

Windows の Docker CLI からも、 WSL 内の Docker CLI からも、 Docker outside of Docker (DooD) が問題なくできるなど、基本的な機能はしっかりしている。

Docker Desktop と比較すると、以下のような利点がある。

  • 無料で利用できる
  • nerdctl にも対応している
  • Kubernetes のバージョンが選べる

一方で、使っているといくつかの問題が目につく。 (1.7.0 現在)

  • プロキシ環境下で イメージの pull させる公式手順がない
    • 回避手順は後述する
  • WSL 内からの CLI の実行にいくつか難がある
    • docker CLI の実行が、 2~3 テンポ遅い
    • docker run--interactive ( -i ) オプションを付けないと、標準出力がホスト側に表示されない

また、問題とまでは言わないまでも、いくつか注意点もある。

  • 付属の docker-compose が、 docker compose コマンドと同等の Compose V2 である
  • Hyper-V の仮想化に依存するため、 VPN 環境下と相性が悪い
    • 後述

Docker Desktop から Rancher Desktop への移行

Windows 版に於ける、 Docker Desktop から Rancher Desktop への移行手順をまとめておく。

1. Docker Desktop を削除

公式の FAQ では、「Docker Desktop を Rancher Desktop と一緒にインストールできる(同時に実行できない)」と書かれてはいる。
しかし、 クライアントの Docker CLI コマンドやその設定フォルダ (%USERPROFILE%\.docker\) の設定内容が衝突して問題が発生するので、 Docker Desktop を削除しておいたほうが良い。

Q: Can I have Docker Desktop installed alongside Rancher Desktop?

A: Yes, but they cannot be run at the same time as both Rancher Desktop and Docker Desktop use the same Docker socket (/var/run/docker.sock). Before starting one, be sure to stop the other first.

その手順としては、以下の通り。

  1. Docker Desktop のアンインストール
  2. %USERPROFILE%\.docker フォルダの削除(ないし、リネーム)
  3. PC の再起動

3. Rancher Desktop のインストール

Rancher Desktop 本体のインストール。

Docker Desktop を動かしていたなら問題ないと思うが、 WSL2 を動かせるだけの仮想化支援機能を持った CPU が必要だ。

  1. https://rancherdesktop.io/ から、 Windows 向けインストーラーをダウンロード
  2. インストーラーを起動し、ライセンス (Apache License, Version 2.0) に同意してインストール
    • システムにインストールするか、ユーザーごとにインストールするか選べるが、 基本的にはシステムにインストールさせる。
    • 予め WSL2 が有効にされた状態でないと、 WSL2 の有効化のために何度か OS の再起動が必要になる。
  3. 初回起動時に、 Kubernetes の要否と コンテナランタイムを選択する。
    dockerd に変更しておこう。
  4. Rancher Desktop の設定で、 WSL2 ディストリビューション内からの docker コマンドの実行を有効にしておく
  5. プロキシ環境下の場合、以下の設定を行う

    • Docker daemon 側でイメージを pull する際のプロキシーを有効にするため、 PowerShell で以下のコマンドを実行し、 Rancher Desktop を再起動させる。

      'echo ''# proxy configuration'' >> /etc/rc.conf; echo ''rc_env_allow="http_proxy http_proxy no_proxy"'' >> /etc/rc.conf; echo ''export http_proxy="http://proxy.example.com:8080/"'' >> /etc/rc.conf; echo ''export https_proxy="http://proxy.example.com:8080/"'' >> /etc/rc.conf;' | wsl --distribution rancher-desktop --exec /bin/ash --stdin
      • : not found というエラーが出るが、気にしない。
      • Rancher Desktop のバージョンアップで、 docker daemon イメージの更新が入った場合、この設定をやり直す必要がある。
    • Docker CLI で イメージのビルドやコンテナ実行時のプロキシーを有効にするため、 Docker クライアントの設定 の例に従い、 %USERPROFILE%\.docker/config.json と、 各 WSL2 ディストリビューション毎の ~/.docker/config.json に proxy の設定を追記する。
      例:

      {
        "proxies":
        {
          "default":
          {
            "httpProxy": "http://proxy.example.com:8080",
            "httpsProxy": "http://proxy.example.com:8080",
            "noProxy": "*.test.example.com,.example2.com,127.0.0.0/8"
          }
        }
      }

Rancher Desktop を起動してしばらく待てば、 Windows ホストや、 WSL2 内から docker コマンドが利用できるようになる。

トラブルシューティング

プロキシ環境下の pull 絡みの操作で connection reset by peer のエラー

pull が絡む操作で以下のようなエラーが出る場合

username@pcname:~$ docker pull alpine:3.16
Error response from daemon: Get "https://registry-1.docker.io/v2/": read tcp 172.25.205.147:57296->34.205.13.154:443: read: connection reset by peer

Rancher Desktop のバージョンアップで、 docker daemon 側のプロキシの設定がリセットされているかもしれない。

上述の 『Docker daemon 側でイメージを pull する際のプロキシーを有効』 の設定をやり直す。

pull 絡みの操作で credentials のエラー

pull が絡む操作で以下のようなエラーが出る場合

error getting credentials - err: exit status 127, out: ``

Windows 側の場合、 %USERPROFILE%.docker\config.json に "credsStore": "wincred" を付け加える

@@ -1,4 +1,5 @@
 {
+  "credsStore": "wincred",
   "proxies": {
     "default": {

WSL 側の場合、 ~/.docker/config.json に "credsStore": "pass" を付け加えてみる

@@ -1,4 +1,5 @@
 {
+  "credsStore": "pass",
   "proxies": {
     "default": {

WSL 上で docker.sock の権限エラー

/var/run/docker.sock がパーミッションエラーになってしまう場合

dial unix /var/run/docker.sock: connect: permission denied

グループの設定とパーミッションを振ってから、 WSL のシェルにログインし直す。
ref: Q: How do I fix permission denied errors when trying to use Docker on WSL?

$ sudo groupadd docker
$ sudo adduser $USER docker
$ sudo chown root:docker /var/run/docker.sock
$ sudo chmod u=rw,g=rw,o= /var/run/docker.sock

WSL 上で実行するとコンテナ内の標準出力が表示されない

WSL ではなぜか、 docker run に --interactive ( -i ) オプションを付けないと、標準出力がホスト側に表示されない。

$ docker run --rm alpine ls -la
$ docker run --rm -i alpine ls -la
total 64
drwxr-xr-x    1 root     root          4096 Dec 22 17:31 .
drwxr-xr-x    1 root     root          4096 Dec 22 17:31 ..
-rwxr-xr-x    1 root     root             0 Dec 22 17:31 .dockerenv
drwxr-xr-x    2 root     root          4096 Nov 22 13:06 bin
drwxr-xr-x    5 root     root           340 Dec 22 17:31 dev
drwxr-xr-x    1 root     root          4096 Dec 22 17:31 etc
drwxr-xr-x    2 root     root          4096 Nov 22 13:06 home
[...]

Windows 側からだと問題なく表示される。

VPN 下で通信できない

Hyper-V の仮想化に依存するため、一部の VPN 環境下と相性が悪い。
回避方法は以下の記事を参照。

docker コマンド実行時に docker daemon is not running エラー

Rancher Desktop を起動して居るのにもかかわらず、以下のようなエラーが出る場合。

error during connect: This error may indicate that the docker daemon is not running.:

とりあえずしばらく待って再試行してみよう。
Rancher Desktop UI 上で起動が完了したように見えてから、実際に docker コマンドが使えるようになるまで 十数秒~数十秒 かかることが多い。

bash で標準出力と標準エラーを 2つのコマンドに出し分ける

本記事は シェルスクリプトのカレンダー | Advent Calendar 2021 - Qiita 17日目の記事だ。

殆どカレンダーが埋まってなかったので、思いついたネタで埋めちゃえ埋めちゃえ。

今回は、 bash 系列 (bash, zsh 等) の プロセス置換 (process substitution) 機能の話だ。

このプロセス置換は POSIX 互換の機能では無いため、以降の例は ash 系列 (busybox hush (ash), dash 等) では利用できない。

bash のプロセス置換

続きを読む

置換ができない/複数ある場合に sed の終了コード0以外にする

本記事は、 シェルスクリプト Advent Calendar 2021 の 4日目 の記事だ。
そして、 且つ docker Advent Calendar 2021 4日目 の記事でもある。

どちらのカレンダーもまだまだスッカスカなので、禁じ手で埋めにかかってしまった。


Docker 公式イメージ などをベースにして、カスタムしてイメージをビルドして使おうとした際、 なるべくなら /etc/apt/apt.conf.d/ 等のように、設定用のファイルを追加して、ツール側がいい感じにマージして利用してくれるのが望ましい。
しかし、 場合によってはやむを得ず、既存のファイルを sed コマンドなどで編集せざるを得ないこともあるだろう。

カスタムイメージの Dockerfile をビルドする際に、当初は意図通り書き換えられていても、イメージが更新された結果、イメージのリビルド時にファイルの書き換えが意図しない結果となってしまう場合がある。 1

通常、 sed コマンドは、置換が発生してもしなくても、 終了コード 0 で終了する。
このため、書き換えの成否にかかわらず、 docker build 時にエラーにならないため、コンテナ実行時に初めて置換が意図しない結果だったことに気づくことがある。

そこで、sed コマンドの書き換えで適切なパターンが見つからなかった場合に 0以外の終了コードを返し、ビルド時にエラーとする方法を考える。

続きを読む

sed の ブロック {} 内で i, r, e コマンドを使うと “unmatched `{‘” とエラーになる

本記事は、 シェルスクリプト Advent Calendar 2021 の 3日目 の記事だ。
3日目が終わりそうになっても誰も書きそうにないので、最近 sed コマンドで ブロック {} を使っていたら、 "unmatched `{" というエラーにハマったので、そのメモ。


target.txt:

foo
bar
foo
bar
foo

insert.txt:

***

上記のような、2つのファイルがあったとする。

target.txt ファイルに対して、 正規表現アドレスbar から始まる行を選択し、 その後ろに r コマンド insert.txt のファイルの中身を挿入する。

するとこんな結果になる。

$ sed -e '/^bar/rinsert.txt' target.txt
foo
bar
***
foo
bar
***
foo

では、アドレス指定の後ろにブロック {} を追加し、以下のように bar が2回以上ヒットしたらエラーコード出して終了するようにしてみる。

$ sed -e '/^bar/{rinsert.txt;x;/./Q129;g}' target.txt
sed: -e expression #1, char 0: unmatched `{'
$ echo $?
1

はい、別のエラーで失敗した。
ちゃんと {} の数はマッチしているのに……

続きを読む

次の○曜日 や 来週の○曜日 を取得する JavaScript

JavaScript の標準組込みオブジェクトは、日時関連の処理が非常に貧弱だ。

次の水曜日は何月何日? とか、 来週の月曜日は何月何日? といった値を取得しようと思ったら、タイムゾーン周りの処理と合わさって、思いのほか面倒だった。

とりあえず、動くコードができたので、記事にして紹介する。

続きを読む