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.
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.
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('~')
.
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']
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')
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.
~
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.
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('~')
.
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']
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')