我需要从一个完整的POSIX路径创建一个字符串(从根开始),这样它就可以直接粘贴到像bash
这样的Unix shell中,例如在Terminal.app
,不需要在路径周围引用。
(我实际上并没有将字符串传递给shell,而是需要将它传递给另一个程序。该程序只需要将文件拖到Terminal.app
时获得的路径。)
为此,我需要至少在字符串中的任何空格中进行转义,方法是在前面加上反斜杠。还有一些角色。
例如,这条路径:
/directory/-as"<>' *+
将被转义如下:
/directory/-as\"\<\>\'\ \*+
什么是执行转换的安全算法?我可以逃脱每一个角色,但这样就太过分了。
似乎没有用于执行此操作的框架功能,因此我需要使用字符串操作进行替换。
为了保守(对于最流行的shell),同时还要避免明显不必要的转义,应该转义哪些字符集?
最好将整个事物放在单引号中,而不是在单个字符中添加反斜杠;那么你需要逃脱的唯一一个字符是字符串中的单引号。
Python standard library's implementation作为一个例子,可以很容易地用任何其他只有基本原语的语言重新实现,内容如下:
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("'", "'\"'\"'") + "'"
也就是说,一般算法如下:
''
(一对文字单引号)。'
,发出你的输入字符串,所有'
s替换为文字字符串'"'"'
,然后附加一个最终的'
。而已。您不需要转义反斜杠(它们在单引号内是字面值),换行符(同样)或其他任何内容。
为了记录,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;
}