为 bash 转义文件名

最后更新日期:2012-03-31

  最近遇到的一个问题:程序中有一个文件名,需要把这个文件名放在 shell 中执行,但文件名中可能包含特殊字符,所以需要转义。

  比如,如果文件名是:

[SumiSora&CASO&HKG][Tears_to_Tiara][02][GB].rmvb

  这个文件名肯定不能直接放到 bash 中的,因为“&”和 [ 、] 等都是 bash 的特殊字符。

  bash 的自动补全默认采用反斜线转义:

\[SumiSora\&CASO\&HKG\]\[Tears_to_Tiara\]\[02\]\[GB\].rmvb

  或者用单引号转义:

'[SumiSora&CASO&HKG][Tears_to_Tiara][02][GB].rmvb'

  所以问题是,如何正确地实现转义?

  经过一些搜索:

  找到了两个东西可以实现这个功能:

  1. python 3.3a 的 shlex.quote
  2. bash 的内置命令 printf “%q” str(这货可不是 coreutils 的 printf !)

  但既然要实现 bash 的文件名转义,没有什么比 bash 本身的代码更权威的了。于是下载了 bash-4.2 的源代码来看。先花了很多时间定位,最终定位到 builtins/printf.def 这个文件,在大约 500 多行 case 'q' 的部分调用了以下函数:

  前两个函数在 lib/sh/strtrans.c 中,后一个函数在 lib/sh/shquote.c 中。所以最后终于定位到 shquote.c 这个文件。

  1. 如果要使用单引号转义,那么使用 sh_single_quote 的算法
  2. 想用反斜线转义,那么使用 sh_blackslash_quote 的算法

  这两个函数的代码如下:

  单引号转义:

  用 lua 实现如下:

  由于在单引号里面再用 \’ 转义单引号也是非法的(我想这是因为单引号里面连 \ 也不是特殊字符),所以对于文件名里面出现的单引号,必须先结束上一个串,插入单引号,再开始下一个串。

  反斜线转义: