・Shellのクォート
自分で作るファイルではそんな記号は使わないので気にしていなかったのだけど、YouTubeのダウンローダなんてのを書いていると、 いろんな記号を含むファイル名が届くことがあるので、「うーむ、、」と首を捻ることもしばしば。
最近、気がついたのが ' や " を含むファイル名の処理で、'(single quote)はshellの世界では変数展開しないクォート、 と記憶しているのだけど、' を含む文字列を ' で括るのはうまく行かない感じ。
$ echo "ABC'D" ABC'D $ echo 'ABC'D' > bash: unexpected EOF while looking for matching `'' bash: syntax error: unexpected end of file
まぁ、'..' の中に ' があると、文字列中の ' で最初の ' が閉じる、と判断されるのは理解できるのだけど、 この文字列中の ' はエスケープとかも効かない感じ。
$ echo 'ABC\'D' > bash: unexpected EOF while looking for matching `'' bash: syntax error: unexpected end of file $ echo 'ABC\\\'D' > bash: unexpected EOF while looking for matching `'' bash: syntax error: unexpected end of file
まぁ、' はその内部の特殊記号を一切展開しない、というルールに従えば、\(バックスラッシュ)のエスケープ機能も 利用できなくなるから、そうすると、' を含む文字列を ' ... ' に含むことは不可能なのかな?
具体的には、YouTubeからダウンロードしてきたファイル名を file コマンドに食わせて、動画の種類を判断するような コードを Python で書いてみたのだけど、YouTubeに登録されているファイル名って特殊記号使いまくりだから、当初は ' でクォートして 特殊記号を展開しないようにして file コマンドに食わせていたのだが、ファイル名中に ' があると、さて困った、的な状況。
まぁ、最初の例のように " でクォートしてやれば ' も問題なく処理できそうなので、ファイル名中に ' があれば、クォート記号は " にする、 みたいな処理で当面はしのげそうだけど、ファイル名中に ' と " があればさてどうしたものか。。。 原理的には、" でクォートして、ファイル名中の " は \ でエスケープする、という形になりそうだけど、 事前にそういうルールを書くよりは、実際にそんなファイル名に遭遇してから考えた方が効率はよさそうだな(苦笑
$ echo $'ABC\'D$EFGH"IJKL"' ABC'D$EFGH"IJKL"
"\'"でシングルクォート,"\t"でタブ,"\n"で改行などを表現できます。 -- tamu 2010-09-06 (月) 06:43:52
% echo $'ABC\'D$EFGH"IJKL"' 変数名が不正ですー. % bash $ echo $'ABC\'D$EFGH"IJKL"' ABC'D$EFGH"IJKL"
インストーラやpkgtool類はashレベルの機能しか使えないから、これも含めてbashの拡張機能は全然把握できてないなぁ(苦笑 -- kojima 2010-09-06 (月) 09:42:47
#!/bin/sh hoge=`cat <<- "EOF" ABC'D $EFGH "IJKL" EOF ` echo "$hoge"
こちらは,文字によってエスケープするかどうか気にしなくて良いです。ファイル名に空白やタブが入っていても大丈夫。 -- tamu 2010-09-06 (月) 10:41:32
He said "It's $250". ↓ 'He said "It'"'"'s $250".'つまり、 foo'bar → 'foo' + "'" + 'bar' って感じですね。可読性はよくないですが、'を '"'"' に機械的に置き換えればいいだけだし、他の部分が誤って展開される心配とかしなくていいので、スクリプト内なんかではよく使います。 -- 川俣 2010-09-06 (月) 12:12:36
def check_quote(file): quoted_file = file.replace("'", "'\"'\"'") return "'" + quoted_file + "'"
みたいな感じでよさそう。 -- kojima 2010-09-08 (水) 12:58:58
sub shquote { my($word) = shift; $word =~ s/('+)/'"$1"'/g; return "'$word'"; }シングルクオートが連続して現れた場合は、まとめてクオートするようにしてみました。 -- 川俣 2010-09-08 (水) 19:36:45