Quantcast
Channel: Bashタグが付けられた新着記事 - Qiita
Viewing all 2750 articles
Browse latest View live

GLSLのレイマーチングをAWKで実装したらどんな感じか

$
0
0

生成物は以下。

ray_marching.png

GLSLをAWKで実装したらどんな感じになるのか、以下の点から気になったためやってみる。

  • ピクセル単位の処理をするGLSLが、行単位の処理に特化したAWKにどう書き換わるか?
  • レイマーチングはシンプルな考えで実現されているが、GLSL以外でもシンプルに実装できるか?
  • AWKは制約が大きい言語だが、自然に実装できるか?
  • どれほどの速度が出るか

GLSLのレイマーチング自体は、

を参考にさせて頂く。

全貌として、

  • ImageMagickのテキストから画像データへの変換を利用する
  • Bashで1行にピクセルでのx、y値を指定したデータをAWKに渡す
  • AWKでは、1行に対応して、ImageMagickが解釈できる1ピクセルを出力していく
  • 1フレ分の画像データだけを出力する(動画を生成したかったが遅すぎて断念)
#!bash
size=256
{
    echo "# ImageMagick pixel enumeration: $size,$size,255,srgb"
    for y in $(seq 0 $((size - 1))); do
        for x in $(seq 0 $((size - 1))); do
            echo $x $y;
        done;
    done | awk -v size=$size -f ray_marching.awk
} | convert - ray_marching.png

AWKで気を付ける必要がある点は以下。

  • ローカル変数は、引数の最後に複数スペースを置いて引数として同様に並べる
  • returnで配列は返せない、なので第一引数を利用する
function vec2(v, x, y) {
    v[0] = x;
    v[1] = (y == "") ? x : y;
}

function vec3(v, x, y, z) {
    v[0] = x;
    v[1] = (y == "") ? x : y;
    v[2] = (z == "") ? x : z;
}

function vec3Clone(v, v0) {
    vec3(v, v0[0], v0[1], v0[2]);
}

function vec3Length(v) {
    return sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);
}

function vec3Add(v, v0) {
    vec3(v, v[0] + v0[0], v[1] + v0[1], v[2] + v0[2]);
}

function vec3Sub(v, v0) {
    vec3(v, v[0] - v0[0], v[1] - v0[1], v[2] - v0[2]);
}

function vec3Mul(v, s) {
    vec3(v, v[0] * s, v[1] * s, v[2] * s);
}

function vec3Mod(v, s) {
    vec3(v,
        v[0] % s + (v[0] < 0 ? s : 0),
        v[1] % s + (v[1] < 0 ? s : 0),
        v[2] % s + (v[2] < 0 ? s : 0));
}

function vec3Dot(v0, v1) {
    return v0[0] * v1[0] + v0[1] * v1[1] + v0[2] * v1[2];
}

function vec3Clamp(v, min, max) {
    vec3(v,
        (v[0] < min) ? 0 : ((v[0] > max) ? max : v[0]),
        (v[1] < min) ? 0 : ((v[1] > max) ? max : v[1]),
        (v[2] < min) ? 0 : ((v[2] > max) ? max : v[2]));
}

function vec3Normalize(v,  l) {
    l = vec3Length(v);
    if (l == 0.0) {
        vec3(v, 0.0);
        return;
    }
    vec3(v, v[0] / l, v[1] / l, v[2] / l);
}

function getDistance(pos, size,  p, vs) {
    vec3Clone(p, pos);
    vec3Mod(p, 6);
    vec3(vs, 2.0);
    vec3Sub(p, vs);
    return vec3Length(p) - size;
}

function getNormal(v, pos, size,  ep, v0, v1, v2, baseDistance) {
    ep = 0.0001;
    vec3(v0, pos[0] - ep, pos[1], pos[2]);
    vec3(v1, pos[0], pos[1] - ep, pos[2]);
    vec3(v2, pos[0], pos[1], pos[2] - ep);
    baseDistance = getDistance(pos, size);
    vec3(v,
        baseDistance - getDistance(v0, size),
        baseDistance - getDistance(v1, size),
        baseDistance - getDistance(v2, size));
    vec3Normalize(v);
}

BEGIN {
    vec3(lightDir, 1.0);
    vec2(resolution, size);
}

{
    vec2(fragCoord, $2, $1);
    vec2(pos,
        (fragCoord[0] * 2.0 - resolution[0]) / resolution[1],
        (fragCoord[1] * 2.0 - resolution[1]) / resolution[1]);
    vec3(col, 0);
    vec3(cameraPos, 0.0, 0.0, 10.0);
    vec3(ray, pos[0], pos[1], 0.0);
    vec3Sub(ray, cameraPos);
    vec3Normalize(ray);
    vec3Clone(cur, cameraPos);
    size = 0.75;
    for (i = 0; i < 99; ++i) {
        d = getDistance(cur, size);
        if (d < 0.0001) {
            getNormal(normal, cur, size);
            dot = vec3Dot(normal, lightDir);
            vec3(col, dot);
            vec3(ambient, 1.0, 0.7, 0.2);
            vec3Add(col, ambient);
            break;
        }
        vec3Clone(delta, ray);
        vec3Mul(delta, d);
        vec3Add(cur, delta);
    }
    vec3Clamp(col, 0.0, 1.0);
    printf "%d,%d: (%d,%d,%d)\n",
        $2, $1,
        int(col[0] * 255.99), int(col[1] * 255.99), int(col[2] * 255.99)
}

感想は以下。

  • レイマーチングはGLSL以外でもシンプルに実現できる
  • AWKの制約はあるものの、それでもある程度自然なコードで実現できる
  • 1フレ256x256を生成するだけでも数分掛かり非常に遅い

Shell Scriptで検索コマンドを作ってみました

$
0
0

この記事は、 CAMエンジニア Advent Calendar 2019 22日目の記事です。
昨日は @gucciNaさんのLottieで少し遊ぶでした。

はじめに

こんにちは。cotsupaです。
この記事では、
Shell Scriptで複数ファイルから文字列を検索するコマンド(findgrep.sh)を作る事を通して、
簡単なShell Scriptの構文を学ぶ事が出来たので、出来るだけ詳細な説明をいれて、まとめました。

とりあえず完成形はこちら

findgrep.sh
#!/bin/bashpattern=$1directory=$2name=$3#第2引数(起点ディレクトリ)が空文字列ならデフォルト値として.(カレントディレクト)を設定//②if[-z"$directory"];then
  directory='.'fi#第3引数(検索ファイルパターン)が空文字列ならデフォルト値として'*'を設定//②if[-z"$name"];then
  name='*'fi#引数がない場合はエラーメッセージを表示//③if["$#"-eq 0 ];then
  echo"Usage: `basename$0` PATTERN [PATH] [NAME_PATTERN]"exit 1
fi#検索ディレクトリが存在しない場合はエラーメッセージを表示//③if[!-d"$directory"];then
  echo"`basename"$0"`: ${directory}: No such directory" 1>&2
  exit 2
fi#対象ファイルから文字列を検索//①
find "$directory"-type f -name"$name" | xargs grep-nH"$pattern"

使用方法

「検索パターン」、「対象始点ディレクトリ」、「対象ファイル」を引数に指定する事で、
始点ディレクトリに含まれるファイル全てを対象に検索を行います。

// ./配下の'*.text'に該当するファイル内でhogeを検索
$ findgrep.sh hoge . '*.txt'
./doc/example.txt:15:hogehogehoge
./doc/example.txt:121:hoge
./README.text:185:(hoge)

スクリプトの解説

上記のスクリプト内で番号を振った順に解説していきます。
前提として、Shell Scriptでは特殊パラメータが存在し、今回は下記を使用して変数を定義しています。

特殊パラメータ意味
\$0実行しているScriptのファイルパス
\$1〜\$9引数(\$1は第一引数、$2は第二引数...)
\$#Scriptに与えた引数の数

1, 引数で指定した対象のファイルから文字列を検索

#対象ファイルから文字列を検索//①
find "$directory"-type f -name"$name" | xargs grep-nH"$pattern"

findコマンドとxargsコマンドをパイプライン演算子(|)で繋げて実行しています。

  • パイプライン演算子

    • コマンドの標準出力を別のコマンドの標準入力に繋ぐことでコマンドの連携をさせる演算子
  • findコマンド

    • 指定ディレクトリからファイルを検索し、出力するコマンド
    • -type fでファイルのみを指定、-name "$name"で第三引数のファイル名を指定しています。
  • xargsコマンド

    • xargs [実行したいコマンド]という形式で実行し、[実行したいコマンド]が標準入力から受け取ったリストを引数として実行します。
    • 今回はfindコマンドの出力結果をgrepコマンドが引数として実行しています。
  • grepコマンド

    • 引数のファイルから文字列を含む行を出力します(出力結果を絞って表示する際によく使います)
    • -nで検索結果の行数を出力し、-Hで引数にファイルが一つしか指定されなくてもも必ずファイル名を出力します。

