How can I execute an os/shell command from python [duplicate] - python

This question already has answers here:
How do I execute a program or call a system command?
(65 answers)
Closed 6 years ago.
For say in terminal I did cd Desktop you should know it moves you to that directory, but how do I do that in python but with use Desktop with raw_input("") to pick my command?

The following code reads your command using raw_input, and execute it using os.system()
import os
if __name__ == '__main__':
while True:
exec_cmd = raw_input("enter your command:")
os.system(exec_cmd)
Best Regards,
Yaron

To go with your specific example, you'd do the following:
import os
if __name__ == "__main__":
directory = raw_input("Please enter absolute path: ")
old_dir = os.getcwd() #in case you need your old directory
os.chdir(directory)
I've used this technique before in some directory maintenance functions I've written and it works. If you want to run shell commands more generally you'd something like:
import subprocess
if __name__ == "__main__":
command_list = raw_input("").split(" ")
ret = subprocess(command_list)
#from here you can check ret if you need to
But beware with this method. The system here has no knowledge about whether it's passing a valid command, so it's likely to fail and miss exceptions. A better version might look like:
import subprocess
if __name__ == "__main__":
command_kb = {
"cd": True,
"ls": True
#etc etc
}
command_list = raw_input("").split(" ")
command = command_list[0]
if command in command_kb:
#do some stuff here to the input depending on the
#function being called
pass
else:
print "Command not supported"
return -1
ret = subprocess(command_list)
#from here you can check ret if you need to
This method represents a list of supported commands. You can then manipulate the list of args as needed to verify it's a valid command. For instance, you can check if the directory you're about to cd exists and return an error to the user if not. Or you can check if the path name is valid, but only when joined by an absolute path.

maybe you can do this:
>>> import subprocess
>>> input = raw_input("")
>>> suprocess.call(input.split()) # for detail usage, search subprocess
for details, you can search subprocess module

Related

Using subprocess.run python module with different languages

i'm building tester for programs in different languages, but I'm not able to get C program working, currently the command is called like this:
codeResult = subprocess.run(self.createRunCommand(currLanguage, file),
input = codeToTest,
shell = True,
timeout = TIMEOUT,
capture_output=True)
and createRunCommand() returns:
def createRunCommand(self, language, file):
if language == '.py':
command = f'python {file}'
elif language == '.c':
if not os.path.exists(f'C:/<myPath>/{file}.out'):
command = f'gcc -std=c11 {file} -o C:/<myPath>/{file}.out \
./C:/<myPath>/{file}.out'
else:
command = f'./C:/<myPath>/{file}.out'
elif language == '.java':
command = f''
elif language == '.cpp':
command = f''
return command
the input and test itself is good, as it runs correctly with a python program, but I cannot figure out how to setup C (and probably other compiled first languages).
You'll need multiple command invocations for (e.g.) C/C++, so have your createRunCommand return multiple.
I also changed things up here to
automatically figure out the language from the extension of the filename
use a list of arguments instead of a string; it's safer
use sys.executable for the current Python interpreter, and shutil.which("gcc") to find gcc.
import os
import shlex
import shutil
import subprocess
import sys
def get_commands(file):
"""
Get commands to (compile and) execute `file`, as a list of subprocess arguments.
"""
ext = os.path.splitext(file)[1].lower()
if ext == ".py":
return [(sys.executable, file)]
if ext in (".c", ".cpp"):
exe_file = f"{file}.exe"
return [
(shutil.which("gcc"), "-std=c11", file, "-o", exe_file),
(exe_file,),
]
raise ValueError(f"Unsupported file type: {ext}")
filename = "foo.py"
for command in get_commands(filename):
print(f"Running: {shlex.join(command)}")
code_result = subprocess.run(command, capture_output=True)

How to terminate one python script when many python scripts are running?

