自宅仮想環境 - QEMU+libvirt+virtinstで仮想マシンを作る
必要パッケージのインストール
$ sudo apt install qemu qemu-kvm libvirt-bin virtinst
ディレクトリの整備
/virtual
以下で仮想環境関係のものを管理しようと思っているので、まずはその準備をします。
具体的には以下5つの作業をします。
/virtual
などのディレクトリを作る- 仮想環境をいじるグループを作る *1
/virtual
を上記グループ所有にする/virtual
のパーミッションを設定する- アカウントをグループに追加する
$ sudo mkdir /virtual/ $ sudo groupadd virtadmin $ sudo -R chown :virtadmin /virtual $ sudo chmod 2770 /virtual $ mkdir /virtual/{disk,iso} $ sudo chown root /virtual/{disk,iso} $ sudo usermod -aG virtadmin <user> $ sudo usermod -aG virtadmin libvirt-qemu
OSイメージのダウンロード
$ cd /virtual/iso $ wget https://cdimage.debian.org/debian-cd/current/amd64/iso-cd/debian-9.3.0-amd64-netinst.iso
VMの作成
次の非常に長いコマンドでVMを作成できます。
$ virt-install --name=first --disk=/virtual/disk/first.qcow2,format=qcow2,size=4 --vcpus=1 --ram=1024 --cdrom=/virtual/iso/debian-9.3.0-amd64-netinst.iso --noautoconsole --graphics vnc,port=5900,listen=127.0.0.1 --network bridge=virbr0,model=virtio --os-type=linux --virt-type=kvm
出力は以下です。
WARNING /virtual/disk/first.qcow2 may not be accessible by the hypervisor. You will need to grant the 'libvirt-qemu' user search permissions for the following directories: ['/virtual/disk', '/virtual'] Starting install... Allocating 'first.qcow2' | 4.0 GB 00:00:00 Creating domain... | 0 B 00:00:00 Domain installation still in progress. You can reconnect to the console to complete the installation process.
Allocating 'xxxx.qcow2'
が出ていて、その後にRemoving disk 'xxxx.qcow2'
が出ていなければ上手くいっていることが多い印象です。
WARNING が出てますが、上記でlibvirt-qemu
にパーミッションを与えているので問題ないはずです。
VMへの接続
VMの作成でのコマンドでは、debian のディスクからブートしただけなので、debianをイチからインストールしていく必要があります。
インストールするには画面につなぐ必要があり、上記コマンドでは --graphics vnc,port=5900,listen=127.0.0.1
でVMの画面へのつなぎ方を指定しています。
今回は、VNCで画面が見れるようにしています。
$ ss -tna | grep 5900 LISTEN 0 1 127.0.0.1:5900 *:*
ローカルループバックIPアドレスからのみを許可しているので、SSHポートフォワード経由でVNC接続すれば画面が見れるので、インストールできます。
(今回はやってませんが。自動インストールしたい。自動インストールしたら画面とか不要じゃない?SSHだけで良くない?)
補足
VMの一覧を見る
現在libvirtdで管理されているVMの一覧はvirsh list --all
で確認できます。
$ virsh list --all Id Name State ---------------------------------------------------- 3 first running
--all
を付けているとシャットダウン中のlibvirtdが管理しているVMも表示されます。
libvirtd
ここまで説明ゼロで申し訳ありませんが、libvirt-binをインストールすると、仮想環境の維持管理を行ってくれる libvirtd
というデーモンに、 virsh
でアクセスして操作するようになります。
(QEMU だと本来は qemu-system-x86 ......
のようなコマンドで、VMが終了するまで実行する形ですが、そこをlibvirtdが裏でやってくれる。)
本来 virsh
でVMを作るとけっこうめんどくさい XML を書く必要がありますが、virt-install
を使うとコマンドだけでVMを作成できるようになります。 *4
virt-install
はブリッジインターフェースなどの設定も勝手にやってくれる(はず)なので非常に便利です。
参考文献(参考にした文献)
自宅仮想環境 - OS インストール
PCが届いたのでOSをインストールしました。
OS
Ubuntu 16.04.3 LTS です(Arch じゃありません。日和りました)。
Download Ubuntu Server | Download | Ubuntu
/etc/fstab
で詰まってた話
ネットワーク環境が最悪で、「ベースシステムのインストール」が何回か失敗してました。
フォーマット直後でないディスクにインストールしようとすると「unclean」と怒られますが、その他設定の良い具合のものが見つかったのでクリーンインストールして終わりにしようとしました。
何回かリトライしていて、早く終わらせたかったのでめんどくさいパーティショナーを飛ばそうと、シェルを起動して /dev/sdb? をそれぞれフォーマットしました(USBフラッシュメモリからインストールしたので /dev/sda はそれが占めてます)。
つまり、インストーラのパーティショナーを通らずにベーシックシステムをインストールしました。
すると、/etc/fstab
が作られなかったらしく、/
のみがRead Onlyでマウントされた状態で起動しました。
対応として、今回はパーティションは↓を採用していたので、そう/etc/fstab
に雑に書きました。(参考: fstab - ArchWiki )
/dev/sda1
->/
/dev/sda2
->/boot
/dev/sda3
->/home
/dev/sda4
->/var
上記対応で、(他に不具合ないか怖いですが、)なんとか動いてます。
なんかあったらそれはそれでおいしいとも思ってます。
スペック
ディスクは SSD 120GB が発掘されました。
$ cat /proc/cpuinfo | grep model\ name | head -1 model name : Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz $ cat /proc/meminfo | grep MemTotal MemTotal: 32829020 kB $ sudo fdisk -l /dev/sda | head -n 1 Disk /dev/sda: 111.8 GiB, 120034123776 bytes, 234441648 sectors
第32回シェル芸勉強会 Q1・Q2・Q3 の別解
昨日は家に帰るのが遅かったので休みました。
前置き
毎日更新で解くと、普段のシェル芸勉強会で問題を解いているときと違い、問題に取り組んでいる期間が長く、頭に問題が残っていて解答を思いつくので、それを残しています。
Q1の解答3
$ echo 14679 | sed 's#.*#seq 9 | tr -dc &\\\\n#' | sh 1 4 6 7 9
解説
この解答は最終的にsh
に投げてます。投げる前の出力(sh
で実行されるコマンド)は次のようになっています。
$ echo 14679 | sed 's#.*#seq 9 | tr -dc &\\\\n#' seq 9 | tr -dc 14679\\n
seq
の連番出力から、必要な数字だけ残しています。
発送のタネは、 第32回シェル芸勉強会 Q1 の解答 - nogiro_iotaのメモ の解答1で出力した
1 -2 -3 4 -5 6 7 -8 9
です。
上記の出力はseq
から不要な数字を除くため、diff
を使って作っていますが、そもそもtr
を使えば、不要な数字だけを消せます。
Q2の解答5
$ echo 14679 | diff -u <(seq 9) <(grep -o .) | sed 1,3d | cat <(echo {a..z}|tr \ \\n) - | awk '/[a-z]/{a[NR]=$0}/^ /{print $0}/^-/{print a[++i]}' | tr -d \ 1 a b 4 c 6 7 d 9
解説
問題を誤読していました。
第32回シェル芸勉強会 Q2 の解答 - nogiro_iotaのメモ では、「Q1の結果から始めてQ2の解答を作れ」だと思って解いていましたが、「Q1と同じ入力から始めてQ2の解答を作れ」でした。
Q1の解答1で不要行を消しているところで、そのままアルファベットを出力しました。
前回の投稿のときには、「連続したアルファベットを出すのが難しい」と言っていましたがbash
のブレース展開を使うと簡単に作れました。
Q2の解答6
$ echo 14679 | grep -o . | sed 's#.*#sed &i&#' | paste -d\| -s | sed 's#.*#echo {a..z} | tr \\ \\\\n | &#' | bash | awk '{print}/^9$/{exit}' 1 a b 4 c 6 7 d 9
解説
アルファベットの連番は簡単に作れますが、それをコマンド中に混ぜ込むのは地味にめんどくさいので、アルファベットの連番に元の出力を混ぜ込むようにしました。
文での説明だとたぶん良くわからないので、bash
に投げる前の出力をご覧ください。
echo {a..z} | tr \ \\n | sed 1i1|sed 4i4|sed 6i6|sed 7i7|sed 9i9
Q3の解答2
seq 65536 | factor | awk 'NF==2{print "\\<"$2"/tcp\\>"}' | grep -f - /etc/services
PlantUMLがすごい
テキストファイルをソースに、UML図を作れるツール。しゅごい。(というお題目で始めたのにどうしてこうなった)
走れメロス(前20段落)の状態遷移図です。(オチ)
出力用ワンライナー
返ってこなくてもバグではありませんし、JVMの設定などをしてないとそもそも例外が飛びます(全文だとJava以前にDotの実行から返ってこずTimeoutしますが。。。)。
aozora books --title "走れメロス" | jq '.[0].book_id' | xargs aozora content --format html --id | sed '1,/id="contents"/d' | sed '/class="chitsuki_1"/,$d' | sed 's#<ruby><rb>\([^<]*\)</rb><rp>[^<]*</rp><rt>\([^<]*\)</rt><rp>[^<]*</rp></ruby>#\1#g' | sed 's#<[^>]*>##g' | tr -d \\r | sed 's#[ 、「」]##g' | head -n 20 | mecab | sed 's#\t.*##' | grep -v -e EOS -e '^$' | awk 'NR==1{print "(*) \""$0"\""}NR!=1{print "\""p"\" \""$0"\""}{p=$0}END{print "\""$0"\" (*)"}' | awk '{a[$0]++;i[NR]=$0;a1[NR]=$1;a2[NR]=$2}END{for(idx=1;idx<=NR;idx++){print a1[idx]" -->["a[i[idx]]"] "a2[idx]}}' | awk '!a[$0]++' | cat <(echo @startuml) - <(echo @enduml) | plantuml -p > merosu_20.png
解説
青空文庫にCLIでアクセスできる aozora-cli
を使って、ワンライナーで走れメロスの状態遷移図(アクティビティ図)を作ったよ!
インストール
まずはインストール。
sudo apt install plantuml fonts-ricty-diminished
Rictyフォントをインストールしているのは、Windows Subsystem for Linum (Bash on Windows) には、デフォルトだと日本語が入っていないからです。
$ pip3 install aozora-cli The program 'pip3' is currently not installed. You can install it by typing: sudo apt install python3-pip
ʅฺ(・ω・。)ʃฺ??
$ apt install python3-pip ...... $ pip3 install aozora-cli Collecting aozora-cli Downloading aozora-cli-0.0.3.tar.gz Collecting requests (from aozora-cli) ......
aozora books --title "走れメロス" | jq '.[0].book_id' | xargs aozora content --format html --id | sed '1,/id="contents"/d' | sed '/class="chitsuki_1"/,$d' | sed 's#<ruby><rb>\([^<]*\)</rb><rp>[^<]*</rp><rt>\([^<]*\)</rt><rp>[^<]*</rp></ruby>#\1#g' | sed 's#<[^>]*>##g' | tr -d \\r | sed 's#[ 、「」]##g' | head -n 20 | mecab | sed 's#\t.*##' | grep -v -e EOS -e '^$' | awk 'NR==1{print "(*) \""$0"\""}NR!=1{print "\""p"\" \""$0"\""}{p=$0}END{print "\""$0"\" (*)"}' | awk '{a[$0]++;i[NR]=$0;a1[NR]=$1;a2[NR]=$2}END{for(idx=1;idx<=NR;idx++){print a1[idx]" -->["a[i[idx]]"] "a2[idx]}}' | awk '!a[$0]++' | cat <(echo @startuml) - <(echo @enduml) | plantuml -p > merosu_20.png java.lang.NegativeArraySizeException
(#^ω^)
$ sudo vim `which plantuml` $ tail -n 1 `which plantuml` $JAVA -Xmx4096m -jar /usr/share/plantuml/plantuml.jar ${@}
で再度実行したらできます。
まとめ
plantumlもaozora-cliも青空文庫もしゅごい。
おまけ
1文
aozora books --title "走れメロス" | jq '.[0].book_id' | xargs aozora content --format html --id | sed '1,/id="contents"/d' | sed '/class="chitsuki_1"/,$d' | sed 's#<ruby><rb>\([^<]*\)</rb><rp>[^<]*</rp><rt>\([^<]*\)</rt><rp>[^<]*</rp></ruby>#\1#g' | sed 's#<[^>]*>##g' | head -n 1 | tr -d \\r | sed 's#[ 、「」]##g' | sed 's#\([^。]*。\).*#\1#' | mecab | sed 's#\t.*##' | grep -v -e EOS -e '^$' | awk 'NR==1{print "(*) \""$0"\""}NR!=1{print "\""p"\" \""$0"\""}{p=$0}END{print "\""$0"\" (*)"}' | awk '{a[$0]++;i[NR]=$0;a1[NR]=$1;a2[NR]=$2}END{for(idx=1;idx<=NR;idx++){print a1[idx]" -->["a[i[idx]]"] "a2[idx]}}' | awk '!a[$0]++' | cat <(echo @startuml) - <(echo @enduml) | plantuml -p > merosu_1.png
1段落
aozora books --title "走れメロス" | jq '.[0].book_id' | xargs aozora content --format html --id | sed '1,/id="contents"/d' | sed '/class="chitsuki_1"/,$d' | sed 's#<ruby><rb>\([^<]*\)</rb><rp>[^<]*</rp><rt>\([^<]*\)</rt><rp>[^<]*</rp></ruby>#\1#g' | sed 's#<[^>]*>##g' | head -n 1 | tr -d \\r | sed 's#[ 、「」]##g' | mecab | sed 's#\t.*##' | grep -v -e EOS -e '^$' | awk 'NR==1{print "(*) \""$0"\""}NR!=1{print "\""p"\" \""$0"\""}{p=$0}END{print "\""$0"\" (*)"}' | awk '{a[$0]++;i[NR]=$0;a1[NR]=$1;a2[NR]=$2}END{for(idx=1;idx<=NR;idx++){print a1[idx]" -->["a[i[idx]]"] "a2[idx]}}' | awk '!a[$0]++' | cat <(echo @startuml) - <(echo @enduml) | plantuml -p > merosu.png
整形ワンライナー
aozora books --title "走れメロス" \ | jq '.[0].book_id' \ | xargs aozora content --format html --id \ | sed '1,/id="contents"/d' \ | sed '/class="chitsuki_1"/,$d' \ | sed 's#<ruby><rb>\([^<]*\)</rb><rp>[^<]*</rp><rt>\([^<]*\)</rt><rp>[^<]*</rp></ruby>#\1#g' \ | sed 's#<[^>]*>##g' \ | tr -d \\r \ | sed 's#[ 、「」]##g' \ | head -n 20 \ | mecab \ | sed 's#\t.*##' \ | grep -v -e EOS -e '^$' \ | awk 'NR==1{print "(*) \""$0"\""}NR!=1{print "\""p"\" \""$0"\""}{p=$0}END{print "\""$0"\" (*)"}' \ | awk '{a[$0]++;i[NR]=$0;a1[NR]=$1;a2[NR]=$2}END{for(idx=1;idx<=NR;idx++){print a1[idx]" -->["a[i[idx]]"] "a2[idx]}}' \ | awk '!a[$0]++' \ | cat <(echo @startuml) - <(echo @enduml) \ | plantuml -p > merosu_20.png
自宅仮想環境を構築するために、PCをポチったこと
(ほぼタイトルどおりですが。。。)
自宅にQEMU+libvirtの仮想環境を構築するためにPCを買いました。
なぜ今の時代にクラウド環境ではなく自宅に環境を作るのか、という話ですが主に以下の3点が理由です。
- 別件でQEMU+libvirtを触っているが、 QEMU on VirtualBox で若干重いし、テキトーに使うのもはばかられること
- 自宅にDNSのリゾルバがほしい
- 音楽用のサーバーがほしい
- Raspbian の動く仮想環境がほしい
それで色々と検討した結果、↓をポチりました。
Shuttle 小型ベアボーンPC 200x78x250mm ブラック XH110G
- 出版社/メーカー: Shuttle INC.
- 発売日: 2017/08/04
- メディア: 付属品
- この商品を含むブログを見る
上記の目的に達するには、最低限
- 仮想環境用なのでVT-xサポートしてるCPU対応のマザーが載ってる
- サウンドカードを挿せる
という条件がまずあり、そこに、
という欲求が混ざった結果です。
CPU とメモリは↓です。
Intel CPU Core i7-7700K 4.2GHz 8Mキャッシュ 4コア/8スレッド LGA1151 BX80677I77700K 【BOX】
- 出版社/メーカー: インテル
- 発売日: 2017/01/06
- メディア: Personal Computers
- この商品を含むブログを見る
シリコンパワー ノートPC用メモリ DDR4-2400(PC4-19200) 16GB 260Pin 1.2V CL17 永久保証 SP016GBSFU240B02
- 出版社/メーカー: シリコンパワー
- 発売日: 2017/03/30
- メディア: Personal Computers
- この商品を含むブログを見る
ディスクは2.5インチをマウントできるのですが、家にいっぱい転がってるものを適当にピックアップして使う予定です。(たぶん40GBのSSDを使います。)
まだ、注文しただけで、おそらく届くのは土曜日になリます。楽しみですね。
ブログに書いてるのは、家に環境作るのはやりたいことではありますが、やりたいなーと思っててもやらないので、(誰も読んでないとしても)宣言しておくことで逃げ道を塞ぐためです。
第32回シェル芸勉強会 Q8 の解答 + 今後の投稿ネタ
前置き
今日は第32回シェル芸勉強会のQ8の解答です。
また、記事後方に今後の投稿ネタをメモります。
第32回シェル芸勉強会の問題は次のURLにあります。
【問題のみ】jus共催 第32回全くインスタ映えしないシェル芸勉強会 | 上田ブログ
シェル芸勉強会の解答は、思考を忠実にアウトプットすることを目標にしていますが、徐々に雑になってきている気がします。
Q8 問題(上記URLからコピー)
次のようなテキストについて、漢字やカタカナが行頭に来るように改行を入れるワンライナーを考えてください。ただし、「シェル芸」のようにカタカナ+漢字のものは1単語として扱い、改行を入れないでください。この問題については一般解を考えてみましょう。
$ cat japanese.txt ん僕らは既に死んでいる 死んでいるからシェル芸だ。
出力を示します。最初の「ん」は独立した行に出力してください。
ん 僕らは 既に 死んでいる 死んでいるから シェル芸だ。
問題を見た感想・補足
すごい形態素解析したいけど、たぶん役に立たない。
試行1
役に立たないって第1印象だけど良く見たらいけそうです。
「カタカナ+漢字」のところがめんどくさそうですが、↓を見た感じいけそうです。
$ cat japanese.txt | iconv -t euc-jp | mecab | iconv -f euc-jp | sed 's#,.*##' ん 助動詞 僕ら 名詞 は 助詞 既に 副詞 死んで 動詞 いる 接尾辞 EOS 死んで 動詞 いる 接尾辞 から 助詞 シェル 名詞 芸 名詞 だ 判定詞 。 特殊 EOS
解答1(邪道)
$ cat japanese.txt | iconv -t euc-jp | mecab -u shellgei.dic | iconv -f euc-jp | sed 's#,.*##' | grep -v EOS | awk '$2=="名詞"||$2=="動詞"||$2=="副詞"{print b;b=""}{b=b$1}END{print b}' ん 僕らは 既に 死んでいる 死んでいるから シェル芸だ。
解説
今回のデータだと(問題文無視)自立語を前に持ってくるとそれっぽくなりそうです。
awk
で、自立語以外だとバッファに貯めて、自立語が来たらバッファを出力という形で改行します。
$ cat japanese.txt | iconv -t euc-jp | mecab | iconv -f euc-jp | sed 's#,.*##' | grep -v EOS | awk '$2=="名詞"||$2=="動詞"||$2=="副詞"{print b;b=""}{b=b$1}END{print b}' ん 僕らは 既に 死んでいる 死んでいるから シェル 芸だ。
ハードルとなるのは、やはり「カタカナ+漢字」の「シェル芸」です。
自然言語解析は辞書がほぼすべてらしいですし、「シェル芸」を一般的な単語だと認識していないIPAが悪いということで解答完了にしても問題ないと個人的には思います。
が、まぁ問題に書かれている形になっていないのは寝覚めが悪いので、手で辞書に登録してやれば望みの解答が得られるように思います。
まず、辞書のもとになるCSVファイルを作成します。以下が作成したものです。(Ubuntuだと、mecab-ipadic-utf8 をインストールするとmecab
を使っているときからですが、Ubuntu(WSL)でaptできるデフォルトのIPA辞書はEUC-JPなので文字コードはiconv
でよしなに変換しています。
UTF-8で使用できます。 @ebanさんありがとうございます! eban on Twitter: "@nogiro_iota Ubuntuならmecab-ipadic-utf8をインストールすればUTF-8の辞書になりますよ #シェル芸")
$ cat shellgei_dict.csv | iconv -f euc-jp シェル芸,0,0,0,名詞,固有名詞,*,*,*,*,しぇるげい,シェルゲイ,シェルゲイ
$ /usr/lib/mecab/mecab-dict-index -d /var/lib/mecab/dic/debian -u shellgei.dic -f euc-jp -t euc-jp shellgei_dict.csv /var/lib/mecab/dic/debian/pos-id.def is not found. minimum setting is used reading shellgei_dict.csv ... 1 emitting double-array: 100% |###########################################| done!
コンパイルした辞書を使うようmecab
にオプションを付けると解答になります。
$ cat japanese.txt | iconv -t euc-jp | mecab -u shellgei.dic | iconv -f euc-jp | sed 's#,.*##' | grep -v EOS | awk '$2=="名詞"||$2=="動詞"||$2=="副詞"{print b;b=""}{b=b$1}END{print b}' ん 僕らは 既に 死んでいる 死んでいるから シェル芸だ。
こっちのほうがカタカナ・漢字より一般的だから許して!
解答2
cat japanese.txt | tr -d \\n | iconv -t sjis | xxd -ps | tr -d \\n | awk 1 | fold -w 4 | tr a-z A-Z | cat <(echo 'ibase=16') - | bc | awk 'BEGIN{p=0}p==1&&!('$((0x829f))'<=$0&&$0<='$((0x82f1))'){b=b" "$0}p==0&&!('$((0x829f))'<=$0&&$0<='$((0x82f1))'){printf b"\n"; b=$0;p=1}'$((0x829f))'<=$0&&$0<='$((0x82f1))'{b=b" "$0;p=0}' | sed 's#$# '$((0x0a))'#' | tr \ \\n | cat <(echo 'obase=16') - | bc | sed 's#^A$#0A#' | tr -d \\n | tr A-Z a-z | xxd -p -r | iconv -f sjis
解説2
UTF-8はマルチバイトなのでめんどくさいですが、SJISだと2バイト文字でかつ、ひらがなが固まったコードが振られていると期待できます。
文字コード表 シフトJIS(Shift_JIS) を見ると実際にひらがなは、0x8201
~0x8283
であることがわかります。
(今回の問題は、カタカナ・漢字に注目するより、その2集合は、ひらがなの補集合であると考えたほうが単純なのでそう考えています。)
iconv
で文字コードを変換したあとは、10進数にしたり awk
で整形したりしています。
次はawk
の出力です。(解答1のものとほぼ変わりません。自立語・付属語の関係がひらがな以外・ひらがな、になった以外に、ひらがな以外の連続に改行を挿れないようにしているだけです。)
$ cat japanese.txt | tr -d \\n | iconv -t sjis | xxd -ps | tr -d \\n | awk 1 | fold -w 4 | tr a-z A-Z | cat <(echo 'ibase=16') - | bc | awk 'BEGIN{p=0}p==1&&!('$((0x829f))'<=$0&&$0<='$((0x82f1))'){b=b" "$0}p==0&&!('$((0x829f))'<=$0&&$0<='$((0x82f1))'){printf b"\n"; b=$0;p=1}'$((0x829f))'<=$0&&$0<='$((0x82f1))'{b=b" "$0;p=0}' 33521 38508 33511 33485 35577 33481 36480 33521 33477 33442 33513 36480 33521 33477 33442 33513 33449 33511 33622 33606 33675 35964 33470
ここに改行コードをつけたり、バイナリから戻したり、文字コードを戻したりすると、解答になります。
$ cat japanese.txt | tr -d \\n | iconv -t sjis | xxd -ps | tr -d \\n | awk 1 | fold -w 4 | tr a-z A-Z | cat <(echo 'ibase=16') - | bc | awk 'BEGIN{p=0}p==1&&!('$((0x829f))'<=$0&&$0<='$((0x82f1))'){b=b" "$0}p==0&&!('$((0x829f))'<=$0&&$0<='$((0x82f1))'){printf b"\n"; b=$0;p=1}'$((0x829f))'<=$0&&$0<='$((0x82f1))'{b=b" "$0;p=0}' | sed 's#$# '$((0x0a))'#' | tr \ \\n | cat <(echo 'obase=16') - | bc | sed 's#^A$#0A#' | tr -d \\n | tr A-Z a-z | xxd -p -r | iconv -f sjis ん 僕らは 既に 死んでいる 死んでいるから シェル芸だ
今後の投稿ネタについて
アウトプットを習慣づけるのが目的なので、シェル芸勉強会の問題を解き終わっても継続して投稿する予定です(したいなー)。
いま考えてるのは以下です。何もなければ明日は構築しようとしてるおうち仮想環境について投稿します。
- ポチったPCの話 -> おうち仮想環境を構築したい
- Raspberry Pi で3軸加速度を取れるようになった話・目的
- シェル芸関係
- 思いついている別解
- ワンライナーで使うコマンドの逆引き的なもの(「縦に並べたい」→
xargs -n 1
)
第32回シェル芸勉強会 Q7 の解答
前置き
今日は第32回シェル芸勉強会のQ7です。
第32回シェル芸勉強会の問題は次のURLにあります。
【問題のみ】jus共催 第32回全くインスタ映えしないシェル芸勉強会 | 上田ブログ
思考を忠実にアウトプットすることを目標にしています。
Q7 問題(上記URLからコピー)
次のファイルについて、次の処理をやってください。
ある数字について、上下左右の数字どれか1つに0が含まれる場合は0、そうでなければ1にする。
次に、上下左右の数字どれか1つに1が含まれる場合は1、そうでなければ0にする。
$ cat image.txt 00010000000000001000000 00000000111111111110000 01111110011111111110001 00011110001111111111000 10011110001111111111000 00000001000000001000000
正解の出力を示します。
00000000000000001000000 00000000001111111100000 00001100011111111110000 00011110001111111111000 00001100000111111110000 00000000000000001000000
解答1
cat image.txt | sed 's#.#& #g' | awk '{for(i=1;i<=NF;i++){print NR","i","$i}}' | awk -F, '{a[$1][$2]=$3}END{for(k1 in a){for(k2 in a[k1]){print k1","k2","(a[k1-1][k2]&&a[k1+1][k2]&&a[k1][k2-1]&&a[k1][k2+1])}}}' | awk -F, '{a[$1][$2]=$3}END{for(k1 in a){for(k2 in a[k1]){print k1","k2","(a[k1-1][k2]||a[k1+1][k2]||a[k1][k2-1]||a[k1][k2+1])}}}' | sed 's#.*,##' | tr -d \\n | fold -w 23 | awk 1
解説
とりあえず縦横の数を数えます。
$ cat image.txt | wc 6 6 144 $ cat image.txt | sed 1\!d | grep -o . | wc 23 23 46
どうやら 6x23
のようです。
2次元配列っぽいデータだとワンライナーで処理しづらいので、座標-値に変換します。(以下の例だとCSVライクに出力します)
$ cat image.txt | sed 's#.#& #g' | awk '{for(i=1;i<=NF;i++){print NR","i","$i}}' | head 1,1,0 1,2,0 1,3,0 1,4,1 1,5,0 1,6,0 1,7,0 1,8,0 1,9,0 1,10,0
で、次の
ある数字について、上下左右の数字どれか1つに0が含まれる場合は0、そうでなければ1にする。
の結果は座標ごとに、
を求めればいいです。
cat image.txt | sed 's#.#& #g' | awk '{for(i=1;i<=NF;i++){print NR","i","$i}}' | awk -F, '{a[$1][$2]=$3}END{for(k1 in a){for(k2 in a[k1]){print k1","k2","(a[k1-1][k2]&&a[k1+1][k2]&&a[k1][k2-1]&&a[k1][k2+1])}}}'
ザ・ゴリ押しですが、awk
の2次元配列に入れて(結局2次元配列に入れてる。。。)論理積を取ります。
awk
は未定義変数(a[0][1]
など(1オリジンのため0が未定義))を参照したときに0になるので、この問題としては良くないですが、今回のデータには辺に1が3つ並んでいないので大丈夫です。
最後の
次に、上下左右の数字どれか1つに1が含まれる場合は1、そうでなければ0にする。
も同様にゴリ押しして、整形すれば答えになります。(今回は論理和です。)
$ cat image.txt | sed 's#.#& #g' | awk '{for(i=1;i<=NF;i++){print NR","i","$i}}' | awk -F, '{a[$1][$2]=$3}END{for(k1 in a){for(k2 in a[k1]){print k1","k2","(a[k1-1][k2]&&a[k1+1][k2]&&a[k1][k2-1]&&a[k1][k2+1])}}}' | awk -F, '{a[$1][$2]=$3}END{for(k1 in a){for(k2 in a[k1]){print k1","k2","(a[k1-1][k2]||a[k1+1][k2]||a[k1][k2-1]||a[k1][k2+1])}}}' | sed 's#.*,##' | tr -d \\n | fold -w 23 | awk 1 00000000000000001000000 00000000001111111100000 00001100011111111110000 00011110001111111111000 00001100000111111110000 00000000000000001000000
まとめ
awk
によるゴリ押ししか形になりませんでした。
ただ、2次元配列を2回以上パイプするには座標にしておくのが便利でした。
# ホントはtee >(grep -e $((1-1))),$((1)) -e $((1+1))),$((1)) -e $((1))),$((1-1)) -e $((1))),$((1+1)) | cut -d, -f3 ....) | tee ...
をブレース展開で大量に作りたかったですが、最初のgrep
でブレース展開すると座標に戻すにはどうすればいいかわかりませんでした。。。