また、"$directory"など変数をダブルクォーテーションで囲うことで変数展開しています。
(詳しくはCAMエンジニア Advent Calendar 2019 18日目shellの基礎構文

2, 引数が指定されなかった場合の条件分岐

#第2引数(起点ディレクトリ)が空文字列ならデフォルト値として.(カレントディレクト)を設定//②if[-z"$directory"];then
  directory='.'fi#第3引数(検索ファイルパターン)が空文字列ならデファルト値として'*'を設定//②if[-z"$name"];then
  name='*'fi

「対象始点ディレクトリ」、「対象ファイル」は必要な場合のみ指定できるように制御構文を使用してそれぞれデフォルトの値を設定しておきます。

  • 制御構文
    • ifthenを使用して条件分岐を記述
    • -zは文字列長が0なら真となります。

(制御構文、シングルクォートによる変数展開の詳細はCAMエンジニア Advent Calendar 2019 18日目shellの基礎構文

3, エラーメッセージの表示

#引数がない場合はエラーメッセージを表示//③if["$#"-eq 0 ];then
  echo"Usage: `basename$0` PATTERN [PATH] [NAME_PATTERN]"exit 1
fi#検索ディレクトリが存在しない場合はエラーメッセージを表示//③if[!-d"$directory"];then
  echo"`basename"$0"`: ${directory}: No such directory" 1>&2
  exit 2
fi

制御構文を使用して、引数がない場合と存在しないディレクトリ名が指定された場合にエラーメッセージを出力させます。

1>&2では出力リダイレクトの記法で標準出力(1番)を標準エラー出力(2番)と同じにする指定です。
一般的なLinuxコマンドはエラーメッセージを標準エラー出力に出力するため、このコマンドもそれに従います。そうする事で、パイプラインなどで別のコマンドに接続する際に支障がなくなります。
(他にも標準出力をファイルにリダイレクトした際でも標準エラー出力は画面に出力されるため見逃さないなど利点があります。)

また、ここではbasenameコマンドを使って、ファイル名を出力しています。

  • basenameコマンド
    • 引数のパス部分を取り除いて、ファイル名を取り出すコマンド

まとめ

これまで、ほとんどShell Scriptに触れてこなかったので、
実際に作って試してみると単純なロジックでも引っかかる部分が多かったです。
最後に、Shell Scriptを書く際に$ bash -x [コマンド]でデバックモードになるので効率よくScriptを作成できるみたいです。もっと早く知りたかったです。

参考資料
新しいLinuxの教科書 [著]三宅英明・大角祐介
shellの基礎構文

パスカルの三角形を計算するBashワンライナー

$
0
0

実行結果

パスカルの三角形を計算するワンライナー
eval echo"1""| awk '{print \$0}END{printf \"1 \"; for (i=1; i<NF; i++) {printf \"%s \", \$i+\$(i+1)} print \"1\"}'"{,,,,,,,,,}
実行結果
1
1 1
1 2 1
1 3 3 1
1 4 6 4 1
1 5 10 10 5 1
1 6 15 20 15 6 1
1 7 21 35 35 21 7 1
1 8 28 56 70 56 28 8 1
1 9 36 84 126 126 84 36 9 1
1 10 45 120 210 252 210 120 45 10 1

解説

このコードの肝となっているのが、ブレース展開というBashが持つ機能です。
ブレース展開とは、例えば次のようなものです。

ブレース展開の例
echo"test"{1..10}#> test1 test2 test3 test4 test5 test6 test7 test8 test9 test10echo{,k,s,t,n,h,m,y,r,w}a
#> a ka sa ta na ha ma ya ra wa

{n..m}(n,mは自然数)とすると、このブレースはコマンド実行時にnからmまでの自然数の数列に展開され、2つの目の例のようにコンマ区切りで要素を並べたものはそれら要素を半角スペースでつなげた文字列に展開されます。
さらにこのブレースの前後に文字列をくっつけると、数学の世界で$a(m+n)$が$a×m+a×n$になる分配法則のように、文字列が展開されます。

ところでコンマ区切りで並べた要素は二つ目の例で見たように、空の文字列でも構いません。例えば、

echo"yes"{,,,,,,}#> yes yes yes yes yes yes yes

というようになります。

このことを利用すると同じ文字列をコンマの数+1ぶん作ることができます。

パスカルの三角形の計算方法はいくつかありますが、一番初歩的なのは隣接する項同士の和を頂点から順々に計算していくという方法でしょう。この計算をawkで書いたものが次のコードです。

各段の計算
awk'{
    printf "1 ";
    for (i=1; i<NF; i++) {
        printf "%s ", $i+$(i+1);
    }
    print "1"
}'#例えば…echo"1 2 1" | awk'{上に同じ}'#> 1 3 3 1echo"1 2 1" | awk'{上に同じ}' | awk'{上に同じ}'#> 1 4 6 4 1echo"1 2 1" | awk'{上に同じ}' | awk'{上に同じ}' | awk'{上に同じ}'#> 1 5 10 10 5 1

すなわちこのawkのコードをm回適用するとパスカルの三角形のm段目の数値が計算できるというわけです。

ただ、何回も同じコードを書くのはあまりきれいじゃないですし、コピペするのも大変ですからここで先程お話ししたブレース展開を用います。

つまり、

(1)
echo"1 2 1" | awk'{上に同じ}' | awk'{上に同じ}' | awk'{上に同じ}' | awk'{上に同じ}'

と書いていたものを

(2)
echo"1 2 1"" | awk '{上に同じ}'"{,,,}

と書いて、"コマンド"{,,,}の部分をBashによって展開してもらおうという話です。

さて、この部分を展開する方法はいくつかあるのですが、いちばんスッキリと書けるのはevalコマンドを用いる方法です。evalもBashの機能の一つですが、このコマンドに文字列を食わせるとコマンドとして実行できる部分やブレース展開のように展開できる部分を展開してくれます。

たとえば、

(3)
eval echo"1 2 1"" | awk '{上に同じ}'"{,,,}

といったコードを実行すると、まずevalコマンドによりeval以降の文字列(すなわち$(2)$)が評価され、$(1)$のコードが返り、それがBashによって解釈され実行されます。

このようにブレース展開をうまく駆使すると、最初に掲げたようなワンライナーでパスカルの三角形をシェル上に表示させることができます。

Bash上で再帰的な計算や何回も同じ関数を適用するような計算を行ないたいときに、eval "コマンド列"{,,,}というイディオムを覚えておくといつか役に立つときがあるかもしれません(笑)。

関連記事・参考リンク

Config ファイルを検索して、設定を書き換える

$
0
0

JMeter にコンフィグファイルがあり、これをプロビジョニング時に書き換える必要が発生した。以前Go言語でやったことはあるが、シェルスクリプトでは、やったことがないので、方法を調べてみる。

設定変更の方式

property ファイルはよく Java で使われるが、例えば GO 言語だと、読むライブラリはあっても書くほうはなかった。プロビジョニング時で固定の変更をしたい場合きっとシェルファイルの中で実施するのが楽だろう。

ブログがあったので、自分でサンプル書いて理解してみる。jmeter.property ファイルをとってきて書き換えてみる。自分が変えたい行は

# Set this if you don't want to use SSL for RMI
#server.rmi.ssl.disable=false

このオプションを有効化したい。ブログを見てコマンドを書く。#とかはなんかエスケープが必要かなと思いつつまずそのままやると、普通に動く。

$ sed-i-e"s/#server.rmi.ssl.disable=false/server.rmi.ssl.disable=false/" jmeter.properties

これで無事ファイルが変更されたので、今回の fix としてはこれでよいが、意味を使わずに知るのは気持ちわるので、もう少し調べてみる。

ここにいいブログがあった

オリジナルの保存

-i--in-placeオプションでこのオプションによって、ファイルの特定の場所の上書き更新ができる。ちなみに、オリジナルのファイルを保存したい場合は、-iに対してsuffix を指定できる。

$ sed-i.bak -e"s/#server.rmi.ssl.disable=false/server.rmi.ssl.disable=false/" jmeter.properties
$ diff ./jmeter.properties ./jmeter.properties.bak 
334c334
< server.rmi.ssl.disable=false--->#server.rmi.ssl.disable=false

大変良い感じだ。では、-eは何だろう?これは、script だ。sed は、Manによると、ストリームエディタで、基本的なテキストのトランスフォーメーションをするとあります。

-e script, --expression=script
add the script to the commands to be executed

スクリプトというとわかりずらいですが、このトランスフォーメーションをするスクリプトです。つまり、sedのコマンドになります。コマンドの詳細はここで見れます。

今回はs/before/after/の置換コマンドです。ちなみに最後の/を忘れたらおこられました。

今回はこれでいけそうです。

rename file name sname to camel

$
0
0
current-directory
rename -f's/_(.)/\U$1/g'*.js
*directory
rename -f's/_(.)/\U$1/g' ./*/*.js

macでシェルをfishに変えてpyenvとpipが上手くできなかったときの対処法

$
0
0

概要

bashからfishに変えてから,以前まではできていた,pyenvでpythonのインストールとpipでパッケージのインストールができなくなったのでその対処法を紹介します.

事象

例えば下記のようなエラーが発生しています.

pip install selenium                        30.8s
WARNING: pip is configured with locations that require TLS/SSL, however the ssl module in Python is not available.
Collecting selenium
  WARNING: Retrying (Retry(total=4, connect=None, read=None, redirect=None, status=None)) after connection broken by 'SSLError("Can't connect to HTTPS URL because the SSL module is not available.")': /simple/selenium/
  WARNING: Retrying (Retry(total=3, connect=None, read=None, redirect=None, status=None)) after connection broken by 'SSLError("Can't connect to HTTPS URL because the SSL module is not available.")': /simple/selenium/
  WARNING: Retrying (Retry(total=2, connect=None, read=None, redirect=None, status=None)) after connection broken by 'SSLError("Can't connect to HTTPS URL because the SSL module is not available.")': /simple/selenium/
  WARNING: Retrying (Retry(total=1, connect=None, read=None, redirect=None, status=None)) after connection broken by 'SSLError("Can't connect to HTTPS URL because the SSL module is not available.")': /simple/selenium/
  WARNING: Retrying (Retry(total=0, connect=None, read=None, redirect=None, status=None)) after connection broken by 'SSLError("Can't connect to HTTPS URL because the SSL module is not available.")': /simple/selenium/
  Could not fetch URL https://pypi.org/simple/selenium/: There was a problem confirming the ssl certificate: HTTPSConnectionPool(host='pypi.org', port=443): Max retries exceeded with url: /simple/selenium/ (Caused by SSLError("Can't connect to HTTPS URL because the SSL module is not available.")) - skipping
  ERROR: Could not find a version that satisfies the requirement selenium (from versions: none)
ERROR: No matching distribution found for selenium
WARNING: pip is configured with locations that require TLS/SSL, however the ssl module in Python is not available.
Could not fetch URL https://pypi.org/simple/pip/: There was a problem confirming the ssl certificate: HTTPSConnectionPool(host='pypi.org', port=443): Max retries exceeded with url: /simple/pip/ (Caused by SSLError("Can't connect to HTTPS URL because the SSL module is not available.")) - skipping
  • opensslそもそもある?
$ which openssl
/usr/local/opt/openssl/bin/openssl

ありますね...

解決方法

opensslが失敗しているので,読み込めてない可能性があります.

  • config.fishに以下を追記
~/.config/fish/config.fish
set-x PATH /bin $PATH# なければ追加set-x PATH /usr/local/bin $PATH# なければ追加set-x PATH /usr/local/opt/openssl/bin $PATH# 一番重要
  • プロファイルの適用
$ source ~/.config/fish/config.fish
  • pythonのインストール

無事pythonをインストールするっことができました

pyenv install 3.6.2                         786ms
python-build: use openssl from homebrew
python-build: use readline from homebrew
Downloading Python-3.6.2.tar.xz...
-> https://www.python.org/ftp/python/3.6.2/Python-3.6.2.tar.xz
Installing Python-3.6.2...
python-build: use readline from homebrew
python-build: use zlib from xcode sdk
Installed Python-3.6.2 to /Users/taro/.anyenv/envs/pyenv/versions/3.6.2

Anaconda3環境構築自動化への道

$
0
0

はじめに

Anacondaをインストールするお決まりのやり方としては以下のようなものがあると思います。

AnacondaのサイトでOSに適したインストーラーのリンクを取得
・curlやwgetでサーバーに持ってくる
・実行して環境変数を設定

一回きりならいいんですが、たくさんのサーバーに対して同様にこれをやろうとすると面倒です。今回はそういう事案が発生したので、それを自動化しようと考え、それ用のBashスクリプトを実装しました。初めはPythonのスクレイピングでやろうとしたんですが、2系と3系でいろいろ面倒なことになったので、勉強もかねてBashにしました。前提としては「Python2しか入っていないサーバー(初期状態)」を対象にPython3(Anaconda3)をインストールします。

環境とかとか

・サーバー: Ubuntu 16.04.6 LTS(Xenial Xerus)
・Bash: 4.3.48(1)-release(x86_64-pc-linux-gnu)
・curl: 7.65.2 または wget: 1.17.1

とにかくLinuxでBashがあれば動きます。curlはあった方がいいので、なかったらsudo apt-get install curlで手に入れます。それでも手に入らない人は気合いで手に入れてください。

とりあえずコードを晒す

わりと愚直に書いたので結構長くなってしまいました。。。一応Githubにあるので興味があればどうぞ。

主要部分についての説明

変数定義とかとか

自分が使うOSに適したAnacondaをとってきたいので、この段階でOSの種類を変数に格納しておきます。あとでこれとインストーラーURLを参照して、適したインストーラーをcurlかwgetでとってくるように実装してます。あと将来的にはMacとかGit-for-Windowsとかでもできるようにしたいと思ってるので、現状Darwin、Linux、MINGWで条件分岐してあります。Anacondaを展開する場所はホームディレクトリの直下に設定していますが、個々人で任意の場所に設定できます。

またcurlを使ってAnacondaディストリビューションサイトURLからAnaconda3という情報を持ったhrefを取得します。ここからURLを取り出して後はcurl/wgetで簡単にGetできます。

def_vars.sh
if[`uname-s`=='Darwin'];then
    OS='MacOS'# Macにインストールする人else
    OSTYPENAME=`expr substr $(uname-s) 1 5`if[${OSTYPENAME}=='MINGW'];then
        OS='Windows'# Git-for-Windowsとかにインストールする人elif[${OSTYPENAME}=='Linux'];then
        OS='Linux'# 我らがLinuxにインストールする人else
        echo'Unknown os type: Darwin/MINGW/Linux'exit
    fi
fi

ANACONDASAVEDIR=~
ANACONDADLURL='https://www.anaconda.com/distribution/'URLHREFS=`curl "${ANACONDADLURL}" | grep'Anaconda3' | grep'href'`

どうやってインストーラーをとってくるか

先程のhrefを変数URLHREFSに格納し、これをfor文でまわしてOSに適したインストーラー情報の取得を試みます。grepを愚直に使用します。

実装の際にインストーラーURLが「href="<インストーラーURL>"・・・」のようになっていることが分かったので、単純にダブルクォーテーション部分を取得すれば、URLが手に入ることが分かります。これはsedコマンドを使うと簡単に抽出できるようです。すごいですね。

実装のイメージとしてはPythonのリストにインストーラーURLを再帰的に格納していく感じです。あとでその中身を選択肢として、どれをインストールするかを標準入力でユーザーに決めさせます。それ自動化かよと思う人もいると思います。悪いとは思っています。

get_href_url.sh
# 空配列の宣言declare-aurlarray=()for href in${URLHREFS}do
    if[`echo"${href}" | grep'href' | grep"${OS}"`];then# ダブルクォーテーションの場合で囲まれた部分を抽出url=`echo"${href}" | sed's/^.*"\(.*\)".*$/\1/'`# シングルクォーテーションの場合で囲まれた部分を抽出#url=`echo "${href}" | sed "s/^.*"\(.*\)".*$/\1/"`urlarray=("${urlarray[@]}""${url}")fi
done

動作確認

実行はbash bash_anaconda3_installer.sh、またはchmodで実行権限を付けてから、./bash_anaconda3_installer.shでできます。

実行すると、まずOSに適したインストーラー情報を取得し、どれをcurl/wgetでとってくるかを選べます。以下のスクショでは0と1番目が同じURLになっていますが、1番目が64-bit用のインストーラーらしいです。まどろっこしくて申し訳ないです。。。

ここでは1を選んでおきます。
anaconda_installer_0001.JPG
インストーラーの取得は完了したようです。ちなみに取得する際にサーバーにwgetがあればそれを使うようにしています。特に意味はありません。これも勉強なので。

取得完了後はそのインストーラーを起動してAnacondaをインストールするかどうかを選べます。よくある部分です。また小ネタですが、ここではwhileを使用して適した文字(yes/no)が入力されるまでは同じメッセージが出力されるようにしています。ちょっとこだわりですw。

yesを入力すると、Linux版のAnacondaインストーラーメッセージが続きます。なのでここについては省略します。
anaconda_installer_0002.JPG
最後にシアン色の文字で"Anaconda3 Installation Acomplished."と出力されればインストール完了です。お疲れ様でした。

現状の課題

Anacondaインストール中に以下のようなメッセージが表示され、yes/noの入力を求められます。
anaconda_installer_0003.JPG
簡単に言うと、conda initを有効にするために.bashrcに環境変数の設定をするかどうかというものです。ここでyesを入力すると、以下のようにホームディレクトリ直下にある.bashrcに環境変数が自動的に書き込まれます。
anaconda_installer_0004.JPG
自分の場合、サーバーごとに.bashrcの設定を分けている点、環境変数系は.bash_profileに書くという点でこれは面倒です。

現状はconda initの箇所はnoを選択し、環境変数は.bash_profileに手打ちするという感じになっています。まだまだ自動化までは程遠い感じです。。。今後はその部分も含め、完全な自動化ができるよう実装していく予定です。

最後に

今まではGo言語やPythonでスクレイピングをやってきましたが、シェルスクリプトの方が自分の用途に合った処理を記述しやすい感じがありました。これを機にもう少し勉強してみます(笑)

ImageMagickでかんたんアスキーアート

$
0
0

Imagemagickで使えるファイルフォーマットを調べていたら…

Webサイトを作るとき簡単なアタリ画像1を作ったり、画像ファイルのプロパティを調べたり、写真のリサイズや色の調整など複雑な処理を大量の写真に対して一気にしたりなど、パソコンで画像をいじるときにコマンドラインから使える画像処理ソフトImageMagick2は便利で重宝しています。
このソフトではいろんな形式の画像ファイルを読み書きすることができますが、jpegやpngの他には具体的にどんな形式が使えるのだろうと思って調べていたら、ちょっと面白いものを発見しました。

ImageMagickで使える画像フォーマットを調べるには、次のコマンドを使います。-listオプションは引数に設定した情報を列挙します。

bash
convert -list format
   Format  Module    Mode  Description
-------------------------------------------------------------------------------
      3FR  DNG       r--   Hasselblad CFV/H3D39II
      AAI* AAI       rw+   AAI Dune image
       AI  PDF       rw-   Adobe Illustrator CS2
      ART* ART       rw-   PFS: 1st Publisher Clip Art
      ARW  DNG       r--   Sony Alpha Raw Image Format
      AVI  MPEG      r--   Microsoft Audio/Visual Interleaved
      AVS* AVS       rw+   AVS X image
      BGR* BGR       rw+   Raw blue, green, and red samples
     BGRA* BGR       rw+   Raw blue, green, red, and alpha samples
     BGRO* BGR       rw+   Raw blue, green, red, and opacity samples
      BIE* JBIG      rw-   Joint Bi-level Image experts Group interchange format (2.1)
      BMP* BMP       rw-   Microsoft Windows bitmap image
     BMP2* BMP       -w-   Microsoft Windows bitmap image (V2)
     BMP3* BMP       -w-   Microsoft Windows bitmap image (V3)
      BRF* BRAILLE   -w-   BRF ASCII Braille format
      CAL* CALS      rw-   Continuous Acquisition and Life-cycle Support Type 1
           Specified in MIL-R-28002 and MIL-PRF-28002
     CALS* CALS      rw-   Continuous Acquisition and Life-cycle Support Type 1
           Specified in MIL-R-28002 and MIL-PRF-28002
   CANVAS* XC        r--   Constant image uniform color
  CAPTION* CAPTION   r--   Caption
      CIN* CIN       rw-   Cineon Image File
      CIP* CIP       -w-   Cisco IP phone image format
     CLIP* CLIP      rw+   Image Clip Mask
     CMYK* CMYK      rw+   Raw cyan, magenta, yellow, and black samples
    CMYKA* CMYK      rw+   Raw cyan, magenta, yellow, black, and alpha samples
      CR2  DNG       r--   Canon Digital Camera Raw Image Format
      CRW  DNG       r--   Canon Digital Camera Raw Image Format
      CUR* ICON      rw-   Microsoft icon
      CUT* CUT       r--   DR Halo
     DATA* INLINE    rw+   Base64-encoded inline images
      DCM* DCM       r--   Digital Imaging and Communications in Medicine image
           DICOM is used by the medical community for images like X-rays.  The
           specification, "Digital Imaging and Communications in Medicine
           (DICOM)", is available at http://medical.nema.org/.  In particular,
           see part 5 which describes the image encoding (RLE, JPEG, JPEG-LS),
           and supplement 61 which adds JPEG-2000 encoding.
      DCR  DNG       r--   Kodak Digital Camera Raw Image File
      DCX* PCX       rw+   ZSoft IBM PC multi-page Paintbrush
      DDS* DDS       rw+   Microsoft DirectDraw Surface
    DFONT* TTF       r--   Multi-face font package (Freetype 2.8.1)
      DNG  DNG       r--   Digital Negative
      DPX* DPX       rw-   SMPTE 268M-2003 (DPX 2.0)
           Digital Moving Picture Exchange Bitmap, Version 2.0.
           See SMPTE 268M-2003 specification at http://www.smtpe.org

     DXT1* DDS       rw+   Microsoft DirectDraw Surface
     DXT5* DDS       rw+   Microsoft DirectDraw Surface
     EPDF  PDF       rw-   Encapsulated Portable Document Format
      EPI  PS        rw-   Encapsulated PostScript Interchange format
      EPS  PS        rw-   Encapsulated PostScript
     EPS2* PS2       -w-   Level II Encapsulated PostScript
     EPS3* PS3       -w+   Level III Encapsulated PostScript
     EPSF  PS        rw-   Encapsulated PostScript
     EPSI  PS        rw-   Encapsulated PostScript Interchange format
      EPT  EPT       rw-   Encapsulated PostScript with TIFF preview
     EPT2  EPT       rw-   Encapsulated PostScript Level II with TIFF preview
     EPT3  EPT       rw+   Encapsulated PostScript Level III with TIFF preview
      ERF  DNG       r--   Epson Raw Format
      FAX* FAX       rw+   Group 3 FAX
           FAX machines use non-square pixels which are 1.5 times wider than
           they are tall but computer displays use square pixels, therefore
           FAX images may appear to be narrow unless they are explicitly
           resized using a geometry of "150x100%".

     FILE* URL       r--   Uniform Resource Locator (file://)
     FITS* FITS      rw-   Flexible Image Transport System
  FRACTAL* PLASMA    r--   Plasma fractal image
      FTP* URL       r--   Uniform Resource Locator (ftp://)
      FTS* FITS      rw-   Flexible Image Transport System
       G3* FAX       rw-   Group 3 FAX
       G4* FAX       rw-   Group 4 FAX
      GIF* GIF       rw+   CompuServe graphics interchange format
    GIF87* GIF       rw-   CompuServe graphics interchange format (version 87a)
 GRADIENT* GRADIENT  r--   Gradual linear passing from one shade to another
     GRAY* GRAY      rw+   Raw gray samples
   GROUP4* TIFF      rw-   Raw CCITT Group4
        H* MAGICK    -w-   Image expressed as a 'C/C++' char array
     HALD* HALD      r--   Identity Hald color lookup table image
      HDR* HDR       rw+   Radiance RGBE image format
HISTOGRAM* HISTOGRAM -w-   Histogram of the image
      HRZ* HRZ       rw-   Slow Scan TeleVision
      HTM* HTML      -w-   Hypertext Markup Language and a client-side image map
     HTML* HTML      -w-   Hypertext Markup Language and a client-side image map
     HTTP* URL       r--   Uniform Resource Locator (http://)
    HTTPS* URL       ---   Uniform Resource Locator (https://)
      ICB* TGA       rw-   Truevision Targa image
      ICO* ICON      rw+   Microsoft icon
     ICON* ICON      rw-   Microsoft icon
      IIQ  DNG       r--   Phase One Raw Image Format
     INFO  INFO      -w+   The image format and characteristics
   INLINE* INLINE    rw+   Base64-encoded inline images
      IPL* IPL       rw+   IPL Image Sequence
   ISOBRL* BRAILLE   -w-   ISO/TR 11548-1 format
  ISOBRL6* BRAILLE   -w-   ISO/TR 11548-1 format 6dot
      JBG* JBIG      rw+   Joint Bi-level Image experts Group interchange format (2.1)
     JBIG* JBIG      rw+   Joint Bi-level Image experts Group interchange format (2.1)
      JNG* PNG       rw-   JPEG Network Graphics
           See http://www.libpng.org/pub/mng/ for details about the JNG
           format.
      JNX* JNX       r--   Garmin tile format
      JPE* JPEG      rw-   Joint Photographic Experts Group JFIF format (80)
     JPEG* JPEG      rw-   Joint Photographic Experts Group JFIF format (80)
      JPG* JPEG      rw-   Joint Photographic Experts Group JFIF format (80)
      JPS* JPEG      rw-   Joint Photographic Experts Group JFIF format (80)
     JSON  JSON      -w+   The image format and characteristics
      K25  DNG       r--   Kodak Digital Camera Raw Image Format
      KDC  DNG       r--   Kodak Digital Camera Raw Image Format
    LABEL* LABEL     r--   Image label
      M2V  MPEG      rw+   MPEG Video Stream
      M4V  MPEG      rw+   Raw MPEG-4 Video
      MAC* MAC       r--   MAC Paint
   MAGICK* MAGICK    rw-   Predefined Magick Image (LOGO, ROSE, etc.); output same as 'H'
      MAP* MAP       rw-   Colormap intensities and indices
     MASK* MASK      rw+   Image Clip Mask
      MAT  MAT       rw+   MATLAB level 5 image format
    MATTE* MATTE     -w+   MATTE format
      MEF  DNG       r--   Mamiya Raw Image File
     MIFF* MIFF      rw+   Magick Image File Format
      MKV  MPEG      rw+   Multimedia Container
      MNG* PNG       rw+   Multiple-image Network Graphics (libpng 1.6.34)
           See http://www.libpng.org/pub/mng/ for details about the MNG
           format.
     MONO* MONO      rw-   Raw bi-level bitmap
      MOV  MPEG      rw+   MPEG Video Stream
      MP4  MPEG      rw+   MPEG-4 Video Stream
      MPC* MPC       rw+   Magick Persistent Cache image format
     MPEG  MPEG      rw+   MPEG Video Stream
      MPG  MPEG      rw+   MPEG Video Stream
      MRW  DNG       r--   Sony (Minolta) Raw Image File
      MSL* MSL       rw+   Magick Scripting Language
      MTV* MTV       rw+   MTV Raytracing image format
      MVG* MVG       rw-   Magick Vector Graphics
      NEF  DNG       r--   Nikon Digital SLR Camera Raw Image File
      NRW  DNG       r--   Nikon Digital SLR Camera Raw Image File
     NULL* NULL      rw-   Constant image of uniform color
      ORF  DNG       r--   Olympus Digital Camera Raw Image File
      OTB* OTB       rw-   On-the-air bitmap
      OTF* TTF       r--   Open Type font (Freetype 2.8.1)
      PAL* UYVY      rw-   16bit/pixel interleaved YUV
     PALM* PALM      rw+   Palm pixmap
      PAM* PNM       rw+   Common 2-dimensional bitmap format
  PATTERN* PATTERN   r--   Predefined pattern
      PBM* PNM       rw+   Portable bitmap format (black and white)
      PCD* PCD       rw-   Photo CD
     PCDS* PCD       rw-   Photo CD
      PCL  PCL       rw+   Printer Control Language
      PCT* PICT      rw-   Apple Macintosh QuickDraw/PICT
      PCX* PCX       rw-   ZSoft IBM PC Paintbrush
      PDB* PDB       rw+   Palm Database ImageViewer Format
      PDF  PDF       rw+   Portable Document Format
     PDFA  PDF       rw+   Portable Document Archive Format
      PEF  DNG       r--   Pentax Electronic File
      PES* PES       r--   Embrid Embroidery Format
      PFA* TTF       r--   Postscript Type 1 font (ASCII) (Freetype 2.8.1)
      PFB* TTF       r--   Postscript Type 1 font (binary) (Freetype 2.8.1)
      PFM* PFM       rw+   Portable float format
      PGM* PNM       rw+   Portable graymap format (gray scale)
    PICON* XPM       rw-   Personal Icon
     PICT* PICT      rw-   Apple Macintosh QuickDraw/PICT
      PIX* PIX       r--   Alias/Wavefront RLE image format
    PJPEG* JPEG      rw-   Joint Photographic Experts Group JFIF format (80)
   PLASMA* PLASMA    r--   Plasma fractal image
      PNG* PNG       rw-   Portable Network Graphics (libpng 1.6.34)
           See http://www.libpng.org/ for details about the PNG format.
    PNG00* PNG       rw-   PNG inheriting bit-depth, color-type from original if possible
    PNG24* PNG       rw-   opaque or binary transparent 24-bit RGB (zlib 1.2.11)
    PNG32* PNG       rw-   opaque or transparent 32-bit RGBA
    PNG48* PNG       rw-   opaque or binary transparent 48-bit RGB
    PNG64* PNG       rw-   opaque or transparent 64-bit RGBA
     PNG8* PNG       rw-   8-bit indexed with optional binary transparency
      PNM* PNM       rw+   Portable anymap
      PPM* PNM       rw+   Portable pixmap format (color)
  PREVIEW* PREVIEW   -w-   Show a preview an image enhancement, effect, or f/x
       PS  PS        rw+   PostScript
      PS2* PS2       -w+   Level II PostScript
      PS3* PS3       -w+   Level III PostScript
      PSB* PSD       rw+   Adobe Large Document Format
      PSD* PSD       rw+   Adobe Photoshop bitmap
     PTIF* TIFF      rw+   Pyramid encoded TIFF
      PWP* PWP       r--   Seattle Film Works
RADIAL-GRADIENT* GRADIENT  r--   Gradual radial passing from one shade to another
      RAF  DNG       r--   Fuji CCD-RAW Graphic File
      RAS* SUN       rw+   SUN Rasterfile
      RAW  DNG       r--   Raw
      RGB* RGB       rw+   Raw red, green, and blue samples
     RGBA* RGB       rw+   Raw red, green, blue, and alpha samples
     RGBO* RGB       rw+   Raw red, green, blue, and opacity samples
      RGF* RGF       rw-   LEGO Mindstorms EV3 Robot Graphic Format (black and white)
      RLA* RLA       r--   Alias/Wavefront image
      RLE* RLE       r--   Utah Run length encoded image
      RMF  DNG       r--   Raw Media Format
      RW2  DNG       r--   Panasonic Lumix Raw Image
      SCR* SCR       r--   ZX-Spectrum SCREEN$
      SCT* SCT       r--   Scitex HandShake
      SFW* SFW       r--   Seattle Film Works
      SGI* SGI       rw+   Irix RGB image
    SHTML* HTML      -w-   Hypertext Markup Language and a client-side image map
      SIX* SIX       rw-   DEC SIXEL Graphics Format
    SIXEL* SIXEL     rw-   DEC SIXEL Graphics Format
SPARSE-COLOR* TXT       -w+   Sparse Color
      SR2  DNG       r--   Sony Raw Format 2
      SRF  DNG       r--   Sony Raw Format
  STEGANO* STEGANO   r--   Steganographic image
      SUN* SUN       rw+   SUN Rasterfile
     TEXT* TXT       rw+   Text
      TGA* TGA       rw-   Truevision Targa image
THUMBNAIL* THUMBNAIL -w+   EXIF Profile Thumbnail
     TIFF* TIFF      rw+   Tagged Image File Format (LIBTIFF, Version 4.0.9)
   TIFF64* TIFF      rw-   Tagged Image File Format (64-bit) (LIBTIFF, Version 4.0.9)
     TILE* TILE      r--   Tile image with a texture
      TIM* TIM       r--   PSX TIM
      TTC* TTF       r--   TrueType font collection (Freetype 2.8.1)
      TTF* TTF       r--   TrueType font (Freetype 2.8.1)
      TXT* TXT       rw+   Text
     UBRL* BRAILLE   -w-   Unicode Text format
    UBRL6* BRAILLE   -w-   Unicode Text format 6dot
      UIL* UIL       -w-   X-Motif UIL table
     UYVY* UYVY      rw-   16bit/pixel interleaved YUV
      VDA* TGA       rw-   Truevision Targa image
    VICAR* VICAR     rw-   VICAR rasterfile format
      VID* VID       rw+   Visual Image Directory
     VIFF* VIFF      rw+   Khoros Visualization image
     VIPS* VIPS      rw+   VIPS image
      VST* TGA       rw-   Truevision Targa image
     WBMP* WBMP      rw-   Wireless Bitmap (level 0) image
      WMV  MPEG      rw+   Windows Media Video
      WPG* WPG       r--   Word Perfect Graphics
        X* X         rw+   X Image
      X3F  DNG       r--   Sigma Camera RAW Picture File
      XBM* XBM       rw-   X Windows system bitmap (black and white)
       XC* XC        r--   Constant image uniform color
      XCF* XCF       r--   GIMP image
      XPM* XPM       rw-   X Windows system pixmap (color)
      XPS  XPS       r--   Microsoft XML Paper Specification
       XV* VIFF      rw+   Khoros Visualization image
      XWD* XWD       rw-   X Windows system window dump (color)
    YCbCr* YCbCr     rw+   Raw Y, Cb, and Cr samples
   YCbCrA* YCbCr     rw+   Raw Y, Cb, Cr, and alpha samples
      YUV* YUV       rw-   CCIR 601 4:1:1 or 4:2:2

* native blob support
r read support
w write support
+ support for multiple images

私の環境だとこんな感じになりました。なお、Modeの欄にrがあるファイル形式は読み込み可能、wとあるのはその形式で出力が可能という意味です。

このリストを丹念に見ていくと、

     UBRL* BRAILLE   -w-   Unicode Text format
    UBRL6* BRAILLE   -w-   Unicode Text format 6dot

というのを見つけました。Braille、Unicode、…といえば!?

Unicodeには点字が含まれている

Unicodeといえば世界各国で使われている言語の文字を集めた文字コードですが、点字に使われる文字もこのなかに含まれていて、U+2800からU+28FFの間に収録されています。

256個の点字
seq 0x2800 0x28FF | ruby -ne'print $_.to_i.chr("UTF-8")'#⠀⠁⠂⠃⠄⠅⠆⠇⠈⠉⠊⠋⠌⠍⠎⠏⠐⠑⠒⠓⠔⠕⠖⠗⠘⠙⠚⠛⠜⠝⠞⠟⠠⠡⠢⠣⠤⠥⠦⠧⠨⠩⠪⠫⠬⠭⠮⠯⠰⠱⠲⠳⠴⠵⠶⠷⠸⠹⠺⠻⠼⠽⠾⠿⡀⡁⡂⡃⡄⡅⡆⡇⡈⡉⡊⡋⡌⡍⡎⡏⡐⡑⡒⡓⡔⡕⡖⡗⡘⡙⡚⡛⡜⡝⡞⡟⡠⡡⡢⡣⡤⡥⡦⡧⡨⡩⡪⡫⡬⡭⡮⡯⡰⡱⡲⡳⡴⡵⡶⡷⡸⡹⡺⡻⡼⡽⡾⡿⢀⢁⢂⢃⢄⢅⢆⢇⢈⢉⢊⢋⢌⢍⢎⢏⢐⢑⢒⢓⢔⢕⢖⢗⢘⢙⢚⢛⢜⢝⢞⢟⢠⢡⢢⢣⢤⢥⢦⢧⢨⢩⢪⢫⢬⢭⢮⢯⢰⢱⢲⢳⢴⢵⢶⢷⢸⢹⢺⢻⢼⢽⢾⢿⣀⣁⣂⣃⣄⣅⣆⣇⣈⣉⣊⣋⣌⣍⣎⣏⣐⣑⣒⣓⣔⣕⣖⣗⣘⣙⣚⣛⣜⣝⣞⣟⣠⣡⣢⣣⣤⣥⣦⣧⣨⣩⣪⣫⣬⣭⣮⣯⣰⣱⣲⣳⣴⣵⣶⣷⣸⣹⣺⣻⣼⣽⣾⣿

実は、(点字の本来の使い途とは違うのですが)ImageMagickでは出力にUBRL(すなわちBraille)を指定すると、これら点字を用いたアスキーアート3をテキストファイルとして出力することができるのです。

Imagemagickを用いてAAを生成

bash
convert -size 100x100 xc:White -fill Black -font"Takao明朝"-pointsize 100 -gravity Center -draw'text 0,0 "龍"' ubrl:
出力
Width: 100
Height: 100

⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢰⣾⣶⣶⣦⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣺⣷⣷⣶⡦⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⡏⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣽⣿⣿⠉⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⡆⠀⠀⠀⠀⠀⣴⣷⣄⠀⠀⠀⣺⣿⣿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣴⣿⣿⣤⠀⠀⠀⠀⠀
⠀⠀⠠⣴⣴⣴⣦⣴⣦⣴⣴⣼⣿⣿⣧⣴⣴⣴⣴⣾⣿⣿⣿⡷⠀⠀⣺⣿⣿⢶⠶⡶⢶⠶⢶⠶⠶⡾⢶⠶⡾⠿⡿⠿⡿⠟⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⣶⣤⡀⠀⠀⠀⠀⠀⠀⠀⢀⣷⣶⣤⣀⠀⠀⠀⠀⠀⢽⣿⣿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠈⢿⣿⣦⡀⠀⠀⠀⠀⠀⣸⣿⣿⠟⠛⠀⠀⠀⠀⠀⣽⣿⣿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣴⣿⣦⡀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠘⣿⣿⣷⠀⠀⠀⠀⢀⣿⣿⠏⠀⠀⠀⠀⠀⠀⠀⣺⣿⣿⠻⠟⠻⠻⠟⠻⠻⠟⠻⠻⠟⠻⣿⣿⣿⠿⠂⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⢻⣿⡿⠀⠀⠀⠀⣼⡿⠃⠀⠀⠀⣴⣿⣦⠀⠀⠺⠛⠻⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣿⣿⡯⠀⠀⠀⠀⠀⠀
⠀⠀⠶⡶⣶⢶⢶⢶⣶⢶⡶⡶⣶⢶⢶⢿⡷⡶⣶⢶⡾⣿⢿⢿⡷⠀⣠⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣿⣿⡯⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣽⣿⣿⡷⠶⡶⢶⠶⢶⢶⡶⡶⡶⡶⡶⣿⣿⡷⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⢰⣦⣄⣀⠀⡀⡀⠀⡀⡀⡀⢀⢀⣰⣿⣷⣄⠀⠀⠀⠀⣺⣿⣿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠿⠿⠯⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⢸⣿⣿⠟⠛⠛⠛⠛⠛⠛⠛⠛⠛⢻⣿⣿⠿⠃⠀⠀⠀⣺⣿⣿⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣶⣦⣄⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⢸⣿⣿⠅⠀⠀⠀⠀⠀⠀⠀⠀⠀⢼⣿⣿⠀⠀⠀⠀⠀⢽⣿⣿⠾⠾⠾⠷⠷⠾⠾⠾⠷⠿⠿⠿⠿⠗⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⢸⣿⣿⣥⣠⣄⣄⣄⣄⣄⣄⣄⣄⣾⣿⣿⠀⠀⠀⠀⠀⣽⣿⣿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⢸⣿⣿⠍⠉⠉⠉⠉⠉⠉⠉⠉⠉⣹⣿⣿⠀⠀⠀⠀⠀⣺⣿⣿⣀⣀⣀⣀⣀⣀⣀⣀⣀⣴⣿⣿⣶⣀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⢸⣿⣿⡁⠀⠀⠀⠀⠀⠀⠀⠀⠀⢼⣿⣿⠀⠀⠀⠀⠀⢽⣿⣿⠋⠙⠙⠙⠙⠋⠙⠙⠋⠙⠙⠋⠙⠉⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⢸⣿⣿⣦⣤⣤⣤⣤⣤⣤⣤⣤⣤⣾⣿⣿⠀⠀⠀⠀⠀⢽⣿⣿⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣴⣦⡀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⢸⣿⣿⡉⠈⠈⠈⠁⠁⠁⠉⠁⠁⢽⣿⣿⠀⠀⠀⠀⠀⣺⣿⣿⣦⣤⣦⣤⣴⣤⣦⣤⣴⣿⣿⣿⣿⡆⠀⢀⣿⠆⠀⠀⠀
⠀⠀⠀⠀⠀⢸⣿⣿⡂⠀⠀⠀⠀⠀⠀⠀⠀⠀⢺⣿⣿⠀⠀⠀⠀⠀⢽⣿⣿⠀⠁⠀⠀⠀⠁⠀⠀⠀⠁⠀⠀⠈⠀⠀⢸⣿⡇⠀⠀⠀
⠀⠀⠀⠀⠀⢸⣿⣿⡂⠀⠀⠀⠀⠀⠀⠀⠀⠀⢼⣿⣿⠀⠀⠀⠀⠀⣽⣿⣿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣾⣿⡇⠀⠀⠀
⠀⠀⠀⠀⠀⢸⣿⣿⡂⠀⠀⠀⣦⣶⣴⣦⣦⣶⣿⣿⣿⠀⠀⠀⠀⠀⢺⣿⣿⣧⣤⣤⣠⣤⣤⣄⣤⣄⣄⣤⣤⣤⣤⣾⣿⣿⣿⣶⠀⠀
⠀⠀⠀⠀⠀⢸⣿⣿⡂⠀⠀⠀⠀⠈⠉⠙⢿⣿⣿⣿⠏⠀⠀⠀⠀⠀⠈⠛⠿⠿⢿⢿⣿⢿⣿⣿⣿⣿⣿⣿⣿⣿⢿⡿⠿⠿⠟⠁⠀⠀
⠀⠀⠀⠀⠀⠘⠛⠛⠂⠀⠀⠀⠀⠀⠀⠀⠈⠉⠉⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀

-drawオプションで画像の上に文字を書くとき、-fontで用いるフォントを指定してやる必要があります。ここではTakao明朝を使っていますが、環境によってはこれが使えないのでその場合は他のフォントを試してみてください。なおImageMagickで使えるフォントの一覧はconvert -list fontで見ることができます。

今年もお世話になりました

echo"よいお年を" | sed's/./&\n/g' | xargs -I@ convert -size 100x100 xc:White -fill Black -font"Takao明朝"-pointsize 100 -gravity Center -draw'text 0,0 "@"' ubrl: | grep-v :
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⣀⣀⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⠛⠿⣿⣿⣿⣦⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢹⣿⣿⣿⠂⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠸⣿⣿⣿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠸⣿⣿⣏⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠨⣿⣿⣗⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠐⣿⣿⡇⠀⠀⠀⠀⠀⠀⢀⣀⣤⣴⣶⣶⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣿⣿⣷⣶⣶⣶⣶⣿⣿⣿⣿⣿⣿⣿⡿⠟⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣿⣿⡿⠛⠛⠛⠛⠛⠛⠙⠉⠉⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣿⣿⣗⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣿⣿⡧⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣽⣿⣗⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣺⣿⣏⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢽⣿⣟⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⣀⣀⣀⣀⣀⣀⣀⠀⣹⣿⣿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⣴⣶⣿⣿⡿⠿⠿⠿⢿⡿⣿⣿⣿⣿⣿⣿⣶⣤⣤⣀⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⢠⣾⣿⠿⠋⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⢹⣿⣿⠿⢿⣿⣿⣿⣿⣶⣦⣤⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⢾⣿⡏⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣸⣿⣿⡆⠀⠈⠉⠛⠿⢿⣿⣿⣿⣿⣶⣄⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⢹⣿⣧⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣾⣿⣿⠂⠀⠀⠀⠀⠀⠀⠈⠛⠿⣿⣿⣿⣿⡄⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⢿⣿⣶⣤⣀⣀⡀⠀⠀⠀⢀⣀⣤⣾⣿⣿⠟⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠙⠻⠟⠁⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠛⠿⢿⣿⣿⣿⣿⣿⣿⣿⣿⡿⠟⠋⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠉⠈⠉⠈⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀

⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠸⣿⣷⣶⣤⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠈⣿⣿⣿⣷⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠻⢷⣦⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⠅⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠹⢿⣷⣤⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠹⣿⣿⣦⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⢸⣿⣿⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⢿⣿⣿⣆⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⢸⣿⣿⠇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⢿⣿⣿⣷⡀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠸⣿⣿⠆⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⣿⣿⣿⣿⡄⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠨⣿⣿⡂⠀⠀⠀⠀⠀⠀⠀⠀⠀⣤⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠸⣿⣿⣿⣿⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⣿⣿⡇⠀⠀⠀⠀⠀⠀⠀⢀⣼⡿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢻⣿⣿⣿⡇⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⢻⣿⡧⠀⠀⠀⠀⠀⠀⢀⣾⡿⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠸⣿⣿⣿⠇⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠸⣿⣿⡀⠀⠀⠀⠀⢠⣾⣿⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⠋⠁⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⢿⣿⣧⠀⠀⠀⣠⣿⣿⡏⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠈⢿⣿⣷⣄⣴⣿⣿⣿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⢿⣿⣿⣿⣿⣿⣗⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⢿⣿⣿⣿⣧⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠻⠿⠟⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀

⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣠⣤⣀⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠻⣿⣿⣿⣶⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠸⣿⣿⣿⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⠿⣷⣶⣤⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⣿⣿⣿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⠿⣿⣿⣦⣄⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣿⣿⡷⠀⠀⠀⠀⢀⣀⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⢿⣿⣿⣷⣆⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⣠⡀⠀⠀⠀⠀⠀⠀⠀⠀⣿⣿⡇⢀⣀⣤⣾⣿⣿⣿⣿⠆⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠻⣿⣿⣿⣧⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠘⢿⣷⣤⣄⣄⣄⣄⣤⣤⣿⣿⣿⣿⣿⣿⡿⠿⠛⠋⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠹⣿⣿⣿⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠉⠛⠻⠿⠿⠿⠟⠟⣿⣿⡏⠉⠈⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠋⠁⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣿⣿⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣿⣿⠇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡀⡀⢀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⣿⣿⡇⠀⠀⢀⣠⣤⣴⣶⣾⣿⣿⣿⣿⣿⣿⣿⣿⣷⣦⣤⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣿⣿⣷⣶⣿⡿⠿⠛⠛⠉⠉⠁⠀⠁⠀⠀⠈⠉⠙⠻⢿⣿⣿⣶⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⣤⣶⣿⣿⣿⡟⠉⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⢿⣿⣿⣆⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⣴⣾⡿⠟⠛⠁⣿⣿⡆⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⣿⣿⣿⡆⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⣠⣾⡿⠟⠉⠀⠀⠀⠠⣿⣿⠇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣿⣿⣿⡇⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⣼⣿⡏⠀⠀⠀⠀⠀⠀⢨⣿⣿⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣿⣿⣿⠃⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⢺⣿⣧⡀⠀⠀⠀⠀⠀⢸⣿⣿⠇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣼⣿⣿⡏⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠹⣿⣿⣦⣀⠀⠀⠀⣼⣿⣿⠅⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⣴⣿⣿⡿⠋⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠈⠻⣿⣿⣿⣾⣾⣿⣿⡟⠀⠀⠀⠀⠀⠘⠻⠿⣷⣶⣶⣴⣦⣶⣶⣾⣿⣿⡿⠟⠋⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⠛⠿⠿⠟⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠉⠉⠉⠙⠉⠉⠉⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀

⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣿⣷⣶⣤⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣸⣿⣿⡿⠟⠛⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢠⣿⣿⣿⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣠⣶⣄⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣾⣿⣿⣏⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣼⣿⣿⣿⣷⣄⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣼⣿⣿⠟⠙⠋⠙⠙⠋⠋⠙⠙⠋⠙⢻⣿⣿⡏⠛⠋⠙⠙⠋⠛⠙⠙⠙⠙⠙⠋⠙⠛⠙⠁⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣸⣿⣿⠏⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⣰⣿⣿⠏⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⣴⣿⡿⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣠⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⢀⣼⣿⠟⠁⠀⢠⣤⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⡇⠀⠀⠀⠀⠀⠀⠀⠀⢀⣾⣿⣿⣦⡀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⢠⣾⡿⠋⠀⠀⠀⢸⣿⣿⡿⠾⠾⠷⠷⠷⠷⠷⠷⠷⢿⣿⣿⡿⠷⠷⠷⠷⠷⠷⠷⠾⠿⠿⠿⠿⠿⠗⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⢰⡿⠋⠀⠀⠀⠀⠀⢸⣿⣿⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣼⣷⣦⠀⠀⠀⠀
⠀⠀⠀⣄⣄⣠⣄⣤⣠⣄⣄⣠⣼⣿⣿⣇⣄⣠⣄⣤⣠⣄⣄⣠⣄⣼⣿⣿⣧⣠⣄⣤⣄⣄⣠⣄⣄⣠⣄⣤⣄⣴⣿⣿⣿⣿⣷⡄⠀⠀
⠀⠀⠀⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⢹⣿⣿⡏⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⡧⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⡧⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⠛⠛⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀

⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣀⣄⣤⣀⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠛⠻⣿⣿⣿⡧⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣿⣿⣿⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣼⣿⣿⠁⠀⠀⠀⣀⣤⣴⣤⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⢠⣦⣀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣾⣿⡿⢁⣀⣤⣶⣿⣿⣿⣿⡿⠟⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⠿⣿⣶⣶⣴⣦⣦⣴⣦⣾⣿⣿⣿⣿⣿⣿⠿⠟⠛⠉⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠙⠛⠛⠛⣻⣿⣿⠛⠉⠉⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣴⣿⡿⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣠⣾⣿⠋⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠻⣿⣦⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣴⣿⣟⣤⣶⣶⠷⠷⣷⣶⣤⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣀⣴⣿⣿⣿⣧⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣰⣿⣿⣿⡿⠋⠁⠀⠀⠀⠀⠙⣿⣿⣦⠀⠀⠀⠀⢀⣠⣴⣾⣿⣿⣿⣿⠿⠛⠉⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⣠⣾⣿⣿⣿⠋⠀⠀⠀⠀⠀⠀⠀⠀⠸⣿⣿⣧⣠⣴⣾⣿⣿⡿⠿⠛⠉⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⢰⣿⣿⣿⡟⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⣠⣿⣿⣿⣿⠿⠛⠉⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠈⠿⡿⠏⠀⠀⠀⠀⠀⠀⠀⢀⣠⣶⣿⣿⢿⣿⣿⣿⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣴⣿⣿⠟⠋⠀⠈⣿⣿⣿⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣴⣿⡿⠋⠀⠀⠀⠀⠀⣿⣿⣿⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣾⣿⠏⠀⠀⠀⠀⠀⠀⠀⢻⣿⠿⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⡟⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣷⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠻⣿⣿⣶⣤⣄⣀⡀⡀⠀⡀⠀⠀⢀⠀⢀⣀⣀⣠⣤⣴⣶⣶⣤⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠙⠻⠿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⠇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠉⠉⠉⠙⠉⠋⠙⠉⠙⠉⠉⠉⠉⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀

参考リンク・関連ページ


  1. レイアウトやデザインのときに一時的に入れておく仮の写真のこと 

  2. apt install imagemagick 

  3. Ascii文字じゃないけどまあいっか 


一瞬で忘れそうなフィルタコマンド備忘録

$
0
0

フィルタ系のコマンドは全然使わないからせめてまとめといて思い出しやすくしておこうと思ったので書き記す。。

フィルタ

標準入力を受け取って、適当な処理をして標準出力に出力するコマンドのこと

wc

オプション無しで引数にファイルを指定すると、行数単語数バイト数を表示してくれる

(オプション)
-cバイト数を数えてくれる
-m文字数を数えてくれる
-l行数を数えてくれる
-w文字数を数えてくれる

sort

sortしてくれる。オプション無しだとASCIIコードに則ってアルファベット順でsortしてくれる。正確には文字コード順に並び替えるだけなので、下の例みたいな順番になる。

(例)
A
Z
a
b
(オプション)
-n数値順にソート
-r逆順にソート

uniq

重複した行を除いて表示してくれるのが基本動作。

(オプション)
-cある行が出現する回数をカウントする

cut

入力の一部を切り出せる

(書式)
cut-d区切り文字 -fフィールド番号 ファイル名
(オプション)
-d区切り文字の指定(ない場合はタブで区切られる)-b指定したバイト目だけを出力する
-c指定した文字番目だけ表示する
-f表示するフィールド番号を指定する
-s区切り文字を含まない行を出力しない

cutコマンドでは-b-c-fのどれかひとつだけを選んで必ず使わなければならない。

tr

文字を置換してくれる。ファイルを引数に指定できないので入力リダイレクトで扱うか、適当なコマンドから出力をパイプしてくることで使える。

(使い方)
tr オプション SET1 SET2

SET1が置換される文字列でSET2が置換後の文字列。

(オプション)
-d置換しないで置換対象をdeleteする
-s SET1 と SET2 の対応するn番目の文字を置換する
(SETで使える正規表現)
\bバックスペース
\n改行
\t水平タブ
a-d アルファベット順でaからdまでの文字
:cntrl: すべての制御文字
詳しくはhelpを読もう、日本語だったよ

tail

標準入力から末尾n行を出力する。オプションを指定しなければ10行出力される。

(オプション)
-n引数で、表示する末尾から何行出力するかを指定
-fファイルを監視し、内容が追記されたら表示する
-vファイル名を一番上に表示してくれる

diff

2ファイルの差分を出力してくれる。出力形式にはデフォルトの形式の他にユニファイド形式というものがある。読み方に癖があるが、ここで解説はしない。gitで使われているのもユニファイド形式なので使うときはユニファイド形式で出力するのがいいだろう。ちなみにディレクトリも比較できる。

(オプション)
-q 2つのファイルの内容が異なるかどうかのみ出力する
-s 2つのファイルの内容が同じかどうかのみ出力する
-uユニファイド形式で出力する
-aすべのファイルをテキストとして扱う
-wすべてのスペースを無視する
-bすべてのスペースによる違いを無視する
-Z行末にあるスペースを無視する

bashからzshへの乗り換え

$
0
0

:pencil:このエントリーは、MacOS Catalinaからデフォルトシェルが bashから zshになったことを期に、bashからzshへ乗り換えるための作業メモ

前提条件

ホームディレクトリで作業

$ pwd
/Users/gentamura

現在利用しているシェルはbash

$ echo $SHELL
/bin/bash

zshがインストールされている

❯ cat /etc/shells
# List of acceptable shells for chpass(1).
# Ftpd will not allow users to connect who are not using
# one of these shells.

/bin/bash
/bin/csh
/bin/ksh
/bin/sh
/bin/tcsh
/bin/zsh

移行作業

ログインシェルを入れ替え

$ chsh -s /bin/zsh

一旦ターミナルを閉じて、再起動

利用しているシェルを確認

$ echo $SHELL
/bin/bash

設定ファイルをコピー

$ cp .bash_profile .zsh_profile

設定ファイルを一部書き換え

$ vim .zsh_profile

export PATH="$HOME/.anyenv/bin:$PATH"
eval "$(anyenv init -)"

export PATH="$HOME/.cargo/bin:$PATH"
export PATH="/Applications/flutter/bin:$PATH"

export GOPATH="$(go env GOPATH)"
export PATH="$GOPATH/bin:$PATH"

eval "$(starship init bash)" => eval "$(starship init zsh)" に変更

再起動

$ source .zsh_profile

以上です。
今の所無事動いているので、なにかあれば追記します:elephant:

動く!電光掲示板シェル芸

$
0
0

 ⚠注意⚠: 点滅GIF画像が出てくるので、明るい部屋で画面から十分画面から離れてご覧ください。

まえがき

 今回の記事では、動くタイプの電光掲示板シェル芸について解説します。
 具体的には文字を電光掲示板ライクに変換して、文字を画像にするtextimgコマンドのオプションを色々使って動かします。
 基本的(?)な電光掲示板シェル芸については@yami_buta氏が本AdventCalendarで過去3回ほど解説1をされているので、先にそちらの方に目を通されることをおすすめします。

おさらい 〜電光掲示板シェル芸とは〜

 「LED表示みたいな画像を生成する」2シェル芸。
▼参考画像3
kohno.png
 今回はこれを動かします。
 こんなことができます。

▼点滅とか…4
gameover.gif
▼縦スクロール5
tenmetsu.gif
▼掲示板っぽい横スクロール6
kohnosc.gif
▼明暗7
internet.gif

やってみる

STEP0: 電光掲示板文字っぽく出力

 今回は文字Qiitaを動かしたいと思います。
 まずdenkoh関数を定義します。(定義ツイート)
 この関数は文字を任意の文字でAAのように変換したものを返し、簡単に電光掲示板風に変換します。
 最近だと@jiro4989氏がコマンドにしたもの8もあるのでそちらを使っても良いです。

denkoh
# denkoh <電光掲示板風にしたい文> <背景文字> <前景文字> <粗さ># dtimgだと:# dtimg <電光掲示板風にしたい文> -b <背景文字> -g <前景文字> -F <粗さ># .bash_aliases`などに追記しておくと便利。
denkoh(){echo-e"$1"|textimg -F$4|convert - txt:-|awk -F'[, ]'\'NR>2{printf($1==0)?"\n":($4==0)?a:b}'a="$2"b="$3";echo}

これを使うと、

Terminal
$ denkoh "Qiita"" ""■ " 20
                                                 
                                                 
             ■■■       ■■■                       
 ■■■■■■      ■■■       ■■■                       
■■■■■■■■     ■■■       ■■■      ■■               
■■■  ■■■     ■■■       ■■■      ■■               
■■   ■■■                        ■■               
■■    ■■■  ■■■■■     ■■■■■      ■■       ■■■■■■  
■■    ■■■  ■■■■■     ■■■■■    ■■■■■■■■  ■■■■■■■■ 
■■    ■■■    ■■■       ■■■    ■■■■■■■■  ■■■  ■■■ 
■■    ■■■    ■■■       ■■■      ■■           ■■■ 
■■    ■■■    ■■■       ■■■      ■■       ■■■■■■■ 
■■    ■■■    ■■■       ■■■      ■■      ■■■■■■■■ 
■■    ■■■    ■■■       ■■■      ■■      ■■■  ■■■ 
■■    ■■     ■■■       ■■■      ■■      ■■   ■■■ 
■■■  ■■■     ■■■       ■■■      ■■      ■■   ■■■ 
■■■■■■■■  ■■■■■■■■  ■■■■■■■■    ■■■■■■  ■■■■■■■■ 
 ■■■■■■   ■■■■■■■■  ■■■■■■■■    ■■■■■■  ■■■■■■■■ 
  ■■■■■■                         ■■■■     ■■■    
     ■■■■                                        
      ■■■                                        
                                                 

 という具合に文字が出力できます。
 この出力をtextimgに標準入力で渡すと画像になります。

Terminal
$ denkoh "Qiita"" ""■ " 20|textimg -o qiita.png

qiita.png

STEP1: 点滅

 点滅させてみましょう。
 同じ行数の空行を出力し、行数指定(textimg-lオプション)で[文字部分n行]->[空行部分n行]となるように調整してコマを切り替えGIF化します。
 ここでは任意の空行を作るため、eval 'echo;'{<任意数個カンマ>}とします。eval 'echo;'{,,,,}{,,,}とすると5*4=20回echoが評価されて20行分空行が作れます。
 ちなみに数で明示的に空行を作るならseq <任意行数>|tr -d 0-9でも同じことができます。
 また複数コマンドの標準出力をまとめてパイプ先コマンドに渡す時は(cmd1;cmd2)|cmd3とします。便利〜

Terminal
#全体の行数の半分を-lに指定$ (denkoh "Qiita"" ""■" 20;eval'echo;'{,,,,,}{,,})|wc -l
42
$ (denkoh "Qiita"" ""■" 20;eval'echo;'{,,,,}{,,,})|textimg -al21-o tenmetsu1.gif

tenmetsu1.gif
 目に痛いときは-dオプションで速度を落としましょう。(デフォルトは20ms)

Terminal
$ (denkoh "Qiita"" ""■" 20;eval'echo;'{,,,,}{,,,})|textimg -al21-d50-o tenmetsu2.gif

tenmetsu2.gif
 いい感じです。
 色コードの指定9で色をつけることもできますが、色のついた絵文字を指定することで簡単に色をつけられます。また後述のlolcatという出力を虹色にするコマンドとも親和性が良いのでそれを使用しても楽しいです。

Terminal
$ (denkoh "Qiita"" ""🍀" 20;eval'echo;'{,,,,}{,,,})|textimg -al21-o tenmetsu3.gif -d50

tenmetsu3.gif

STEP2: 縦スクロール

 次は縦に動かします。
 -Sオプションは、デフォルトで1行ずつ移動します。

Terminal
$ (eval'echo;'{,,,,}{,,,};denkoh Qiita " ""🍀" 20)|textimg -aSl21-o tatesc1.gif

tatesc1.gif
 -Wオプションで任意の行数移動。

Terminal
$ (eval'echo;'{,,,,}{,,,};denkoh Qiita " ""🍀" 20)|textimg -aSl21-W3-o tatesc2.gif

tatesc2.gif
 そして-Eオプションで、EOSでまた行頭にループさせます。

Terminal
$ (eval'echo;'{,,,,}{,,,};denkoh Qiita " ""🍀" 20)|textimg -aSEl21-W3-o tatesc3.gif

tatesc3.gif
 空行を減らして-dでコマの切り替え速度をアップ!

Terminal
$ (eval'echo;'{,,,,}{,,,};denkoh Qiita " ""🍀" 20)|textimg -aSEl21-W3-o tatesc4.gif

tatesc4.gif

STEP3: 横スクロール

 電光掲示板でよくある横方向のスクロール。これを実現するにはsedを使ってdenkoh出力を任意文字分を行末から行頭へ回転移動させることを繰り返します。

Terminal
#一旦出力を変数に格納$ s="$(denkoh Qiita ' ''🍀' 20)"$ for i in$(seq 10)#ずらす回数分ループ, 一度に5文字ずつずらすdo echo"$s"|sed -E's/(.{'$[i*5]'})(.*)/\2\1/'done|textimg -al22-o yoko.gif #もともとの行数+2

yoko.gif
 いい感じです。
 この処理は、denkohmoveという関数に定義しておくと便利です。(定義ツイート)10

denkohmove
# denkohmove <電光掲示板風にしたい文> <背景文字> <前景文字> <粗さ> \#            <移動する幅> <ずらす回数(=コマ数)> <出力時の解像度> <コマの切り替え速度># ex: denkohmove ' 🚙 🚗 🚙 🚗' ' ' '⚪' 10 \#                8 7 5 10 
denkohmove(){text="$1"back="$2"front="$3"line="$4"width="$5"flame="$6"resolute="$7"speed="$8"s="$(denkoh ${text}${back}${front}${line})"for i in$(seq${flame})do echo"$s"|sed -E's/(.{'$[i*width]'})(.*)/\2\1/'done|textimg -asl$[line+1] -F${resolute}-d${speed}}

STEP4: 明暗

 複数文字でのdenkoh出力を組み合わせ繰り返すことで明暗の反復を表現できます。
 「明」は中身のある文字(例:●,▲)、「暗」はない文字(例:○,△)で表現します。

Terminal
$ s="`denkoh Qiita ' '■ 20`"$ for i in● ○
    do echo-e"$s"|sed "s/■/$i/g"done|textimg -al22-o meimetsu1.gif

meimetsu1.gif

 明暗の文字を連続して切り替えるとメリハリ(?)が付きます。

Terminal
$ s="`denkoh Qiita ' '■ 20`"$ for i in● ○ ▲ △ ● ○ ▲ △ #明暗の文字を交互に配置するdo echo-e"$s"|sed "s/■/$i/g"done|textimg -al22-o meimetsu2.gif

meimetsu2.gif

 また出力時の行数を2倍にすると明暗を並べて表現できます。

Terminal
$ s="`denkoh Qiita ' '■ 20`"$ for i in● ○ ▲ △ ● ○ ▲ △
    do echo-e"$s"|sed "s/■/$i/g"done|textimg -al44-o meimetsu3.gif

meimetsu3.gif

 コマンドラインを虹色にするのでおなじみlolcatコマンド11で着色してみます。-fオプションは、パイプで渡しても色コードが失われないようにするオプションです。また-pオプションは虹の次の色へ変わるまでの間隔、-Fオプションは色の機微が遷移する間隔を指定します。

Terminal
$ s="`denkoh Qiita ' '■ 20`"$ for i in● ○ ▲ △ ● ○ ▲ △
    do echo-e"$s"|sed "s/■/$i/g"done|lolcat -f|textimg -al22-o meimetsu4.gif

meimetsu4.gif

 いいですね。

 ちなみに、STEP3とこの手法を組み合わせて各ループごとの偶数番目と奇数番目で前景文字を異なる文字で置換することで、冒頭例のようにチカチカした感じを表現できます。ここでは「明」を白混じりの🍺、「暗」を黄いろい🌟に指定しています。

Terminal
#ずらす作業の関数化$ f(){echo"$d"|sed -E's/(.{'$[i*9]'})(.*)/\2\1/';}$ d="$(denkoh Qiita ' ''🍺' 20)"$ for i in{1..5}do[[$((i%2))= 0 ]]&&f ||{ f|sed 's_🍺_🌟_g';}#奇数番目ループなら置換done|textimg -al22-o chika.gif

chika.gif 
 派手〜!

STEP5: シェル芸botに投稿!

 シェル芸botは皆さんご存知、フォロワーの#シェル芸付きのツイートをコマンドとして評価し結果をツイートで返すbotです。
 シェル芸botには、コンテナ内の/images/直下に画像ファイルをコマンド内で配置することでその画像を投稿できる機能があります。(静/動画ともに最大4枚まで)
 textimgにはこのために画像の出力先を/images/o.[png|gif]にできる-sオプションが用意されています。上記の例の-oオプション部分を削除して代わりに-sを使いましょう。
 また、投稿が1ツイートに収まりきらない場合や、他人がツイートで定義した関数・変数を使用したいなら、コメント付き引用RTでそのツイートをRTすることで実現できます。(参考: denkohの定義ツイートとそれを引用したツイート)

備考: 投稿できない時 && テスト環境の紹介

 しかし、今回取り上げたGIF画像を実行しようとしてもうまく結果がbotに返されないこともあります。理由としては、

  • シェル芸botの実行TLE(制約20s)
    • timeout 20 <ツイート>で実行している(?)
  • 画像サイズ超過
    • 静止画 : 5MB
    • GIF動画 : 15MB(スマホなら5MB)

 が挙げられ、殆どは前者が原因です。計算量を落とすには、

  • textimgでの出力時に-Fオプションでデフォルトの20より少ない数を指定して解像度を落とす(denkohなら最後の数字)
  • denkohで出力する行数を減らす
  • STEP3ならずらす幅を大きくしループを少なくしてコマを減らす

 などの方法があります。投稿できる計算量にしてからツイートを投稿しましょう。
 シェル芸bot作者の@theoldmoon0602氏によってbotの最新dockerイメージが配布12されているのでそれを使用してもいいですが、画像を逐一確認しながら20秒制限を調整するには、以下のWeb実行環境(各種ネタコマンド完備)でテストするのが無難です。13

実行されるまで#シェル芸ツイートを連投しTLを汚しながらコマンドを調整する方法もありますが...

結論

textimg便利すぎ!!!!!16

最後に

 いかがでしたか?
 このようにコマンドライン上だけで多種多様なGIF画像ができてしまいます。
 またこの他にも今回紹介した例を応用して様々な画像が作れます。

▼例: 斜めスクロール
kohnosc.gif

▼例: 複数文字を順に17
merry.gif

 お試しあれ。

 余談ですが、今回紹介したtextimgやdtimgはシェル上に画像を投稿するために作られたコマンドです。他にもシェル芸bot上には様々なシェル芸愛好家(通称:シェル芸人)の方々が、主にシェル芸botで使うことを目的として作成したクソ便利なコマンド群が導入されています。18
 是非本記事で使用したコマンドや他の楽しいコマンド群を使って#シェル芸でシェル芸botに投稿してみましょう!
 それでは皆様、良いシェル芸ライフを!

ArchLinuxで最新のLinuxカーネルのバージョンを取得する

$
0
0

これだけ

pacman -Ss | grep'core/linux' | grep-v"linux-" | awk'{print $2}'

ターミナルを見やすくして作業効率を上げよう!

$
0
0

概要

本記事では、ターミナルのテーマの変更やgitとの連携についてまとめています。
初期状態の見にくいターミナルから卒業しましょう!

フォントを変更する

自分的に見やすいフォントはSF Mono Regularです。フォントサイズも大きくしておきましょう。

ターミナル -> 環境設定 -> プロファイル -> テキスト
スクリーンショット 2019-12-25 13.17.14.png

テーマを変更する

デフォルトのテーマではなく、下記からクローンして読み込ませます。
http://cocopon.github.io/iceberg.vim/
下記の流れで読み込ませることができます。読み込ませたら、デフォルトに設定しておきましょう。

歯車マーク -> "読み込む..." -> クローンしたファイルを選択
スクリーンショット 2019-12-25 13.20.38.png

読み込ませたあと、背景(カラーとエフェクト)を編集してもっと見やすくしましょう。
不透明度、ブラーを編集します。
スクリーンショット 2019-12-25 13.24.11.png

ちなみに選択部分はこんな感じ。見やすい色にしてみてください。
スクリーンショット 2019-12-25 13.37.58.png

さらに色付け

.bashrcを編集してさらに色付けをしていきます。

# default:cyan / root:red
if [ $UID -eq 0 ]; then
    PS1="\[\033[31m\]\h\[\033[00m\]:\[\033[01m\]\w\[\033[00m\]\\$ "
else
    PS1="\[\033[36m\]\h\[\033[00m\]:\[\033[01m\]\w\[\033[00m\]\\$ "
fi

# "-F":ディレクトリに"/"を表示 / "-G"でディレクトリを色表示
alias ls='ls -FG'
alias ll='ls -alFG'

編集したら、読み込ませるために下記を実行してください。
$ source ~/.bashrc

これで、このように色がついたと思います。
スクリーンショット 2019-12-25 13.28.51.png

gitのリポジトリ・ブランチ名を表示させる

先ほどの編集ではgitのリポジトリ名・ブランチ名は表示されません。
"作業するブランチをよく間違えてしまう・・"とかの悩みを抱えている人はさらに編集していきましょう。

# gitの設定
source /usr/local/etc/bash_completion.d/git-prompt.sh
source /usr/local/etc/bash_completion.d/git-completion.bash
GIT_PS1_SHOWDIRTYSTATE=true

# default:cyan / root:red
if [ $UID -eq 0 ]; then
    PS1='\[\033[31m\]\h\[\033[00m\]:\[\033[01m\]\w\[\033[31m\]$(__git_ps1)\[\033[00m\]\\$ '
else
    PS1='\[\033[36m\]\h\[\033[00m\]:\[\033[01m\]\w\[\033[31m\]$(__git_ps1)\[\033[00m\]\\$ '
fi

# "-F":ディレクトリに"/"を表示 / "-G"でディレクトリを色表示
alias ls='ls -FG'
alias ll='ls -alFG'

編集をし終わったら、先ほどのコマンドを再実行してください。
$ source ~/.bashrc

これで、リポジトリ名・ブランチ名が表示されると思います。
試しに何かしらのリポジトリにうつってみてください!

終わりに

まだまだ自分の理想に近づけたい・・!という人は.bashrcを編集して、かっこよく効率よく作業できる環境を作ってみてください!

jq + bash (zsh) でリストっぽいデータを処理する

$
0
0

配列っぽいデータが含まれるJSONデータをjqコマンドとシェルを組み合わせて処理する方法.

こういう感じのJSONデータ (states.json) があるとして.

{"states":[{"name":"北海道","name_ac":"ほっかいどう"},{"name":"青森","name_ac":"あおもり"},{"name":"岩手","name_ac":"いわて"},{"name":"宮城","name_ac":"みやぎ"},{"name":"秋田","name_ac":"あきた"},{"name":"山形","name_ac":"やまがた"},{"name":"福島","name_ac":"ふくしま"}]}

こんな感じで出力させるものとする.

北海道 ほっかいどう
青森 あおもり
岩手 いわて
宮城 みやぎ
秋田 あきた
山形 やまがた
福島 ふくしま

整形して出力

単にJSONを見やすく整形出力したいなら, これだけ.

jq '.'< states.json

JSONのなかの特定のプロパティにアクセスする

プロパティ名を指定すればよい.

jq '.states'< states.json

次の結果が得られる.

[
  {
    "name": "北海道",
    "name_ac": "ほっかいどう"
  },
  {
    "name": "青森",
    "name_ac": "あおもり"
  },
  {
    "name": "岩手",
    "name_ac": "いわて"
  },
  {
    "name": "宮城",
    "name_ac": "みやぎ"
  },
  {
    "name": "秋田",
    "name_ac": "あきた"
  },
  {
    "name": "山形",
    "name_ac": "やまがた"
  },
  {
    "name": "福島",
    "name_ac": "ふくしま"
  }
]

シーケンシャルに処理できるようにする

配列などのデータを扱うなら, for/while で処理できたほうがいい.
jqの機能 length と seqコマンドを組み合わせる例を見かけることがあるけど, 個人的にはwhileを好む.

jq -c'.states[]'< states.json

-cオプションで, 出力されるJSONを1行にできる.
フィルタ (ここまであげた例でいうシングルクォートでくくってる箇所) で指定してるプロパティに []をつけると,
1アイテムを1行ずつ出力してくれる.

{"name":"北海道","name_ac":"ほっかいどう"}
{"name":"青森","name_ac":"あおもり"}
{"name":"岩手","name_ac":"いわて"}
{"name":"宮城","name_ac":"みやぎ"}
{"name":"秋田","name_ac":"あきた"}
{"name":"山形","name_ac":"やまがた"}
{"name":"福島","name_ac":"ふくしま"}

whileループで処理する

ここまでできると, パイプと while readとさらにjqコマンドを組み合わせることで,
各アイテムごとのプロパティにアクセスできるようになる.

jq -c'.states[]'< states.json | \while read state;do
        jq -r'[.name, .name_ac] | @tsv'<<<$state | (read name name_ac
            echo$name$name_ac)done

-rオプションでダブルクォートでくくるのをなしにしている.
フィルタでは [.name, .name_ac]でリストっぽい出力にし, jqの組み込み機能でtsv (タブ区切りのシーケンス) に変換し, readコマンドで変数へセットしてる.

readで各アイテムを読みだした後, 変数へセットするところは, 素直に次のように書いてもいいと思う.
(jqコマンドの呼び出しは少なくできるけど, 値にスペースが含まれてることを考えると, このほうが使いやすいかも)

jq -c'.states[]'< states.json | \while read state;do
        name=$(jq -r'.name'<<<$state)name_ac=$(jq -r'.name_ac'<<<$state)echo$name$name_acdone

ShellScript作るときによく使うやつ

$
0
0

なんか便利そうなやつ

なんか画面に入力してほしいときのやつ

read-p"なんか入力してちょ: " INPUT_VAL

なんか確認するやつ

function confirmation_to_continue(){read-p"よろしいですか? (y/N): " yn
  case"$yn"in[yY]*);;*)echo"abort.";exit;;esac}

空判定するやつ

function error_if_empty(){while[-z"$1"];do
    echo"入力値が空です。"exit 1
  done}

数値判定するやつ

function error_if_not_numeric(){expr"$1" + 1 >/dev/null 2>&1
  while![$?-lt 2 ];do
    echo"数値で入力してください。"exit 1
  done}

DBでなんやかんややりたいやつ

なんかDBから値とってきたいやつ

VAL=$(echo${QUERY} | ${MYSQL}-N)

transactionしたくね?的なやつ

${MYSQL}<<EOF
    BEGIN;
    $QUERY
    COMMIT;
EOF

2段階認証死ぬほど面倒くさいときのやつ

$
0
0

Google Authenticatorのあれ

oathtool入れてるの前提
適当な名前をつけてシェルをつくる

#!/bin/shTOTP=`oathtool --totp--base32 XXXXXXXXXXXXXXX`

expect -c"
    set timeout 5
    spawn ssh AAAAAAAAAAAAA
    expect \"Verification code:\"
    send \"${TOTP}\n\"
    interact
"

これでaliasも登録すればいっぱつ。やったぜ。

ちなみにAWSの2段階認証はaws-mfa入れてなんやかんやできます。やったぜ。

pip3 install aws-mfa

設定とかは割愛

Ubuntu 18.04 のスタートアップスクリプトで環境変数を設定する

$
0
0

Ubuntu 18.04 でのスタートアップスクリプト指定の良い方法のポストで記述したが、前回は、Ubuntuのスタートアップスクリプトをcrontabで作成する方法を記述した。

しかし、あの方法では一つ欠点がある。環境変数がうまく反映されないのだ。

環境変数を設定しない場合

前回の方法で例えばこんなスクリプトを書いてみる。本来はスタートアップスクリプトとして書くのだが、今回はテストのために、テストスクリプトを書いてみた。クーロンを設定するシェルと、単純に環境変数を出力するシェルだ。

set_cron.sh

#!/bin/bashPATH=/home/azureuser/csv:$PATHSHELL_DIR=/home/ushio/Codes/DevSecOps/volley/playground/cron
SHELL_FILE=${SHELL_DIR}/foo.sh
(echo"*/1 * * * * ${SHELL_FILE}> ${SHELL_DIR}/abc.log") | crontab -u ushio -

foo.sh

#!/bin/bashecho"Print PATH environment variables."echo$PATH

これを普通に実行すると次のようになる。ちなみに、設定の反映は、クーロンが起動していないといけないため、と設定反映のため、リスタートしておく。

$ ./set_cron.sh
$ sudo /etc/init.d/cron restart

実行結果

凄くPATHが少ない。crontabで設定した結果を見ても、PATHは含まれていない。

$ cat abc.log
Print PATH environment variables.
/usr/bin:/bin

/var/spool/cron/crontabs/ushio

$ sudo cat  /var/spool/cron/crontabs/ushio
# DO NOT EDIT THIS FILE - edit the master and reinstall.# (- installed on Wed Dec 25 22:32:15 2019)# (Cron version -- $Id: crontab.c,v 2.13 1994/01/17 03:20:37 vixie Exp $)*/1 **** /home/ushio/Codes/DevSecOps/volley/playground/cron/foo.sh > /home/ushio/Codes/DevSecOps/volley/playground/cron/abc.log

PATH を含む方法

改行コード\nは、crontabは認識してくれなさそうなので、複数行を送る方法はないみたいなので、次のような工夫をしてみました。2回crontabを実施するけど、2回目は、1回目に設定した内容を引き継ぐようにしています。-lオプションをつかって、現在の内容をとり、それと新しい設定をマージしています。

set_cron.sh

#!/bin/bashPATH=/home/azureuser/csv:$PATHSHELL_DIR=/home/ushio/Codes/DevSecOps/volley/playground/cron
SHELL_FILE=${SHELL_DIR}/foo.sh
echo"PATH=${PATH}" | crontab -u ushio -
(crontab -l 2>/dev/null;echo"*/1 * * * * ${SHELL_FILE}> ${SHELL_DIR}/abc.log") | crontab -u ushio -

実行結果

想定通りの結果がでていますね。

$ Print PATH environment variables.
/home/azureuser/csv:/home/ushio/.goenv/versions/1.13.4/bin:/home/ushio/.goenv/shims:/home/ushio/.goenv/bin:/home/ushio/.anyenv/bin:/home/ushio/.vscode-server/bin/8795a9889db74563ddd43eb0a897a2384129a619/bin:/home/ushio/.goenv/versions/1.13.4/bin:/home/ushio/.goenv/shims:/home/ushio/.goenv/bin:/home/ushio/.anyenv/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/home/ushio/dotnet:/usr/lib/jvm/java-8-openjdk-amd64/bin:/home/ushio/Codes/DevSecOps/EpiServer/StressTesting/apache-jmeter-5.2/bin:/home/ushio/go/1.13.4/bin:/home/ushio/dotnet:/usr/lib/jvm/java-8-openjdk-amd64/bin:/home/ushio/Codes/DevSecOps/EpiServer/StressTesting/apache-jmeter-5.2/bin:/home/ushio/go/1.13.4/bin

/var/spool/cron/crontabs/ushio

$ sudo cat  /var/spool/cron/crontabs/ushio
# DO NOT EDIT THIS FILE - edit the master and reinstall.# (- installed on Wed Dec 25 22:32:15 2019)# (Cron version -- $Id: crontab.c,v 2.13 1994/01/17 03:20:37 vixie Exp $)PATH=/home/azureuser/csv:/home/ushio/.goenv/versions/1.13.4/bin:/home/ushio/.goenv/shims:/home/ushio/.goenv/bin:/home/ushio/.anyenv/bin:/home/ushio/.vscode-server/bin/8795a9889db74563ddd43eb0a897a2384129a619/bin:/home/ushio/.goenv/versions/1.13.4/bin:/home/ushio/.goenv/shims:/home/ushio/.goenv/bin:/home/ushio/.anyenv/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/home/ushio/dotnet:/usr/lib/jvm/java-8-openjdk-amd64/bin:/home/ushio/Codes/DevSecOps/EpiServer/StressTesting/apache-jmeter-5.2/bin:/home/ushio/go/1.13.4/bin:/home/ushio/dotnet:/usr/lib/jvm/java-8-openjdk-amd64/bin:/home/ushio/Codes/DevSecOps/EpiServer/StressTesting/apache-jmeter-5.2/bin:/home/ushio/go/1.13.4/bin
*/1 **** /home/ushio/Codes/DevSecOps/volley/playground/cron/foo.sh > /home/ushio/Codes/DevSecOps/volley/playground/cron/abc.log

lambda custom runtimeでbash動かす+標準にないnkfコマンドを使えるようにする

$
0
0

はじめに

オンプレで動いているBashのスクリプトをlambdaで動かしたいなと思い、
AWSのチュートリアルを実践プラスアルファで、nkfコマンド使えるようにする。

参考にしたチュートリアル

チュートリアル – カスタムランタイムの公開 - AWS Lambda

$ tree
.
├── bootstrap
├── function.sh
└── response.txt

こんな感じで始めていきます。

ロールの作成

やること

  • Rolesのページを開く。
  • ロールの作成
    • 信頼されたエンティティ - Lambda
    • アクセス許可 - AWSLambdaBasicExecutionRole
    • ロール名 - lambda-role

チュートリアルでは、これをさっくり作ってくれとなっていますが、せっかくなのでawscliで作ってみます。

AWSCLIでの手順

変数の定義

$ ROLE_NAME=lambda-role
$ echo$ROLE_NAME
lambda-role

信頼ポリシーの作成

$ cat<<EOF> policy
> {
>             "Version": "2012-10-17",
>             "Statement": [
>                 {
>                     "Effect": "Allow",
>                     "Principal": {
>                         "Service": "lambda.amazonaws.com"
>                     },
>                     "Action": "sts:AssumeRole"
>                 }
>             ]
>         }
> EOF

確認。
失敗していたらparse errorと出る

$ cat policy | jq
{"Version": "2012-10-17",
  "Statement": [{"Effect": "Allow",
      "Principal": {"Service": "lambda.amazonaws.com"},
      "Action": "sts:AssumeRole"}]}

Role作成

$ aws iam create-role --role-name${ROLE_NAME}--assume-role-policy-document file://policy

結果確認

$ aws iam list-roles --query"Roles[?RoleName == '${ROLE_NAME}']"{"Role": {"Path": "/",
        "RoleName": "lambda-role",
        "RoleId": "AAAAAAAAAAAAAAAAAAAAAA",
        "Arn": "arn:aws:iam:: 123456789012:role/lambda-role",
        "CreateDate": "2019-12-01-01",
        "AssumeRolePolicyDocument": {"Version": "2012-10-17",
            "Statement": [{"Effect": "Allow",
                    "Principal": {"Service": "lambda.amazonaws.com"},
                    "Action": "sts:AssumeRole"}]}}}

管理ポリシーAWSLambdaBasicExecutionRoleをアタッチする

$ aws iam attach-role-policy --role-name${ROLE_NAME}--policy-arn arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole

結果確認

$  aws iam list-attached-role-policies --role-name lambda-role
{"AttachedPolicies": [{"PolicyName": "AWSLambdaBasicExecutionRole",
            "PolicyArn": "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"}]}

bootstarpを作成する

カスタムランタイムの場合、bootstrapというファイルが、一番はじめに呼び出される。

bootstrap
#!/bin/sh -xvset-euo pipefail

# ここだけ、チュートリアルから編集。tmpフォルダ内にawscliを配置するためHOMEを設定export HOME="/tmp"# ハンドラ関数の読み込みsource$LAMBDA_TASK_ROOT/"$(echo$_HANDLER | cut-d.-f1).sh"while true
do
HEADERS="$(mktemp)"# イベントの取得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)# ハンドラ関数の実行RESPONSE=$($(echo"$_HANDLER" | cut-d.-f2)"$EVENT_DATA")# 結果を返却
curl -X POST "http://${AWS_LAMBDA_RUNTIME_API}/2018-06-01/runtime/invocation/$REQUEST_ID/response"-d"$RESPONSE"done

function.sh

bootstrapを起点として、実際に呼び出される関数です。
bootstrapのRESPONSE=$($(echo "$_HANDLER" | cut -d. -f2) "$EVENT_DATA")ところから呼び出されます。

function.sh
function handler (){EVENT_DATA=$1echo"$EVENT_DATA" 1>&2;RESPONSE=$(nkf --help)echo$RESPONSE}

アップロード

# Lambdaレイヤーに、bootstrapを含めてzip化$ zip runtime.zip bootstrap 
$ aws lambda publish-layer-version --layer-name bash-runtime --zip-file fileb://runtime.zip

# Lambda作成$ zip function_only.zip function.sh
$ aws lambda create-function --function-name bash-runtime --zip-file fileb://function-only.zip --handlerfunction.handler --runtime provided --role  arn:aws:iam::123456789012:role/lambda-role --layers arn:aws:lambda:ap-northeast-1:123456789012:layer:bash-runtime:1

怒られます

これをアプロードし、Lambda上のテストで実行すると怒られます。

nkfコマンド自体が見つからないよって言われて、怒られる
+++ nkf --help
/var/task/function.sh: line 5: nkf: command not found
++ RESPONSE=
+ RESPONSE=

というわけで、nkfコマンドをインストールして、一緒にアップロードします

Lambdaの中身はAmazon Linuxとのことなので、LinuxOSから、yum install。

最初はAL2でやろうと思ったのですが、nkfのリポジトリが無さそうだったので、とりあえずcentOSにしました(やることに対してイメージが大きい気がするけど。。)

FROM centos:7WORKDIR /usr/local/src/# yum-config-manager のインストールRUN yum -yinstall yum-utils

# yum repository の追加RUN yum -y update
RUN yum -yinstall epel-release
RUN yum -yinstall http://rpms.famillecollet.com/enterprise/remi-release-7.rpm
RUN yum -yinstall http://dev.mysql.com/get/mysql57-community-release-el7-9.noarch.rpm

# NKF インストールRUN yum -yinstall nkf
version:'3'services:lambda-with-c-compiler:build:.volumes:-./lib:/var/tmpcommand:bash -c "cp /usr/bin/nkf /var/tmp/"

やっていることは、./libフォルダとマウントした/var/tmpに、yum installしたnkfをコピーしているだけです。

$ docker-compose build
$ docker-compose up -d$ ls-l ./lib/
-rwxr-xr-x  1 nakajimaatsushi  staff  273248 Dec 24 01:37 nkf

また、Lambdaレイヤーとしてアップロードしたものは、/optの下に配置されるそうなので、
そこにPATH通すように、bootstrapを変更します。

bootstrapに追記
export PATH="$HOME/.local/bin:/opt/lib:$PATH"

そこからアップし直すと、使えました。

# Lambdaレイヤーに、bootstrapとlibを含めてzip化$ zip runtime.zip bootstrap lib/*$ aws lambda publish-layer-version --layer-name bash-runtime --zip-file fileb://runtime.zip

# Lambda-Layerを更新$ aws lambda update-function-configuration --function-name bash-runtime --layers arn:aws:lambda:ap-northeast-1:123456789012:layer:bash-runtime:2

# Lambdaコード更新(libだけ追加なら正直やらなくていいはず)$ zip function_only.zip function.sh
$ aws lambda update-function-code --function-name bash-runtime --zip-file fileb://function-only.zip

正直使い所はなさそう

参考にしたもの

チュートリアル – カスタムランタイムの公開 - AWS Lambda
カスタム AWS Lambda ランタイム - AWS Lambda
AWS CLI Command Reference — AWS CLI 1.16.309 Command Reference

「Cloud9メモ」→「bashプロンプトの表示を短縮する」

$
0
0

bashプロンプトの表示をフルパス(\w)から短いパス(\W)に変更します。

~/.bashrcのファイルを開いて、PS1の行を変更する。

~/.bashrc
# PS1='\[\033[01;32m\]$(_cloud9_prompt_user)\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]$(__git_ps1 " (%s)" 2>/dev/null) $ 'PS1='\[\033[01;32m\]$(_cloud9_prompt_user)\[\033[00m\]:\[\033[01;34m\]\W\[\033[00m\]$(__git_ps1 " (%s)" 2>/dev/null) $'

~/.bashrcを再読み込みして、ターミナルに変更内容を反映する。

$ source ~/.bashrc

ターミナルで使える自作コマンドを作る

$
0
0

この記事について

この記事では、ターミナルで使える自作コマンドを一から作って、使えるようにするための手順を紹介します。

使用する環境・バージョン

  • OS : MacOS Mojave ver 10.14.5
  • bash : Copyright (C) 2007 Free Software Foundation, Inc.

前提条件

  • 自作コマンドはディレクトリ~/commandに作成します。

読者に要求する前提知識

  • 「パスを通す」の意味がわかり、実際にできること。
  • shellスクリプトが書けること。

自作コマンドの作り方

1.コマンドファイルを作る

今回は、aaaという名前のコマンドを作ることにします。
そのため、ディレクトリ~/command中にaaaという名前のファイルを作ります。
注:拡張子はなしで作ります。

$ cd ~/commmand
$ touch aaa

作ったaaaファイルの一行目に、以下のように記述します。

/command/aaa
#!/bin/sh

2.シェルスクリプトを書く

これで準備はできたので、あとは二行目以下に望み通りの機能を実現するシェルスクリプトを書くだけです。
ここでは、コマンドを作るという観点で便利なコーディングを紹介したいと思います。

コマンドの引数を処理する

コマンドには、引数という形でシェルスクリプトに値を渡すことができます。

$ command aaa 引数1 引数2 ... 引数n

この引数はシェルスクリプトの中で$1,$2$nという風に設定されています。
実際に、スクリプトに以下のように記述します。

/command/aaa
echo"$1"echo"$2"echo"$3"

この状態でコマンドに引数をつけて実行すると、以下のように出力されます。

$ aaa one two three
one
two
three

このように、第一引数one、第二引数two、第三引数threeが認識されています。

参考:UNIX & Linux コマンド・シェルスクリプト リファレンス 引数を処理する

コマンド名を認識する

今実行しているコマンド名aaaをスクリプト中で取得するためにはどうしたらいいでしょうか。
実は、実行コマンドのパスが「0番目の引数」にあたります。
実際に0番目の引数を表示するようなコマンドを作ってみます。

/command/aaa
echo"$0"

これを実行すると、以下のような出力が得られます。

$ aaa
/Users/username/command/aaa

確かに0番目の引数がパスを指しているのがわかります。
コマンド名aaaだけを取得するためには、以下のようにします。

/command/aaa
echo"$(basename${0})"
$ aaa
aaa

コマンドのオプションを処理する

コマンドには、引数以外にも、-aのように、「ハイフン+アルファベット一文字」の形でも値を渡すことができます。(オプション)

$ command aaa -a-btest

上のコマンドだと、オプション-aと、testの値付きのオプション-bが指定されています。
オプションの処理をするためには、getoptsコマンドを使用します。

/command/aaa
while getopts ab: OPT;do
    case$OPTin
        a )FLG_A="TRUE";;
        b )FLG_B="TRUE";VALUE_B="$OPTARG";;esacdone

