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.'