Welcome, guest | Sign In | My Account | Store | Cart

Often one has to quote a python string so that the result can be used as an argument to a command running in a POSIX shell.

The function QuoteForPOSIX can be used with sh, bash, csh, ksh

Python, 26 lines
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import re

def QuoteForPOSIX(string):
    '''quote a string so it can be used as an argument in a  posix shell

       According to: http://www.unix.org/single_unix_specification/
          2.2.1 Escape Character (Backslash)

          A backslash that is not quoted shall preserve the literal value
          of the following character, with the exception of a <newline>.

          2.2.2 Single-Quotes

          Enclosing characters in single-quotes ( '' ) shall preserve
          the literal value of each character within the single-quotes.
          A single-quote cannot occur within single-quotes.

    '''

    return "\\'".join("'" + p + "'" for p in string.split("'"))

if __name__ == "__main__":
    import os

    filename = "filename with spaces.doc"
    os.system("ls " + QuoteForPOSIX(filename))

10 comments

Rafał Śnieżyński 17 years, 5 months ago  # | flag

Isn't the quoting of subprocess.list2cmdline safe enough?

Richard Philips (author) 17 years, 5 months ago  # | flag

Use of list2cmdline. list2cmdline has two 'problems':

(1) It is not mentioned in the Python documentation (a sure sign that the author prefers to keep the use of it private and does not want to take the burden of maintaining it)

(2) From the code, it is clearly for use in a Microsoft only environment. QuoteForPOSIX is intended for UNIX environments.

The subprocess module is a goldmine for little nuggets like list2cmdline and I really hope the author would promote it.

Matthew Towler 17 years, 4 months ago  # | flag

Code does not work. The original code does not run. 'p' is used before the loop that declares it. I belive the following code does the same thing and does work.

def quote_for_POSIX(string):

    output = "'"
    output += string.replace( "'", r"\'" )
    output += "'"

    return output

As a side issue I have found that if using file names escaped this way as the command line in a call to os.popen() the quoting is not respected and a name with spaces is incorrectly interpreted as multiple arguments.

Steve Freitas 17 years, 2 months ago  # | flag

Unnecessary import. The re module isn't actually used in the code, so the import of it can be omitted.

Daryl Spitzer 17 years, 1 month ago  # | flag

replace() vs. split() and join()? Are split() and join() more efficient than something like:

return "'%s'" % string.replace( "'", r"'\''" )

or

return "'" + string.replace( "'", r"'\''" ) + "'"

?

Damon Kohler 16 years, 5 months ago  # | flag

repr works well too. Python does some nice escaping on its own in this case. This works well for me:

msg = "keep's it simple."
os.system('echo %r' % msg)
Pádraig Brady 14 years, 11 months ago  # | flag

All the above is wrong since for sh, \' is not valid within single quotes. The "pipes" module available since python 1.6 has a handy function to do the right thing.

import pipes, os
os.system('echo %s' % pipes.quote(r"\<&\"'"))
Pádraig Brady 14 years, 8 months ago  # | flag

Another hack that is not as nice as the pipes.quote() mentioned above is to, allow ' within single quotes by them with '"'"'. I.E. you could quote the string like:

"'%s'" % cmd.replace("'","'\"'\"'")
John Wiseman 14 years, 3 months ago  # | flag

Note that pipes.quote is broken, it does not handle zero-length arguments. That is, pipes.quote('') => '', but it should be something like "''".

Glyph Lefkowitz 11 years, 9 months ago  # | flag

The flaw you describe in pipes.quote was fixed in Python 2.6.