diff options
author | Éric Araujo <merwok@netwok.org> | 2011-07-27 16:29:31 (GMT) |
---|---|---|
committer | Éric Araujo <merwok@netwok.org> | 2011-07-27 16:29:31 (GMT) |
commit | 9bce311ea4f58ec04cab356a748e173ecfea381c (patch) | |
tree | fd767a62dab42bb79ce2e86a2ac6fb970930c10e /Lib | |
parent | fcdaaa9011be28d4653dadc92df4a94b2f669711 (diff) | |
download | cpython-9bce311ea4f58ec04cab356a748e173ecfea381c.zip cpython-9bce311ea4f58ec04cab356a748e173ecfea381c.tar.gz cpython-9bce311ea4f58ec04cab356a748e173ecfea381c.tar.bz2 |
Add shlex.quote function, to escape filenames and command lines (#9723).
This function used to live as pipes.quote, where it was undocumented but
used anyway. (An alias still exists for backward compatibility.) The
tests have been moved as is, but the code of the function was changed to
use a regex instead of a loop with string comparisons (at Ian Bicking’s
suggestion). I’m terrible at regexes, so any feedback is welcome.
Diffstat (limited to 'Lib')
-rw-r--r-- | Lib/pipes.py | 23 | ||||
-rw-r--r-- | Lib/shlex.py | 20 | ||||
-rw-r--r-- | Lib/test/test_pipes.py | 14 | ||||
-rw-r--r-- | Lib/test/test_shlex.py | 20 |
4 files changed, 39 insertions, 38 deletions
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): |