1行目のwhile文にgetoptsが使われています。
第一引数には取りうるオプションを並べます。オプション-aには値がないのでそのままaと記載、オプション-bには値がつくのでb:と記載します。
第二引数OPT`にはコマンド実行時に使われたオプションアルファベットが格納されます。

上記のスクリプトにすることで、以下の形でそれぞれのオプションの有無・その値が取得できます。
- オプション-aが存在 → 変数FLG_A"TRUE"が格納される
- オプション-bが存在 → 変数FLG_B"TRUE"が格納される + 変数VALUE_Bに-bについた値が格納される

参考:シェルスクリプトでオプション付きのコマンドを作成する
参考:Bash の getopts でロングオプションを処理する

3.実行する準備をする

せっかく作った自作コマンドですが、そのままでは使うことができません。
そのため、作ったスクリプトをきちんとコマンドとして認識させましょう。
参考:シェルスクリプトで自作コマンドを作成する

3.1-1 パスを通す

作ったコマンドがあるディレクトリ~/commandにパスを通します。

$ vi .bash_profile
~/.bash_profile
export PATH=$HOME/command:$PATH

3.1-2 シンボリックリンクを貼る

~/commandにパスを通す代わりに、~/command/aaaにシンボリックリンクを貼ってもよいです。

$ ln-si ~/command/aaa /usr/local/bin

3.2 自作コマンドのスクリプトに権限を付与

これでコマンド自体は認識されるようになりますが、このままだと権限エラーが出てしまいます。

$ aaa
bash: /Users/username/command/aaa: Permission denied

そのため、作ったスクリプト~/command/aaaに権限を付与します。

$ chmod 777 ~/command/aaa

これで自作コマンドaaaが使えるようになりました!

$ aaa
(指定の動作)
Viewing all 2750 articles
Browse latest View live