クォーテーションを解さないコマンド実行インターフェースで任意のシェルスクリプトを実行する

目的

# pyonpyon.py
import sys, os, re
cmd = re.split(r'\s+', sys.stdin.read().strip())
os.execvp(cmd[0], cmd)
% python3 pyonpyon.py <<<'date'
Sat Apr  4 16:52:55 JST 2015
% python3 pyonpyon.py <<<'echo kokoro pyonpyon'
kokoro pyonpyon

例えばこういうインターフェースがあったとする。 単に execvp() やってるだけなので、シェルスクリプトのようなこと (コマンドを複数実行、パイプ、リダイレクト、etc...)は普通にはできない。

% python3 pyonpyon.py <<<'sh -c "echo kokoro pyonpyon"'
kokoro: -c: line 0: unexpected EOF while looking for matching `"'
kokoro: -c: line 1: syntax error: unexpected end of file

これはクォーテーション中のスペースでも分解されてしまうので、 sh -c '"echo' kokoro 'pyonpyon"' と解される。

$'\nnn' が使える場合

python3 pyonpyon.py <<<"sh -c sh<<<echo\$'\\040'kokoro\$'\\040'pyonpyon"
kokoro pyonpyon

sh<<<$'\nnn' が使える必要がある。 今回やりたかった busybox 環境ではいずれも使えなかった。

# sh<<<echo$'\040'kokoro
syntax error
# echo echo$'\040'kokoro|sh
echo040kokoro: not found

IFS 環境変数が使える場合

# busybox sh -c 'IFS=@;echo@kokoro@pyonpyon'
kokoro pyonpyon

shbash へのシンボリックリンクになっているような今風の環境では使えない。

% sh -c 'IFS=@;echo@kokoro@pyonpyon'
sh: echo@kokoro@pyonpyon: command not found
% ls -l /bin/sh
lrwxrwxrwx 1 root root 4 Oct  8 23:09 /bin/sh -> bash

あとがき

二つしか思い付かなかったが、今回やりたかったシステムでは IFS の方で上手く動いたので、まあよし。

スペースさえどうにかできればよいのでごく簡単な例しか書かなかったが、 あとは sh に投げるときに ()$;| 等々をきちんとエスケープすればだいたいなんとかなる。