スペースの入ったパスの賢いお取り扱い

【Macターミナル】よく使うフォルダだから、とにかく楽して移動したい | 林檎コンピュータ

という記事を書いていて気づいたのだが、標準出力にパスを出して cd などをしたい時、パスにスペースが入っているとうまくいかない。
 なんのことかという話だが、

path=~/'my folder'
cd $(echo $path)

だと、移動に失敗する、という話なのだ。

スペースを含むパス

ターミナルを使い始めたころ、よくつまずいたのは、パスに入ったスペースである。bash や zsh は、半角スペースを区切りと見なし、スペース以後の文字列をオプションやら引数として扱う。
 しかし回避は簡単なのだ。
 テスト用に、デスクトップに「space 入ってる」というフォルダを作成する。
 mkdir ~/Desktop/'space 入ってる'

スペースは、バックスラッシュを前置することでエスケープできるはず。

name@MacBook-Air ~
$ cd ~/Desktop/space\ 入ってる
name@MacBook-Air ~/Desktop/space 入ってる
$

移動できた。
 ほかにも、クオートやダブルクオートで囲めば移動できるはず。
 ホームを表わすチルダは、クオートで囲むと展開できないので、以下のように書いた。

cd ~/Desktop/'space 入ってる'
# -> 移動できる
cd "$HOME/Desktop/space 入ってる"
# -> 移動できる

標準出力?を引数に使うと失敗する

ここまではいい。
 筆者がやりたいのは、以下のようなことだ。

path="$HOME/Desktop/space 入ってる"
cd $(echo $path)

実行すると以下のようになる。

name@MacBook-Air ~
$ path="$HOME/Desktop/space 入ってる"
name@MacBook-Air ~
$ cd $(echo "$path")
cd: string not in pwd: /Users/username/Desktop/space

スペースの箇所で切れてしまい、移動できない。
 しかしバックスラッシュを使えばいけるはず。
 変数に:qをくっつければエスケープされた文字列を出力してくれる。

$ echo $path:q
/Users/username/Desktop/space\ 入ってる

これをふまえて、以下のように書いた。

$ cd $(echo $path:q)
cd: string not in pwd: /Users/username/Desktop/space\

駄目みたい。
 ダブルクオートを試してみる。

$ cd $(echo \"$path\")
cd: string not in pwd: "/Users/username/Desktop/space

なぜか駄目。
 変数 path を書き直してシングルクオートでもやってみる。

$ path='/Users/username/Desktop/space 入ってる'
$ cd $(echo \'$path\')
cd: string not in pwd: '/Users/username/Desktop/space

どうしても駄目。
 バックスラッシュもクオートも関係ねぇ、スペースをまず評価するんだ、とでもいうような固陋な態度である。でなければ、筆者がなにか勘違いしているのだろう。

eval コマンド

この問題は eval コマンドで解決する。

$ path="$HOME/Desktop/space 入ってる"
$ eval cd $(echo $path:q)
# -> 移動できる
$ eval cd $(echo \'$path\')
# -> 移動できる
$ eval cd $(echo \"$path\")
# -> 移動できる

eval コマンドというのは、

「eval 文字列」で文字列をコマンドとして実行します。「eval 文字列1 文字列2 ……」のように文字列を複数指定した場合、スペースをはさんで連結後、シェルに実行させます。
【 eval 】コマンド――文字列を評価、連結して実行する:Linux基本コマンドTips(170) – @IT

とのこと。
 echo が出力する文字列だと、単なる平文みたいに扱われてクオートもバックスラッシュも意味を成さないのかもしれない。「スペースをはさんで連結後」というところがポイントっぽい。
 ということは、

pic='cd ~/Pictures'
eval $pic

とやれば、ピクチャフォルダへ移動できるはずだ。実際に試すと移動できる。
 なんか凄い。知っていればいろいろ役に立ってくれそうなコマンドだと思う。