2

I am looking to create a diskpart script for Windows using Python.

I need to run diskpart and then issues additional commands after the program is executed the series of inputs are below. I will eventually put this in a loop so it can be done for a range of disks.

  • SELECT DISK 1
  • ATTRIBUTES DISK CLEAR READONLY
  • ONLINE DISK noerr
  • CLEAN
  • CREATE PART PRI
  • SELECT PART 1
  • ASSIGN
  • FORMAT FS=NTFS QUICK LABEL="New Volume"

I have tried to do this as follows below.

In the example below I am able to execute diskpart then run the first command which is "select disk 1" and then it terminates. I want to be able to send the additional commands to complete the process of preparing the disk how can this be done? diskpart does not take arguments that can facilitate this besides reading from a file but I want to avoid that on Windows 2012 PowerShell cmdelts make this easier to achieve.

import subprocess

from subprocess import Popen, PIPE, STDOUT
p = Popen(['diskpart'], stdout=PIPE, stdin=PIPE, stderr=STDOUT)
grep_stdout = p.communicate(input=b'select disk 1')[0]
print(grep_stdout.decode())

Looking for something along the lines of

from subprocess import Popen, PIPE, STDOUT
p = Popen(['diskpart'], stdout=PIPE, stdin=PIPE, stderr=STDOUT)
grep_stdout = p.communicate(input=b'select disk 1')[0]

- run command
- run command
- run command
- run command 
- run command
- run command
- run command

print(grep_stdout.decode())

I tried the following below and actually executes diskpart and then also runs the command "select disk 1" and exits after that I belive this is not the correct way of sending the input but is more along the lines of what I am trying to achieve if I can continue to send subsequent commands.

import subprocess
from subprocess import Popen, PIPE, STDOUT
p = Popen(['diskpart'], stdout=PIPE, stdin=PIPE, stderr=STDOUT)
grep_stdout = p.communicate(input=b'select disk 1')[0]
Hector De Jesus
  • 101
  • 1
  • 8
  • you could also use `SendKeys` module if you don't need to read the output back, here's [code example](http://stackoverflow.com/a/12606327/4279) -- the advantage is that you can send F10 and other special keys. – jfs Mar 11 '15 at 21:14

3 Answers3

1

I think you are having a problem with communicate here - it sends the data to the process and then waits for it to complete. (See Communicate multiple times with a process without breaking the pipe?)

I'm not sure that this will help exactly, but I wrote up a batch script based on the linked answer.

test.bat:

@echo off

set /p animal= "What is your favourite animal? " 
echo %animal%

set /p otheranimal= "What is another awesome animal? "
echo %otheranimal%

set "animal="
set "otheranimal="

test.py:

import time
from subprocess import Popen, PIPE

p = Popen(["test.bat"], stdin=PIPE)

print("sending data to STDIN")
res1 = p.stdin.write("cow\n")
time.sleep(.5)
res2 = p.stdin.write("velociraptor\n")

This works by sending the data to stdin, but not waiting for the process to complete.

I'm not a windows expert, so I apologise if the input handling in diskpart works differently to standard input to a batch file.

Community
  • 1
  • 1
Tim B
  • 121
  • 1
  • 7
  • Thanks for helping to answer my question Tim. I tried based on your example but the script exits after running the command diskpart. diskpart.exe can only take two aruments /s and /? which I cant use for what I am trying to do. Once diskpart is executed then I can run commands like "select disk 1" this is where I have the problem after the program is executed then I have a host of different commands that I am able to execute they cannot be run as diskpart.exe – Hector De Jesus Mar 11 '15 at 00:20
  • I understand - I referred to 'arguments' which was incorrect and confusing (edited). The idea behind the script was that the successive calls to `p.stdin.write` send each input to the running process using standard input, just like typing them in at the prompt once `diskpart` is running. – Tim B Mar 11 '15 at 00:46
  • I figured out why I got the error I am using python 3.4.3 so I have to write p.stdin.write as follows "res1 = p.stdin.write(bytes("select disk 1\n", 'utf-8'))" Now I am able to run diskpart and then command select disk 1 but subsequents commands have no effect and there is no error in the script. – Hector De Jesus Mar 11 '15 at 02:02
  • Good point, I was using python 2.7 so didn't need to do the string encoding things. Perhaps try increasing the value of `time.sleep` - the example is only set to wait .5 seconds before sending in the next input, it might need a bit longer to ensure that the `select disk 1` is finished before it enters the next one. – Tim B Mar 11 '15 at 02:34
  • Tim Looks to have been an error on my part thanks so much for your help the script now does what I was look for I have updated my original question with the solution thank you so much! – Hector De Jesus Mar 11 '15 at 02:59
1

With Tims help I was able to do the following to get my script to work.

import time
from subprocess import Popen, PIPE

p = Popen(["diskpart"], stdin=PIPE)
print("sending data to STDIN")
res1 = p.stdin.write(bytes("select disk 2\n", 'utf-8'))
time.sleep(.5)
res2 = p.stdin.write(bytes("ATTRIBUTES DISK CLEAR READONLY\n", 'utf-8'))
time.sleep(.5)
res3 = p.stdin.write(bytes("online disk noerr\n", 'utf-8'))
time.sleep(.5)
res4 = p.stdin.write(bytes("clean\n", 'utf-8'))
time.sleep(.5)
res5 = p.stdin.write(bytes("create part pri\n", 'utf-8'))
time.sleep(.5)
res6 = p.stdin.write(bytes("select part 1\n", 'utf-8'))
time.sleep(.5)
res7 = p.stdin.write(bytes("assign\n", 'utf-8'))
time.sleep(.5)
res8 = p.stdin.write(bytes("FORMAT FS=NTFS QUICK \n", 'utf-8'))
time.sleep(.5)
Hector De Jesus
  • 101
  • 1
  • 8
  • 1
    Replace all instances of `bytes(..., 'utf-8')` with simply `b''`, e.g. `b'select disk 2\r\n'`. Or use `universal_newlines=True` to get text mode. – Eryk Sun Mar 11 '15 at 03:07
  • you might also need `p.stdin.flush()` after each `.write()` call. Don't copy-paste it 8 times, create a small function that accepts a pipe (such as `p.stdin`) and writes a given bytestring with a given delay) instead. – jfs Mar 11 '15 at 21:15
  • You could pass `univeral_newlines=True` as @eryksun suggested. And pass the input as text: `print("select disk 2", file=p.stdin, flush=True)` (it looks like you are using Python 3). Note: the newline is added by `print` function here. – jfs Mar 11 '15 at 21:18
  • Thanks to all for the input. – Hector De Jesus Mar 11 '15 at 21:50
1

You could also make a text file with the diskpart commands separated by newline characters and then run diskpart /s 'filename'. Like this:

from subprocess import Popen, PIPE
import os

with open("temp.txt", "wt") as file:
    file.write("command\ncommand\ncommand")
p = Popen(["diskpart","/s","temp.txt"], stdin=PIPE)
os.remove("temp.txt")

This solution would prevent the possible problem of writing to the terminal before the program is ready. Also, it may fix a potential problem if diskpart asks for further clarification and gets the next command instead.

Source: https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/diskpart-scripts-and-examples

  • 1
    This should be the best answer imo. Disk part has this particular function designed specifically for your use case. The other methods of interacting with the subprocess is too risky. – Billy Cao Sep 09 '21 at 16:42
  • I mean, do you really want to take any chances when messing with disk formatting? – SpaceSaver2000 May 01 '22 at 22:14