Sometimes it’s nice to be able to display a command being run and do it in a form that can be repeated from a terminal window. Unfortunately, I’ll build a command in python like this:
args = ['somecmd', 'WHERE=$(DESTDIR)', '--message', "Lot's of text" ]
The trivial answer of ‘ ‘.join (args) produces something that can’t be passed to the shell:
>>> ' '.join (args) "somecmd WHERE=$(DESTDIR) --message Lot's of text"
Here is a code fragment and some attached tests that provide better quoting. It’s used like this:
>>> import shquote >>> args = ['somecmd', 'WHERE=$(DESTDIR)', '--message', "Lot's of text" ] >>> print ' '.join ([shquote.shquote (a) for a in args]) somecmd 'WHERE=$(DESTDIR)' --message "Lot's of text"
#!/usr/bin/env python
def shquote (arg):
"""
Quote a single argument in the most readable way so it is safe from
shell expansion.
"""
# Return an empty string as double quoted
if not arg:
return '""'
special = """ t[]{}()'*?|;""" # These need double quotes
superspecial = """$"!""" # These need single quotes
quotechar = None
# See if we need single quotes
for c in superspecial:
if c in arg:
quotechar = "'"
break
# See if we need double quotes
if not quotechar:
for c in special:
if c in arg:
quotechar = '"'
break
# No quoting necessary
if not quotechar:
return arg
# If quotechar is present then escape it by dropping out of quotes
if quotechar in arg:
arg = arg.replace (quotechar, "%s\%s%s" % (quotechar, quotechar, quotechar))
return quotechar + arg + quotechar
if __name__ == '__main__':
tests = [('', '""'),
('*.cpp', '"*.cpp"'),
('test.[ch]', '"test.[ch]"'),
('(', '"("'),
(')', '")"'),
('a', 'a'),
('$', """'$'"""),
('$a', """'$a'"""),
('abc|def', '"abc|def"'),
('abc;def', '"abc;def"'),
('a b', '"a b"'),
('"abc"', """'"abc"'"""),
("""It's mine""", '"It's mine"'),
('abc | def ABC=$(XYC)', """'abc | def ABC=$(XYC)'"""),
(""""That's impossible!" he said.""", """'"That'\''s impossible!" he said.'"""),
]
for (input, expected) in tests:
output = shquote (input)
if output != expected:
print 'input = ', input
print 'expected = ', expected
print 'got = ', output
else:
print "%-30s => %s" % (input, output)
And the result of running this:
bash-3.2$ ./shquote.py
=> ""
*.cpp => "*.cpp"
test.[ch] => "test.[ch]"
( => "("
) => ")"
a => a
$ => '$'
$a => '$a'
abc|def => "abc|def"
abc;def => "abc;def"
a b => "a b"
"abc" => '"abc"'
It's mine => "It's mine"
abc | def ABC=$(XYC) => 'abc | def ABC=$(XYC)'
"That's impossible!" he said. => '"That'''s impossible!" he said.'