28

I would like to execute multiple commands in a row:

i.e. (just to illustrate my need):

cmd (the shell)

then

cd dir

and

ls

and read the result of the ls.

Any idea with subprocess module?

Update:

cd dir and ls are just an example. I need to run complex commands (following a particular order, without any pipelining). In fact, I would like one subprocess shell and the ability to launch many commands on it.

martineau
  • 119,623
  • 25
  • 170
  • 301
MechanTOurS
  • 1,485
  • 2
  • 15
  • 18

5 Answers5

32

To do that, you would have to:

  • supply the shell=True argument in the subprocess.Popen call, and
  • separate the commands with:
    • ; if running under a *nix shell (bash, ash, sh, ksh, csh, tcsh, zsh etc)
    • & if running under the cmd.exe of Windows
tzot
  • 92,761
  • 29
  • 141
  • 204
  • 2
    Or for windows, can use && so that a command that errors out will prevent execution of the commands after it. – twasbrillig Jan 26 '14 at 20:45
  • @twasbrillig Interesting; I didn't know that cmd.exe shared this command separator with Unix shells. I should update my knowledge. Thanks! – tzot Jan 27 '14 at 07:47
  • Thank you this is invaluable and was really hard to find. – CodeMonkey Jul 11 '17 at 12:41
24

There is an easy way to execute a sequence of commands.

Use the following in subprocess.Popen

"command1; command2; command3"

Or, if you're stuck with windows, you have several choices.

  • Create a temporary ".BAT" file, and provide this to subprocess.Popen

  • Create a sequence of commands with "\n" separators in a single long string.

Use """s, like this.

"""
command1
command2
command3
"""

Or, if you must do things piecemeal, you have to do something like this.

class Command( object ):
    def __init__( self, text ):
        self.text = text
    def execute( self ):
        self.proc= subprocess.Popen( ... self.text ... )
        self.proc.wait()

class CommandSequence( Command ):
    def __init__( self, *steps ):
        self.steps = steps
    def execute( self ):
        for s in self.steps:
            s.execute()

That will allow you to build a sequence of commands.

S.Lott
  • 384,516
  • 81
  • 508
  • 779
  • 2
    `subprocess.Popen("ls")` works. However, `subprocess.Popen("ls; ls")` fails for me. Error: `Traceback (most recent call last): File "", line 1, in File "/usr/lib64/python2.6/subprocess.py", line 639, in __init__ errread, errwrite) File "/usr/lib64/python2.6/subprocess.py", line 1228, in _execute_child raise child_exception OSError: [Errno 2] No such file or directory` – Xander Dunn Sep 18 '12 at 15:51
  • This failed because Popen expects a list as its first argument and just "ls" is equivalent to ["ls"]. It tries to find a executable with name "ls; ls" which is obviously not there. – Jatin Kumar Dec 09 '14 at 08:19
  • Wouldn't the execute from CommandSequence class over ride the execute() from Command class? If so, then how does s.execute work? – user97662 Mar 26 '15 at 00:48
  • lack of shell=True caused the same problem for me def do_shell(self, command): self.proc=subprocess.Popen(command,shell=True) self.proc.wait() – Goblinhack May 13 '15 at 21:54
  • 1
    Code not even tested – MaxBlax360 May 16 '17 at 08:02
  • 2
    Using `s = """my script with line break"""` and running it with `subprocess.run([s], shell=True)` worked well for me. – Izaya Nov 12 '20 at 08:59
  • @Izaya many thanks for your comment! Using Python 3.8.10 I have been browsing various answers and only this one worked. – kmalarski Dec 17 '21 at 17:17
5

Finding 'bar' in every file whose name contains 'foo':

from subprocess import Popen, PIPE
find_process = Popen(['find', '-iname', '*foo*'], stdout=PIPE)
grep_process = Popen(['xargs', 'grep', 'bar'], stdin=find_process.stdout, stdout=PIPE)
out, err = grep_process.communicate()

'out' and 'err' are string objects containing the standard output and, eventually, the error output.

Oli
  • 15,345
  • 8
  • 30
  • 36
2

Yes, the subprocess.Popen() function supports a cwd keyword argument, with which you can set the directory it runs the process in.

I guess the first step, the shell, is not needed, if all you want is to run ls, there's no need to run it through a shell.

Of course, you could also just pass the desired directory as an argument to ls.

Update: it might be worth noting that for typical shells, cd is implemented in the shell itself, it is not an external command on disk. This is because it needs to change the process' current directory, which must be done from within the process. Since commands run as child processed, spawned by the shell, they cannot do this.

unwind
  • 391,730
  • 64
  • 469
  • 606
-4

below python script have 3 function what you went just excute:

import sys
import subprocess

def cd(self,line):
    proc1 = subprocess.Popen(['cd'],stdin=subprocess.PIPE)
    proc1.communicate()

def ls(self,line):
    proc2 = subprocess.Popen(['ls','-l'],stdin=subprocess.PIPE)
    proc2.communicate()

def dir(silf,line):
    proc3 = subprocess.Popen(['cd',args],stdin=subprocess.PIPE)
    proc3.communicate(sys.argv[1])
  • it is incorrect. `cd` has no effect on the parent and it is likely that OP wants builtin into the shell command (`Popen()` doesn't run the shell unless you ask explicitly -- though it won't help in this case). – jfs Oct 04 '15 at 08:58