Matt Joiner
Matt Joiner

Reputation: 118600

What's the reverse of shlex.split?

How can I reverse the results of a shlex.split? That is, how can I obtain a quoted string that would "resemble that of a Unix shell", given a list of strings I wish quoted?

Update0

I've located a Python bug, and made corresponding feature requests here.

Upvotes: 45

Views: 12726

Answers (6)

Graham Palmer
Graham Palmer

Reputation: 53

While shlex.quote is available in Python 3.3 and shlex.join is available in Python 3.8, they will not always serve as a true "reversal" of shlex.split. Observe the following snippet:

import shlex
command = "cd /home && bash -c 'echo $HOME'"
print(shlex.split(command))
# ['cd', '/home', '&&', 'bash', '-c', 'echo $HOME']
print(shlex.join(shlex.split(command)))
# cd /home '&&' bash -c 'echo $HOME'

Notice that after splitting and then joining, the && token now has single quotes around it. If you tried running the command now, you'd get an error: cd: too many arguments

If you use subprocess.list2cmdline() as others have suggested, it works nicer with bash operators like &&:

import subprocess
print(subprocess.list2cmdline(shlex.split(command)))
# cd /home && bash -c "echo $HOME"

However you may notice now that the quotes are now double instead of single. This results in $HOME being expanded by the shell rather than being printed verbatim as if you had used single quotes.

In conclusion, there is no 100% fool-proof way of undoing shlex.split, and you will have to choose the option that best suites your purpose and watch out for edge cases.

Upvotes: 0

irdkwmnsb
irdkwmnsb

Reputation: 323

It's shlex.join() in python 3.8

Upvotes: 13

Matthijs Kooijman
Matthijs Kooijman

Reputation: 2797

There is a feature request for adding shlex.join(), which would do exactly what you ask. As of now, there does not seem any progress on it, though, mostly as it would mostly just forward to shlex.quote(). In the bug report, a suggested implementation is mentioned:

' '.join(shlex.quote(x) for x in split_command)

See https://bugs.python.org/issue22454

Upvotes: 11

merwok
merwok

Reputation: 6905

We now (3.3) have a shlex.quote function. It’s none other that pipes.quote moved and documented (code using pipes.quote will still work). See http://bugs.python.org/issue9723 for the whole discussion.

subprocess.list2cmdline is a private function that should not be used. It could however be moved to shlex and made officially public. See also http://bugs.python.org/issue1724822.

Upvotes: 32

tokland
tokland

Reputation: 67900

How about using pipes.quote?

import pipes
strings = ["ls", "/etc/services", "file with spaces"]
" ".join(pipes.quote(s) for s in strings)
# "ls /etc/services 'file with spaces'"

.

Upvotes: 20

Larry Hastings
Larry Hastings

Reputation: 2714

subprocess uses subprocess.list2cmdline(). It's not an official public API, but it's mentioned in the subprocess documentation and I think it's pretty safe to use. It's more sophisticated than pipes.open() (for better or worse).

Upvotes: 6

Related Questions