Tilde (~) isn't working in subprocess.Popen()

Question:

When I run in my Ubuntu terminal:

sudo dd if=/dev/sda of=~/file bs=8k count=200k; rm -f ~/file

it works fine.

If I run it through Pythons subprocess.Popen():

output, err = subprocess.Popen(['sudo', 'dd', 'if=/dev/' + disk, 'of=~/disk_benchmark_file', 'bs=8k', 'count=200k'], stderr=subprocess.PIPE).communicate()
print err

it doesn’t work. The Error I get is:

dd: failed to open ‘~/disk_benchmark_file’: No such file or directory

If I change in the Popen() call the tilde ~ to /home/user, then it works!

Why is it like that? And more important to me: How can I make it work?
I don’t know what the user name will be in production.

Asked By: RichArt

||

Answers:

~ is a shortcut in the shell for home. In order for your command to be interpreted by the shell you need to set shell=True in your Popen.

The shell argument (which defaults to False) specifies whether to use the shell as the program to execute. If shell is True, it is recommended to pass args as a string rather than as a sequence

https://docs.python.org/2/library/subprocess.html

Note, there are some warnings about doing this though.

Answered By: bravosierra99
import os
import shlex

outfile = os.path.expanduser('~/file')
cmd_string = 'sudo dd if=/dev/sda of=%s bs=8k count=200k; rm -f %s' % (outfile, outfile)
cmd_list = shlex.split(cmd_string)

# Then use cmd_list as argument for Popen

shlex.split is the standard and safest way to produce the list that must be used as command in subprocess. It is able to handle all the exception and make your code easier to read

You can find the home using os.path.expanduser('~').

Answered By: Riccardo Petraglia

You need to wrap those pathnames with os.path.expanduser():

>>> import os
>>> os.path.expanduser('~/disk_benchmark_file')
'/home/dan/disk_benchmark_file'

In your code the occurrence of:

['sudo', 'dd', 'if=/dev/' + disk, 'of=~/disk_benchmark_file', 'bs=8k', 'count=200k']

should be replaced with:

['sudo', 'dd', 'if=/dev/' + disk, 'of=' + os.path.expanduser('~/disk_benchmark_file'), 'bs=8k', 'count=200k']
Answered By: Dan D.

You need to expand the leading ~ in the path to the user’s home directory before passing it to Popen. You can use pathlib for this:

from pathlib import Path

Path('~/disk_benchmark_file').expanduser()

Alternatively, you can use Path.home() to get the home directory:

Path(Path.home(), 'disk_benchmark_file')
Answered By: Eugene Yarmash
Categories: questions Tags: , , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.