R で現在のディレクトリーの中身を表示するのに list.files()
とか dir()
とか面倒くさいので ls
だけで何とかしたいというお話です。
RでUNIXコマンド使えるようにするパッケージ無いかしら?getwdの代わりにpwdとか
— akiaki5516 (@doradora09) 2015, 1月 15
ヒントは @hoxo_m 氏のブログ記事『R を終了させる最短コードがおもしろい』にあるように, print
関数にあります。つまり, インタラクティブでオブジェクトを表示する際には print
関数が呼ばれるので, print
関数がシステムコマンドを呼び出すようにしてやれば良いわけです。
print.command <- system | |
print.dev.null <- function(...) {} | |
`[.command` <- function(cmd, ...) { | |
args <- sapply(list(...), shQuote) | |
cmd <- paste(c(cmd, args), collapse=" ") | |
result <- system(cmd) | |
class(result) <- "dev.null" | |
result | |
} | |
as.command <- function(cmd) { | |
e <- as.environment(-1) | |
cmd <- deparse(substitute(cmd)) | |
class(cmd) <- "command" | |
assign(cmd, cmd, envir=e) | |
} |
使用方法は以下のような感じになります。
> source("shell.R") | |
> as.command(ls) | |
> # Just you type "ls" as a command | |
> ls | |
Applications Documents Library ... | |
> # Arguments are passed with brackets | |
> ls["-l", "Documents"] | |
total 1456 | |
drwxr-xr-x 6 user user 204 1 17 20:26 Digital Editions | |
... | |
> # Original ls function also goes | |
> ls() | |
[1] "[.command" "as.command" "print.command" "print.dev.null" |
以下解説。
print.command
は command クラスのオブジェクトに対する print
です。これを system
関数に設定することで, command クラスのオブジェクトを R で表示する際には自身を引数とするシステムコマンドが呼ばれます。
そのコマンドを作るのが as.command
関数です。以下のように呼び出します。
1 | as.command (ls) |
引数として与えられた式 (この場合は ls) の文字列表現を呼び出し元の環境に同名のオブジェクトとして作成します。つまり ls というオブジェクトが "ls" という文字列を持っていることになります。このオブジェクトはそのままでは character クラスなので, command クラスにしてやります。こうすることで, R インタラクティブ上で表示される際に, print.command
が呼び出されるので,コマンドが実行されるという仕組みです。
このままではコマンドにオプションを指定することができないので,オプションを与える仕組みを作ります。最初に考えられるのは command オブジェクトを文字列ではなく関数にするという方法ですが,それでは base パッケージの ls
関数とかぶってしまいますし,オプションを与えないときにも ()
を付ける必要があります。代わりに []
を用いることで, base パッケージの ls
関数とあまり衝突しないようにさせることができます。
[]
の挙動を変えるには, print
関数と同様に, command クラス用の [
関数を作成します。つまり `[.command`
です。最初の引数は command クラスのオブジェクトで,その他をコマンドラインオプションとするために ...
をパラメーターに指定します。そのまま system
関数の結果を返すようにすると,リターンコードがインタラクティブ上に表示されてしまいますので,それを防ぐために何も表示しないための dev.null
クラスを別途作成し, print
で何も表示しないようにしています。
ls["-l", "/"]
のように文字列を与えるのが若干微妙ですが, substitute
が ...
に対して最初の式しかとることができず, ..1
のような番号付き表現はとることができないようなので,文字列を与えるところで妥協しています。
[2015-01-19 02:00 追記] せっかくなのでパッケージ化しました → RaaS