Hello guys I have opened 3 python scripts that they are running at the same time. I want to terminate(Kill) one of them with other python file. It means if we run many python scripts at the same time how to terminate or kill one of them or two of them? Is it possible with os or subprocess modules? I try to use them but they kill all python scripts with killing python.exe
FirstSc.py
UserName = input("Enter your username = ")
if UserName == "Alex":
#Terminate or Kill the PythonFile in this address C:\MyScripts\FileTests\SecondSc.py
SecondSc.py
while True:
print("Second app is running ...")
ThirdSc.py
while True:
print("Third app is running ...")
Thanks guys I get good answers. Now if we have a Batch file like SecBatch.bat instead of SecondSc.py how to do this. It means we have these and run FirstSc.py and SecBatch.bat at the same time:
FirstSc.py in this directory D:\MyFiles\FirstSc.py
UserName = input("Enter your username = ")
if UserName == "Alex":
#1)How to print SecBatch.bat syntax it means print:
#CALL C:\MyProject\Scripts\activate.bat
#python C:\pyFiles\ThirdSc.py
#2)Terminate or kill SecBatch.bat
#3)Terminate or kill ThirdSc.py
SecBatch.bat in this directory C:\MyWinFiles\SecBatch.bat that it run a Python VirtualEnvironment then run a python script in this directory C:\pyFiles\ThirdSc.py
CALL C:\MyProject\Scripts\activate.bat
python C:\pyFiles\ThirdSc.py
ThirdSc.py in this directory C:\pyFiles\ThirdSc.py
from time import sleep
while True:
print("Third app is running ...")
sleep(2)
I would store the PID of each script in a standard location. Assuming you are running on Linux I would put them in /var/run/. Then you can use os.kill(pid, 9) to do what you want. Some example helper funcs would be:
import os
import sys
def store_pid():
pid = os.getpid()
# Get the name of the script
# Example: /home/me/test.py => test
script_name = os.path.basename(sys.argv[0]).replace(".py", "")
# write to /var/run/test.pid
with open(f"/var/run/{script_name}.pid", "w"):
f.write(pid)
def kill_by_script_name(name):
# Check the pid file is there
pid_file = f"/var/log/{name}.pid"
if not os.path.exists(pid_file):
print("Warning: cannot find PID file")
return
with open(pid_file) as f:
# The following might throw ValueError if pid file has characters
pid = int(f.read().strip())
os.kill(pid, 9)
Later in FirstSc:
if UserName == "Alex":
kill_by_script_name("SecondSc")
kill_by_script_name("ThirdSc")
NOTE: The code is not tested :) but should point to you to the correct direction (at least for one common way to solve this problem)
You may be able to terminate a Python process by the name of the script file using system commands such as taskkill (or pkill on Linux systems). However, a better way to accomplish this would be (if possible) to have FirstSc.py or whatever script that's doing the killing launch the other scripts using subprocess.Popen(). Then you can call terminate() on it to end the process:
import subprocess
# Launch the two scripts
# You may have to change the Python executable name
second_script = subprocess.Popen(["python", "SecondSc.py"])
third_script = subprocess.Popen(["python", "ThirdSc.py"])
UserName = input("Enter your username = ")
if UserName == "Alex":
second_script.terminate()

Python: Read data from STDIN, unless its not provided

I have a python program which reads from STDIN:
#!/usr/bin/env python
def my_func(data):
print (data)
if __name__ == '__main__':
import sys
data = sys.stdin.read()
my_func(data)
I see the expected results when I execute this with:
cat file.txt | ./app.py
I want to add some other functionality to the program:
if __name__ == '__main__':
import sys
data = sys.stdin.read()
if data:
my_func(data)
else:
print ('I am some other functionality')
However when I execute this with:
./app.py
... the program just hangs, as if it is waiting for STDIN input.
What's the correct way to write this, so it will handle both methods of executing.
As #Klaus D. suggested, using different command line arguments should be the most straightforward option.
sys.argv[i] returns the argument at index i used when launching the script. Index 0 is always the script name while any other index represents subsequent arguments passed to the script. With that in mind:
if __name__ == '__main__':
import sys
objective = sys.argv[1]
if objective == 'read':
data = sys.stdin.read()
if data:
my_func(data)
else:
print ('I am some other functionality')
Many Unix-style programs look for a dash - to indicate input from stdin. So,
import sys
if sys.argv[1] == '-':
read_from_stdin()
else:
other_action()
$ ./app.py - < /path/to/input

Python's sh module - is it at all possible for a script to request input?

Using Python's sh, I am running 3rd party shell script that requests my input (not that it matters much, but to be precise, I'm running an Ansible2 playbook with the --step option)
As an oversimplification of what is happening, I built a simple bash script that requests an input. I believe that if make this simple example work I can make the original case work too.
So please consider this bash script hello.sh:
#!/bin/bash
echo "Please input your name and press Enter:"
read name
echo "Hello $name"
I can run it from python using sh module, but it fails to receive my input...
import errno
import sh
cmd = sh.Command('./hello.sh')
for line in cmd(_iter=True, _iter_noblock=True):
if line == errno.EWOULDBLOCK:
pass
else:
print(line)
How could I make this work?
After following this tutorial, this works for my use case:
#!/usr/bin/env python3
import errno
import sh
import sys
def sh_interact(char, stdin):
global aggregated
sys.stdout.write(char)
sys.stdout.flush()
aggregated += char
if aggregated.endswith(":"):
val = input()
stdin.put(val + "\n")
cmd = sh.Command('./hello.sh')
aggregated = ""
cmd(_out=sh_interact, _out_bufsize=0)
For example, the output is:
$ ./testinput.py
Please input your name and press Enter:arod
Hello arod
There are two ways to solve this:
Using _in:
using _in, we can pass a list which can be taken as input in the python script
cmd = sh.Command('./read.sh')
stdin = ['hello']
for line in cmd(_iter=True, _iter_noblock=True, _in=stdin):
if line == errno.EWOULDBLOCK:
pass
else:
print(line)
Using command line args if you are willing to modify the script.

