在 macOS 中创建外壳转义的 POSIX 路径



我需要从完整的POSIX路径(从根目录开始)创建一个字符串,以便它可以像bash一样直接粘贴到Unix shell中,例如在Terminal.app中,而无需在路径周围引起来。

(我实际上并没有将字符串传递给 shell,而是需要它将其传递给另一个程序。该程序需要的路径与您将文件拖入Terminal.app时获得的形式相同。

为此,我需要至少转义字符串中的任何空格,方法是在它们前面加上反斜杠。还有更多的角色。

例如,此路径:

/directory/-as"<>' *+

将按如下方式进行转义:

/directory/-as"<>' *+

执行该转换的安全算法是什么?我可以逃避每个角色,但那太过分了。

似乎没有框架函数来执行此操作,因此我需要用字符串操作进行替换。

为了保守(对于最流行的外壳),同时避免明显不必要的转义,应该转义哪一组字符?

作为记录,Terminal.app将文件名放入其窗口时转义以下非控制 ASCII 字符:

空间

!"#$%&'()*,:;<=>?[]`{|}~

这些是无法逃脱的:

控制代码(00-1F 和 7F)

字母数字

+-.@^_

以下是将执行替换的代码:

NSString* shellPathFromPOSIXPath (NSString *path)
{
static NSRegularExpression *regex = nil;
if (!regex) {
NSString *pattern =
@"([ !\"\#\$\%\&\'\(\)\*\,\:\;\<\=\>\?\[\]\`\{\|\}\~])";
regex =
[NSRegularExpression regularExpressionWithPattern:pattern options:0 error:nil];
}
NSString *result =
[regex stringByReplacingMatchesInString:path
options:0
range:NSMakeRange(0, path.length)
withTemplate:@"\\$1"];
return result;
}

最好将整个内容放在单引号中,而不是为单个字符添加反斜杠; 那么你需要转义的唯一字符是字符串中存在的单引号。

Python 标准库的实现,作为一个示例提供,可以在任何其他只有基本原语的语言中轻松重新实现,如下所示:

def quote(s):
"""Return a shell-escaped version of the string *s*."""
if not s:
return "''"
if _find_unsafe(s) is None:
return s
# use single quotes, and put single quotes into double quotes
# the string $'b is then quoted as '$'"'"'b'
return "'" + s.replace("'", "'"'"'") + "'"

也就是说,一般的算法如下:

  • 空字符串变为''(一对文本单引号)。
  • 已知安全的字符串(尽管最安全的是不尝试为此实现代码路径,特别是因为 shell 通常在未定义的空间中实现自己的语法扩展)可以裸露/不引号发出。
  • 否则,请预置一个',发出输入字符串,将所有'替换为文字字符串'"'"',然后附加最后一个'

就是这样。您不需要转义反斜杠(它们是单引号内的文字)、换行符(同样)或其他任何内容。

最新更新