summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Doc/library/shlex.rst19
-rw-r--r--Doc/library/subprocess.rst7
-rw-r--r--Lib/pipes.py23
-rw-r--r--Lib/shlex.py20
-rw-r--r--Lib/test/test_pipes.py14
-rw-r--r--Lib/test/test_shlex.py20
-rw-r--r--Misc/NEWS5
7 files changed, 66 insertions, 42 deletions
diff --git a/Doc/library/shlex.rst b/Doc/library/shlex.rst
index 0113fb7..e5aec4a 100644
--- a/Doc/library/shlex.rst
+++ b/Doc/library/shlex.rst
@@ -34,6 +34,22 @@ The :mod:`shlex` module defines the following functions:
passing ``None`` for *s* will read the string to split from standard
input.
+
+.. function:: quote(s)
+
+ 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.
+ Examples::
+
+ >>> filename = 'somefile; rm -rf /home'
+ >>> command = 'ls -l {}'.format(quote(filename))
+ >>> print(command)
+ ls -l 'somefile; rm -rf /home'
+ >>> remote_command = 'ssh home {}'.format(quote(command))
+ >>> print(remote_command)
+ ssh home 'ls -l '"'"'somefile; rm -rf /home'"'"''
+
+
The :mod:`shlex` module defines the following class:
@@ -282,5 +298,4 @@ parsing rules.
* EOF is signaled with a :const:`None` value;
-* Quoted empty strings (``''``) are allowed;
-
+* Quoted empty strings (``''``) are allowed.
diff --git a/Doc/library/subprocess.rst b/Doc/library/subprocess.rst
index 7e759f0..2c76130 100644
--- a/Doc/library/subprocess.rst
+++ b/Doc/library/subprocess.rst
@@ -92,7 +92,8 @@ This module defines one class called :class:`Popen`:
>>> call("cat " + filename, shell=True) # Uh-oh. This will end badly...
*shell=False* does not suffer from this vulnerability; the above Note may be
- helpful in getting code using *shell=False* to work.
+ helpful in getting code using *shell=False* to work. See also
+ :func:`shlex.quote` for a function useful to quote filenames and commands.
On Windows: the :class:`Popen` class uses CreateProcess() to execute the
child program, which operates on strings. If *args* is a sequence, it will
@@ -871,3 +872,7 @@ runtime):
described in rule 3.
+.. seealso::
+
+ :mod:`shlex`
+ Module which provides function to parse and escape command lines.
diff --git a/Lib/pipes.py b/Lib/pipes.py
index 51666a8..693309f 100644
--- a/Lib/pipes.py
+++ b/Lib/pipes.py
@@ -62,7 +62,9 @@ For an example, see the function test() at the end of the file.
import re
import os
import tempfile
-import string
+# we import the quote function rather than the module for backward compat
+# (quote used to be an undocumented but used function in pipes)
+from shlex import quote
__all__ = ["Template"]
@@ -245,22 +247,3 @@ def makepipeline(infile, steps, outfile):
cmdlist = trapcmd + '\n' + cmdlist + '\n' + rmcmd
#
return cmdlist
-
-
-# Reliably quote a string as a single argument for /bin/sh
-
-# Safe unquoted
-_safechars = frozenset(string.ascii_letters + string.digits + '@%_-+=:,./')
-
-def quote(file):
- """Return a shell-escaped version of the file string."""
- for c in file:
- if c not in _safechars:
- break
- else:
- if not file:
- return "''"
- return file
- # use single quotes, and put single quotes into double quotes
- # the string $'b is then quoted as '$'"'"'b'
- return "'" + file.replace("'", "'\"'\"'") + "'"
diff --git a/Lib/shlex.py b/Lib/shlex.py
index 3edd3db..279ab48 100644
--- a/Lib/shlex.py
+++ b/Lib/shlex.py
@@ -6,13 +6,14 @@
# Posix compliance, split(), string arguments, and
# iterator interface by Gustavo Niemeyer, April 2003.
-import os.path
+import os
+import re
import sys
from collections import deque
from io import StringIO
-__all__ = ["shlex", "split"]
+__all__ = ["shlex", "split", "quote"]
class shlex:
"A lexical analyzer class for simple shell-like syntaxes."
@@ -274,6 +275,21 @@ def split(s, comments=False, posix=True):
lex.commenters = ''
return list(lex)
+
+_find_unsafe = re.compile(r'[^\w\d@%_\-\+=:,\./]').search
+
+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("'", "'\"'\"'") + "'"
+
+
if __name__ == '__main__':
if len(sys.argv) == 1:
lexer = shlex()
diff --git a/Lib/test/test_pipes.py b/Lib/test/test_pipes.py
index f2b58d5..6a7b45f 100644
--- a/Lib/test/test_pipes.py
+++ b/Lib/test/test_pipes.py
@@ -79,20 +79,6 @@ class SimplePipeTests(unittest.TestCase):
with open(TESTFN) as f:
self.assertEqual(f.read(), d)
- def testQuoting(self):
- safeunquoted = string.ascii_letters + string.digits + '@%_-+=:,./'
- unsafe = '"`$\\!'
-
- self.assertEqual(pipes.quote(''), "''")
- self.assertEqual(pipes.quote(safeunquoted), safeunquoted)
- self.assertEqual(pipes.quote('test file name'), "'test file name'")
- for u in unsafe:
- self.assertEqual(pipes.quote('test%sname' % u),
- "'test%sname'" % u)
- for u in unsafe:
- self.assertEqual(pipes.quote("test%s'name'" % u),
- "'test%s'\"'\"'name'\"'\"''" % u)
-
def testRepr(self):
t = pipes.Template()
self.assertEqual(repr(t), "<Template instance, steps=[]>")
diff --git a/Lib/test/test_shlex.py b/Lib/test/test_shlex.py
index 25e4b6d..ea3d777 100644
--- a/Lib/test/test_shlex.py
+++ b/Lib/test/test_shlex.py
@@ -1,6 +1,7 @@
-import unittest
-import os, sys, io
+import io
import shlex
+import string
+import unittest
from test import support
@@ -173,6 +174,21 @@ class ShlexTest(unittest.TestCase):
"%s: %s != %s" %
(self.data[i][0], l, self.data[i][1:]))
+ def testQuote(self):
+ safeunquoted = string.ascii_letters + string.digits + '@%_-+=:,./'
+ unsafe = '"`$\\!'
+
+ self.assertEqual(shlex.quote(''), "''")
+ self.assertEqual(shlex.quote(safeunquoted), safeunquoted)
+ self.assertEqual(shlex.quote('test file name'), "'test file name'")
+ for u in unsafe:
+ self.assertEqual(shlex.quote('test%sname' % u),
+ "'test%sname'" % u)
+ for u in unsafe:
+ self.assertEqual(shlex.quote("test%s'name'" % u),
+ "'test%s'\"'\"'name'\"'\"''" % u)
+
+
# Allow this test to be used with old shlex.py
if not getattr(shlex, "split", None):
for methname in dir(ShlexTest):
diff --git a/Misc/NEWS b/Misc/NEWS
index 857acce..3cd8018 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -237,6 +237,9 @@ Core and Builtins
Library
-------
+- Issue #9723: Add shlex.quote functions, to escape filenames and command
+ lines.
+
- Issue #12607: In subprocess, fix issue where if stdin, stdout or stderr is
given as a low fd, it gets overwritten.
@@ -6674,4 +6677,4 @@ Docs
----
-**(For information about older versions, consult the HISTORY file.)** \ No newline at end of file
+**(For information about older versions, consult the HISTORY file.)**