Help us understand the problem. What is going on with this article?

難読化シェル芸の解読: 1

はじめに

全体

post
# https://twitter.com/hiro_poke91/status/1218084119504048128
.>&$
"$(<$)"
! __="${_##*. ??}"
$?
___=${__:$_$_:_}${__:~_<<_:_}${__:~_:_}${__:_:_}${__:~_-_:_} __=${__::_}${__:$#$_$#:_}${__:~_<<_:_}${__:$_$#:_}
$___ ${__^^}
(($?=$?))>&$
_=($(<$))
${_[${##}$#>>++_]:~$#}${_[$_$#/++_]::_}${_[_**++_]:~$#}

返り値は

result
# https://twitter.com/minyoruminyon/status/121808415852362957
Fri Jan 17 17:14:26 JST 2020

どうやらロケールが英語圏のdateらしい
???なんだこれ おかしいよ

解読開始

.>&$

  • これを実行すると$というファイルに.コマンドのエラー文が書き込まれる
$
/task.sh: 1 行: .: ファイル名が引数として必要です
.: 使用法: . filename [arguments]
  • >&StdOut+StdErrのリダイレクト

"$(<$)"

  • cmd <filecmdfileの内容を入力する
  • ここでは<$のコマンドがないので、catを入力先としているので$(cat $)と同値
  • そのため"$の中身"が実行され変数_(後述)に格納される(Thanks:@qwertanusさん)
  • ちなみにファイル入出力はどこでやっても良い
example
echo unko >unko
>unko echo unko
echo >unko unko

! __="${_##*. ??}"

  • 変数_は現在実行しているシェルを格納する(コマンド名とかshファイル名)
  • 前の行で実行された$の中身が変数_に格納されている
  • そしてこの行を実行すると変数__lename [arguments]が代入される
  • ${v##exp}vの値のexpにマッチした部分以降を最長一致で取り出す展開
  • ここで入力されたファイル$*. ??がマッチするのは以下
matched
/task.sh: 1 行: .: ファイル名が引数として必要です
.: 使用法: . fi
  • これ以降のlename [arguments]が変数__に代入される
  • 行頭の!は代入後の終了ステータス0を否定/反転させ1にしている
  • よって次行の$?1となる(Thanks:@qwertanusさん)

$?

  • 変数?は直前のコマンドの終了ステータスの参照
  • ここでは1が返る
  • そして1という名前のコマンドは当然存在しないので127が変数?に格納される
  • またここで変数_1が格納される(次で使用)

___=${__:$_$_:_}${__:~_<<_:_}${__:~_:_}${__:_:_}${__:~_-_:_}

  • $_1, $__lename [arguments](18文字)
  • これを実行すると変数__unsetが代入される
  • ${v:offset:length}vの値のoffsetのインデックスからlength文字分切り出す展開
  • Pythonだとv[offset:length]みたいな
  • ~はビット反転, <<は左ビットシフト
  • ___=${__:11:1}${__:-4:1}${__:-2:1}${__:1:1}${__:-3:1}
  • よって___=unsetとなる

__=${__::_}${__:$#$_$#:_}${__:~_<<_:_}${__:$_$#:_}

  • $_1, $__lename [arguments](18文字)
  • 変数#は関数の引数の数で、今は関数内でないので0が返る
  • 上と同様に、__=${__::1}${__:010:1}${__:-4:1}${__:10:1}
  • 010(8)=8(10)(Thanks:@hiroさん)
  • offsetを省略すると0番目からとなる
  • よって__=langとなる

$___ ${__^^}

  • ${v^^}はvの値の小文字を全て大文字に変換する展開(upcase)
  • unset LANGとなる
  • シェル芸Botのロケール(LANGの設定値)はja_JP.UTF-8
  • 解除するとコマンドのデフォルトでのロケールで実行されるのでdateが英語に

(($?=$?))>&$

  • (())は算術演算を行う
  • ここでは$?=$?0=0となり、00の代入を行う
  • 非変数への代入は不正なので、エラーが起き、そのメッセージがファイル$に書き込まれる
$
/task.sh: line 7: ((: 0=0: attempted assignment to non-variable (error token is "=0")

_=($(<$))

  • var=()は配列の宣言
  • ここでは$の内容がスペースで区切りで配列_に代入される
  • 参照は${arr[index]}で行う

${_[${##}$#>>++_]:~$#}${_[$_$#/++_]::_}${_[_**++_]:~$#}

  • _=('' line 7: ((: 0=0: attempted assignment to non-variable (error token is "=0"))
  • 0番目は空文字列(Thanks:@hiroさん)
  • ${v:offset}はvの値のoffsetのインデックスから末尾までを切り出す展開
  • ++はインクリメント, >>は右ビットシフト
terminal
root@628d738436d9:/# echo $[++a]
1
root@628d738436d9:/# echo $[++a]
2
root@628d738436d9:/# echo $[++a]
3
  • ${#v}はvの文字列長を返す展開で、vが配列なら${#v[*]}で配列の要素数が返る
  • ${##}$#の長さ1が返る
  • ${_[10>>1]:-1}${_[10/2]::2}${_[2**3]:-1}
  • ${_[5]:-1}${_[5]::2}${_[8]:-1}dateが返り実行される

結論

こわ

追記:あわせて読みたい本質情報群

eggplants
I WANNA EAT LOTS OF EGGPLANT!!!
https://github.com/eggplants
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした