.. versionadded:: 3.8
-.. function:: quote(s)
+.. function:: quote(s, *, force=False)
Return a shell-escaped version of the string *s*. The returned value is a
string that can safely be used as one token in a shell command line, for
cases where you cannot use a list.
+ If *force* is :const:`True`, then *s* is unconditionally quoted,
+ even if it is already safe for a shell without being quoted.
+
.. _shlex-quote-warning:
.. warning::
>>> command
['ls', '-l', 'somefile; rm -rf ~']
+ The *force* keyword can be used to produce consistent behavior when
+ escaping multiple strings:
+
+ >>> from shlex import quote
+ >>> filenames = ['my first file', 'file2', 'file 3']
+ >>> filenames_some_escaped = [quote(f) for f in filenames]
+ >>> filenames_some_escaped
+ ["'my first file'", 'file2', "'file 3'"]
+ >>> filenames_all_escaped = [quote(f, force=True) for f in filenames]
+ >>> filenames_all_escaped
+ ["'my first file'", "'file2'", "'file 3'"]
+
.. versionadded:: 3.3
+ .. versionchanged:: next
+ The *force* keyword was added.
+
The :mod:`!shlex` module defines the following class:
process via a pidfd. Available on Linux 5.6+.
(Contributed by Maurycy Pawłowski-Wieroński in :gh:`149464`.)
+shlex
+-----
+
+* Add keyword-only parameter *force* to :func:`shlex.quote` to force quoting
+ a string, even if it is already safe for a shell without being quoted.
+ (Contributed by Jay Berry in :gh:`148846`.)
+
xml
---
return ' '.join(quote(arg) for arg in split_command)
-def quote(s):
- """Return a shell-escaped version of the string *s*."""
+def quote(s, *, force=False):
+ """Return a shell-escaped version of the string *s*.
+
+ If *force* is *True*, then *s* is unconditionally quoted,
+ even if it is already safe for a shell without being quoted.
+ """
if not s:
return "''"
safe_chars = (b'%+,-./0123456789:=@'
b'ABCDEFGHIJKLMNOPQRSTUVWXYZ_'
b'abcdefghijklmnopqrstuvwxyz')
- # No quoting is needed if `s` is an ASCII string consisting only of `safe_chars`
- if s.isascii() and not s.encode().translate(None, delete=safe_chars):
+ # No quoting is needed if we are not forcing quoting
+ # and `s` is an ASCII string consisting only of `safe_chars`.
+ if (not force
+ and s.isascii() and not s.encode().translate(None, delete=safe_chars)):
return s
# use single quotes, and put single quotes into double quotes
self.assertRaises(TypeError, shlex.quote, 42)
self.assertRaises(TypeError, shlex.quote, b"abc")
+ def testForceQuote(self):
+ self.assertEqual(shlex.quote("spam"), "spam")
+ self.assertEqual(shlex.quote("spam", force=False), "spam")
+ self.assertEqual(shlex.quote("spam", force=True), "'spam'")
+ self.assertEqual(shlex.quote("spam eggs", force=False), "'spam eggs'")
+ self.assertEqual(shlex.quote("spam eggs", force=True), "'spam eggs'")
+ self.assertEqual(shlex.quote("two's-complement", force=False), "'two'\"'\"'s-complement'")
+
def testJoin(self):
for split_command, command in [
(['a ', 'b'], "'a ' b"),
--- /dev/null
+Add keyword-only parameter *force* to :func:`shlex.quote` to force quoting
+a string, even if it is already safe for a shell without being quoted.