Create a shell-escaped POSIX path in macOS

一世执手 提交于 2019-12-25 03:03:33

问题


I need to create a string from a full POSIX path (starting at the root), so that it could be pasted directly into a Unix shell like bash, e.g. in Terminal.app, without the need for quotes around the path.

(I do not actually pass the string to a shell, but instead need it for passing it to another program. That program expects the path in just the form that you get when you drag a file into Terminal.app.)

For that, I need to escape at least any spaces in the string, by prepending them with a backslash. And some more characters as well.

For example, this path:

/directory/-as"<>' *+

Would be escaped as follows:

/directory/-as\"\<\>\'\ \*+

What's a safe algorithm to perform that conversion? I could escape every character, but that would be overkill.

There seems to be no framework function for doing this, so I'll need to do the replacing with string operations.

To be conservative (for the most popular shells), while also avoiding clearly unnecessary escapings, what set of characters should be escaped?


回答1:


Better to put the whole thing in single quotes, rather than adding backslashes to individual characters; then the only character you need to escape is a single-quote present inside the string.

The Python standard library's implementation, provided as an example which can be easily reimplemented in any other language having only basic primitives, reads as follows:

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("'", "'\"'\"'") + "'"

That is to say, the general algorithm is as follows:

  • An empty string becomes '' (a pair of literal single-quotes).
  • A string which is known to be safe (though it's safest to not try to implement a codepath for this at all, particularly as shells often implement their own syntax extensions in undefined space) can be emitted bare/unquoted.
  • Otherwise, prepend a ', emit your input string with all 's replaced with the literal string '"'"', and then append a final '.

That's it. You don't need to escape backslashes (they're literal inside single quotes), newlines (likewise), or anything else.




回答2:


For the record, Terminal.app escapes the following non-control ASCII chars when dropping a file name into its window:

Space

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

And these are not escaped:

Control codes (00-1F and 7F)

Alphanumerical

+-.@^_

And here's the code that would perform the replacement:

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;
}


来源:https://stackoverflow.com/questions/55381517/create-a-shell-escaped-posix-path-in-macos

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!