How do you get the process ID of a program in Unix or Linux using Python?

I'm writing some monitoring scripts in Python and I'm trying to find the cleanest way to get the process ID of any random running program given the name of that program
something like
ps -ef | grep MyProgram
I could parse the output of that however I thought there might be a better way in python
From the standard library:
os.getpid()
If you are not limiting yourself to the standard library, I like psutil for this.
For instance to find all "python" processes:
>>> import psutil
>>> [p.info for p in psutil.process_iter(attrs=['pid', 'name']) if 'python' in p.info['name']]
[{'name': 'python3', 'pid': 21947},
{'name': 'python', 'pid': 23835}]
Try pgrep. Its output format is much simpler and therefore easier to parse.
Also:
Python: How to get PID by process name?
Adaptation to previous posted answers.
def getpid(process_name):
import os
return [item.split()[1] for item in os.popen('tasklist').read().splitlines()[4:] if process_name in item.split()]
getpid('cmd.exe')
['6560', '3244', '9024', '4828']
With psutil:
(can be installed with [sudo] pip install psutil)
import psutil
# Get current process pid
current_process_pid = psutil.Process().pid
print(current_process_pid) # e.g 12971
# Get pids by program name
program_name = 'chrome'
process_pids = [process.pid for process in psutil.process_iter() if process.name == program_name]
print(process_pids) # e.g [1059, 2343, ..., ..., 9645]
For Windows
A Way to get all the pids of programs on your computer without downloading any modules:
import os
pids = []
a = os.popen("tasklist").readlines()
for x in a:
try:
pids.append(int(x[29:34]))
except:
pass
for each in pids:
print(each)
If you just wanted one program or all programs with the same name and you wanted to kill the process or something:
import os, sys, win32api
tasklistrl = os.popen("tasklist").readlines()
tasklistr = os.popen("tasklist").read()
print(tasklistr)
def kill(process):
process_exists_forsure = False
gotpid = False
for examine in tasklistrl:
if process == examine[0:len(process)]:
process_exists_forsure = True
if process_exists_forsure:
print("That process exists.")
else:
print("That process does not exist.")
raw_input()
sys.exit()
for getpid in tasklistrl:
if process == getpid[0:len(process)]:
pid = int(getpid[29:34])
gotpid = True
try:
handle = win32api.OpenProcess(1, False, pid)
win32api.TerminateProcess(handle, 0)
win32api.CloseHandle(handle)
print("Successfully killed process %s on pid %d." % (getpid[0:len(prompt)], pid))
except win32api.error as err:
print(err)
raw_input()
sys.exit()
if not gotpid:
print("Could not get process pid.")
raw_input()
sys.exit()
raw_input()
sys.exit()
prompt = raw_input("Which process would you like to kill? ")
kill(prompt)
That was just a paste of my process kill program I could make it a whole lot better but it is okay.
For posix (Linux, BSD, etc... only need /proc directory to be mounted) it's easier to work with os files in /proc
Works on python 2 and 3 ( The only difference is the Exception tree, therefore the "except Exception", which i dislike but kept to maintain compatibility. Also could've created custom exception.)
#!/usr/bin/env python
import os
import sys
for dirname in os.listdir('/proc'):
if dirname == 'curproc':
continue
try:
with open('/proc/{}/cmdline'.format(dirname), mode='rb') as fd:
content = fd.read().decode().split('\x00')
except Exception:
continue
for i in sys.argv[1:]:
if i in content[0]:
# dirname is also the number of PID
print('{0:<12} : {1}'.format(dirname, ' '.join(content)))
Sample Output (it works like pgrep):
phoemur ~/python $ ./pgrep.py bash
1487 : -bash
1779 : /bin/bash
This is a simplified variation of Fernando's answer. This is for Linux and either Python 2 or 3. No external library is needed, and no external process is run.
import glob
def get_command_pid(command):
for path in glob.glob('/proc/*/comm'):
if open(path).read().rstrip() == command:
return path.split('/')[2]
Only the first matching process found will be returned, which works well for some purposes. To get the PIDs of multiple matching processes, you could just replace the return with yield, and then get a list with pids = list(get_command_pid(command)).
Alternatively, as a single expression:
For one process:
next(path.split('/')[2] for path in glob.glob('/proc/*/comm') if open(path).read().rstrip() == command)
For multiple processes:
[path.split('/')[2] for path in glob.glob('/proc/*/comm') if open(path).read().rstrip() == command]
The task can be solved using the following piece of code, [0:28] being interval where the name is being held, while [29:34] contains the actual pid.
import os
program_pid = 0
program_name = "notepad.exe"
task_manager_lines = os.popen("tasklist").readlines()
for line in task_manager_lines:
try:
if str(line[0:28]) == program_name + (28 - len(program_name) * ' ': #so it includes the whitespaces
program_pid = int(line[29:34])
break
except:
pass
print(program_pid)

Categories