0

How could I run this code using subprocess module?

commands.getoutput('sudo blkid | grep 'uuid' | cut -d " " -f 1 | tr -d ":"')

I've tried this but it doesn't work at all

out_1 = subprocess.Popen(('sudo', 'blkid'), stdout=subprocess.PIPE)
out_2 = subprocess.Popen(('grep', 'uuid'), stdin=out_1.stdout, stdout=subprocess.PIPE)
out_3 = subprocess.Popen(('cut', '-d', '" "', '-f', '1'), stdin=out_2.stdout, stdout=subprocess.PIPE)
main_command = subprocess.check_output(('tr', '-d', '":"'), stdin=out_3.stdout)

main_command

Error: cut: the delimiter must be a single character

  • What does it do - do you have error messages to post – PyNEwbie Mar 01 '14 at 15:08
  • Error: cut: the delimiter must be a single character –  Mar 01 '14 at 15:17
  • Do you know that `grep 'uuid' | cut -d " " -f 1 | tr -d ":"` could be replaced with a single command: `awk '/uuid/{print gsub(":", "", $1)}'` – devnull Mar 01 '14 at 15:22
  • I've tried it but the output it's completely different: while grep shows /dev/sda1, awk just does 1 –  Mar 01 '14 at 15:32

2 Answers2

3
from subprocess import check_output, STDOUT

shell_command = '''sudo blkid | grep 'uuid' | cut -d " " -f 1 | tr -d ":"'''
output = check_output(shell_command, shell=True, stderr=STDOUT,
                      universal_newlines=True).rstrip('\n')

btw, it returns nothing on my system unless grep -i is used. In the latter case it returns devices. If it is your intent then you could use different command:

from subprocess import check_output

devices = check_output(['sudo', 'blkid', '-odevice']).split()

I'm trying not to use shell=True

It is ok to use shell=True if you control the command i.e., if you don't use user input to construct the command. Consider the shell command as a special language that allows you to express your intent concisely (like regex for string processing). It is more readable then several lines of code that do not use shell:

from subprocess import Popen, PIPE

blkid = Popen(['sudo', 'blkid'], stdout=PIPE)
grep = Popen(['grep', 'uuid'], stdin=blkid.stdout, stdout=PIPE)
blkid.stdout.close() # allow blkid to receive SIGPIPE if grep exits
cut = Popen(['cut', '-d', ' ', '-f', '1'], stdin=grep.stdout, stdout=PIPE)
grep.stdout.close()
tr = Popen(['tr', '-d', ':'], stdin=cut.stdout, stdout=PIPE,
           universal_newlines=True)
cut.stdout.close()
output = tr.communicate()[0].rstrip('\n')
pipestatus = [cmd.wait() for cmd in [blkid, grep, cut, tr]]

Note: there are no quotes inside quotes here (no '" "', '":"'). Also unlike the previous command and commands.getoutput(), it doesn't capture stderr.

plumbum provides some syntax sugar:

from plumbum.cmd import sudo, grep, cut, tr

pipeline = sudo['blkid'] | grep['uuid'] | cut['-d', ' ', '-f', '1'] | tr['-d', ':']
output = pipeline().rstrip('\n') # execute

See How do I use subprocess.Popen to connect multiple processes by pipes?

Community
  • 1
  • 1
jfs
  • 399,953
  • 195
  • 994
  • 1,670
0

pass your command as one string like this:

main_command = subprocess.check_output('tr -d ":"', stdin=out_3.stdout)

if you have multiple commands and if you want to execute one by one, pass them as list:

main_command = subprocess.check_output([comand1, command2, etc..], shell=True)
venpa
  • 4,268
  • 21
  • 23