難読化シェル芸の世界

難読化シェル芸の本を書きました。オンデマンド本なので、ほとんど書店に並びません~
amazonでポチっとお願いします。kindle版もあります。
https://amzn.to/2D0FDNm

マイナビBOOKSでも販売中です。たまにセールがあります。
https://book.mynavi.jp/ec/products/detail/id=103255

BookLiveでも電子書籍で
https://booklive.jp/product/index/title_id/611073/vol_no/001

正誤表

マイナビBOOKS - 「難読化シェル芸の世界 -Bashとすてきな難読化」サポートサイト
https://book.mynavi.jp/supportsite/detail/9784839969691.html

補足

12ページ:ファイル名展開

本書では、当機能を「ファイル名展開」と表記しています。
bashのman pageの原文では"Pathname Expansion"、日本語訳でも"パス名展開"であり、正しくはこちらの名称で記載した方がより適切でした。

13ページ:${変数名/pattern/word}の説明

置換の仕様が複雑です。以下の通り整理しました。

パターン 一致の仕様 sedで同じ動作をさせる場合
${変数名/pattern/word} 最長一致 sed -e "s/pattern/word/"
${変数名//pattern/word} 最長一致 sed -e "s/pattern/word/g"
${変数名/#pattern/word} 最長一致 sed -e "s/^pattern/word/"
${変数名/%pattern/word} 最長一致 sed -e "s/pattern$/word/"

13ページ:${変数名:offset:length}の説明

offset、lengthが負の数の場合を補記します。

offsetですが、マイナスを指定した場合は文法として別のパラメータ展開になります。

# 指定した変数が空文字列の場合は右に指定した文字が入る
$ hoge=
$ echo ${hoge:-fuga}
fuga

lengthは、以下の通りです。

  • 省略した場合はオフセットから最後まで出力
  • マイナスを指定した場合は最後からマイナス分引いた位置までの長さになる
$ hoge=abcdefg
$ echo ${hoge:2}
cdefg
$ echo ${hoge:2:-2}
cde

13ページ:大文字小文字の変換の説明

本書では、大文字小文字の変換を${変数名^}、${変数名^^}、${変数名,}、${変数名,,}と表現していました。正確には${変数名^pattern}という形式になり、patternを省略した形となります。pattern を省略した場合、? を指定したものとして扱われ、 全ての文字にマッチします。

$ hoge=abc
$ echo ${hoge^}
Abc
$ echo ${hoge^^}
ABC
$ echo ${hoge^^b}
aBc
$ echo ${hoge^^bc} # 1文字しか指定できないようです
abc

14ページ:シェル変数について

シェル変数(原文では"Shell Variables")という単語はman pageでは別のニュアンスで使われています。
シェル変数、環境変数を別のものとして表現しているというより、シェル変数が環境変数を包含しているような表現になっています。

環境変数⊂シェル変数

本書においては、性質が異なる二種類の変数をそれぞれ「シェル変数」「環境変数」と定義(別々のものとして扱う)して説明しています。

39ページ:echoの代替

exprでも代替できます。

$ expr "Hello World"
Hello World

23ページ:foldによる文字の折返し

本書では、foldコマンドより折り返しをしていますが、foldコマンドはバイト単位で処理するため、"難読化シェル芸難"が16バイトにならない言語環境では上手く動作しませんでした。grepを利用することで言語環境の違いを考慮することなく処理することができます。

$ yes 難読化シェル芸|head -n8|tr -d '\n'|grep -oE '.{8}'

61ページ:Twitter用Unicodeゼロ幅スペースぶっ込みツール

実は、今のzwsは以下の制限があります。
せっかくなので、こういった入力のパターンも受け付けるように修正したい所です。

ヒアストリングによる入力を受け付けない

$ zws <<<string

ヒアドキュメントによる入力を受け付けない

$ zws <<EOS
string
EOS

修正方法を少し考えてみたいと思います(修正できたらこちらでお知らせします)

71ページ:算術式展開で数値を得る

bashのver5において、算術式展開を評価する仕様が変わったらしく、インクリメントと乗算を組み合わせた式が期待通りに動作しません。

以下の構文はbash ver5ではエラーになります。

$ # bash ver4
$ echo $((++z*++z---z)) # ++z * ++z - --z と解釈される
1
$ # bash ver5
$ echo $((++z*++z---z)) # エラーとなる
-bash: ++z*++z---z: --: assignment requires lvalue (エラーのあるトークンは "---z")

そのため、bash ver5環境において、本書の記号難読化シェル芸(ページ71)を動かすには、以下を置き換える必要があります。

変更前

$((z=z^z))                           #  0 自分自身でXORすると0になる。何回実行しても0。
$((z=z^z||++z))                      #  1 よくわからないが1になる。何回実行しても1。
$((++z*++z---z))                     #  4 変数zの初期値が1前提
$((++z+++z+--z))                     #  7 変数zの初期値が1前提
$((++z*++z*z))                       # 18 変数zの初期値が1前提
$((++z*++z*++z*z-z+++z))             # 19 変数zの初期値が0前提
$((++z*++z*++z---z+++z---z))         # 22 変数zの初期値が1前提
$((++z*++z*++z---z+++z---z+++z---z)) # 23 変数zの初期値が1前提
$((++z*++z*++z))                     # 24 変数zの初期値が1前提
$((++z*++z*++z---z+++z---z+++z))     # 26 変数zの初期値が1前提

変更後(bash ver4,ver5 両方で動作可能)

$((z=z^z))                           #  0 自分自身でXORすると0になる。何回実行しても0。
$((z=z^z||++z))                      #  1 よくわからないが1になる。何回実行しても1。
$((++z+--z+z))                       #  4 変数zの初期値が1前提
$((z+++z+z+z))                       #  7 変数zの初期値が1前提
$((++z*++z*z))                       # 18 変数zの初期値が1前提
$((++z*++z*z+z^z+z))                 # 19 変数zの初期値が0前提
$((++z*++z*z+z^z))                   # 22 変数zの初期値が1前提
$((++z*z*z*z+z^z+++z))               # 23 変数zの初期値が1前提
$((++z*++z*++z))                     # 24 変数zの初期値が1前提
$((++z*++z*++z*z-z*z*z---z-z))       # 26 変数zの初期値が1前提

74ページ:$_について

特殊変数 $_ ですが、以下のような条件においては代入が可能であることを補記しておきます。
確かに_=hogeのような代入は出来ませんが、

$ : 1
$ echo $[++_] $[_+=3] $_
2 5 5

のように、算術式評価で変更したり

$ : ""
$ echo ${_:=123} $_
123 123

のように、パラメタ展開による代入は可能です。

80ページ:${!#}の説明

この部分は明確に誤りであったため、訂正します。${!変数名}は、変数間接展開(indirect expansion)と呼ばれるものになります。結果的に動作は同じでしたが、$!とは無関係の機能です。

変数間接参照展開は、変数の中身を変数名として扱う機能であり、以下の動作になります。

$ fuga=aaa
$ hoge=fuga
$ echo ${!hoge}
aaa
$ eval echo '$'${hoge} # 変数間接参照展開と等価
aaa

改めて、${!#}の動作を解説すると、与えられた引数の数を表す$#に対して、変数間接展開をしていることになります。
この場合、$#は0となりますので、結果として、${!#}は${0}と等価となる訳です。

122ページ:英単語の先頭文字を大文字に変換する

sed単体でも可能であると、読者様よりアドバイスを頂きました。

$ echo foo bar buz|sed 's/\b\([a-z]\)/\U\1/g'
Foo Bar Buz

123ページ:標準入力を受け取る

testコマンドの-pを使う以下の方法ですが、パイプからの入力だけを判定しています。
ヒアドキュメントやヒアストリングなどは受け取らない点、ご留意ください。

$ echo |if [ -p /dev/stdin ];then echo "from pipe";fi
from pipe

SpecialThanks

くおんさん
本書について、多くの指摘・アドバイスを頂きました。

マイナビ連載(マナティ)

難読化シェル芸の世界 Bashとすてきな難読化①
https://book.mynavi.jp/manatee/detail/id=103580

難読化シェル芸の世界 Bashとすてきな難読化②
https://book.mynavi.jp/manatee/detail/id=103731

難読化シェル芸の世界 Bashとすてきな難読化③
https://book.mynavi.jp/manatee/detail/id=103808

難読化シェル芸の世界 Bashとすてきな難読化④
https://book.mynavi.jp/manatee/detail/id=103892

関連ネタ

メディア

クリップボードから画像を追加 (サイズの上限: 100 MB)