How to nest script that inputs variable for automation purposes? - python

I'm creating a python script (cmd_exec.py) to open another python script (entername.py). How do I nest it so the script inputs string and execute the enter button all automatically? Trying to remember the question about ASCII input code for enter, but can't find it. So that when I run the cmd_exec.py in powershell, it would display "Hello foo":
cmd_exec.py:
import subprocess, sys
p = subprocess.Popen(["powershell.exe", "py C:\\Users\\uname\\PProjects\\cmdauto\\entername.py"], stdout=sys.stdout)
p.communicate()
I want maname variable to get inserted in the entername.py script and the script to execute/press enter button. So that when I run the cmd_exec.py script, I would see it doing all by itself and prints out "Hello foo"
entername.py:
maname = "foo"
person = input('Enter your name: ')
print('Hello', person)

Not sure if this helps or if this will work for you but in Bash you can specify input with the '<' operator.
For instance if you have a python file program.py and your inputs are in input.txt you can run
$ python3 program.py < input.txt
You may want to take this approach unless have other constraints

You may be looking for sys.argv and subprocess.getstatusoutput()
import subprocess
maname = "foo"
person = input('Enter your name: ')
print('Hello', person)
# not sure why powershell is here, normally would be only `python C:\\Users\\uname\\PProjects\\cmdauto\\entername.py {person}`
x = subprocess.getstatusoutput(f"powershell.exe 'py C:\\Users\\uname\\PProjects\\cmdauto\\entername.py {person}'")
print(x)
# (0, 'python f:\\entername.py')
# entername.py
import sys
person = int(sys.argv[1]) # get the 2nd item on `argv` list, the 1st is the script name

Related

Python: get last command line in linux terminal

I would like to write a python script which access the last command executed in terminal, i.e the command that launched the program.
For example, I want the terminal to output 'python myfile.py' if i typed python myfile.py
First I tried:
import os
os.system("touch command.txt")
os.system("history > command.txt")
with open("command.txt", "r") as f:
command = f.read()[-1]
print(command)
but this is not working since history is a bash built-in function.
Then I tried :
import os, subprocess
command = subprocess.check_output(["tail","-n","1",os.path.expanduser("~/.bash_history")]).decode("utf-8").rstrip()
print(command)
but this does not meet my expectations, because bash history is only updated when the terminal is closed.
To improve this behavior I tried os.putenv("PROMPT_COMMAND", "history-a"), but it didn't help neither, because bash history update is still one step behind, as my variable command would now contain the command line just before python myfile.py
Now I'm stuck and I need your help pls
You can't get the original shell command line in a reliable way without participation of the shell itself, but you can generate an equivalent command line using sys.argv. (It won't include things like redirections, but if you're just re-executing from inside the existing copy of the program, all those executions will have been already performed before you're started anyhow, so when you re-exec yourself the new copy will inherit their effect).
So:
#!/usr/bin/env python
import os.path, sys
try:
from shlex import quote # Python 3
except ImportError:
from pipes import quote # Python 2
sys_argv_str = ' '.join(quote(x) for x in sys.argv)
print("We can be restarted by calling the argv: %r" % (sys.argv,))
print("As a shell-syntax string, that would be: %s" % (sys_argv_str,))
print("...or, if your shell is bash, you can specify the interpreter directly:")
print(' ' + ' '.join(quote(x) for x in (['exec', '-a', sys.argv[0], os.path.abspath(sys.executable), os.path.abspath(__file__)] + sys.argv[1:])))
If someone calls ./yourprogram "first argument" "second argument", that output might look like:
We can be restarted by calling the argv: ['./yourprogram', 'first argument', 'second argument']
As a shell-syntax string, that would be: ./yourprogram 'first argument' 'second argument'
...or, if your shell is bash, you can specify the interpreter directly:
exec -a ./yourprogram /usr/bin/python /home/charles/tmp/yourprogram 'first argument' 'second argument'
Note that argv[0] is not guaranteed to be identical to __file__! When a program is starting another program it can pass any string it likes in the argv[0] slot; it's merely convention, not a firm guarantee, that that will contain the name that was used to start the software at hand.

How to get bash script take input from python script

I am new to programming in python and wanted to try something out.
I have a bash script, which takes in parameters like User name, Full name etc. This is the bash script I have
#!/bin/sh
echo -n "username: "
read username
echo -n "First name: "
read first
echo -n "Last name: "
read last
echo "$username", "$first", "$last"
I am trying to call the bash script via python. Except I want to enter the parameters in python and it needs to be parsed to the bash script. Any help will be greatly appreciated.
Python code to call the bash script
import os
import sys
os.system("pwd")
os.system("./newuser1.list" )
You are not specific to your python version, but here is something that works with 2.7.
As I understand, you want to take input in the python script and pass it to the bash script. You can use raw_input("Prompt") in python 2, and just input("Prompt") for python 3. When passing the params to the shell script, simply append them to the string passed to the shell.
As such:
import os
user_name = raw_input("Enter username: ")
first_name = raw_input("Enter firstname: ")
last_name = raw_input("Enter lastname: ")
os.system("./test.sh {0} {1} {2}".format(user_name, first_name, last_name))
For the shell script, grab the params with the $1, $2... environment style variables. Put them in variables like below, or just use them directly.
The shell script (For the example i named it test.sh):
#!/bin/sh
userName=$1
firstName=$2
lastName=$3
echo "$userName, $firstName, $lastName"
Is this what you were looking for?

I can't run python file interactivly with terminal

I'm using OSX Mac terminal to run python 2.7.10.
for example:
I have a file called "myfile.py"
so when I want to run it on the terminal it would be like this:
python Desktop/myfile.py
However inside the file I have wrote some functions like
def myFunction(x,y):
return float(x)/y
with this method of running a python script I can not interact with my program and use myFunction to input x and y with different values every time and test myFunction properly.
Thank You,
Try passing -i before the script name
python -i myfile.py
You can learn more about what options are available by running man python.
To quote from the manual:
-i When a script is passed as first argument or the -c option
is used, enter interactive mode after executing the script
or the command. It does not read the $PYTHONSTARTUP file.
This can be useful to inspect global variables or a stack
trace when a script raises an exception.
You can use python -i script.py
This way, script.py is executed and then python enter interactive mode. In interactive mode you can use all functions, classes and variables that was defined in the script.
You can use raw_input() to do that.Your myfile.py code can look like this:
def myFunction(x,y):
return float(x)/y
x = raw_input("Please enter x value: ")
y = raw_input("Please enter y value: ")
print(myFunction(x,y))

Applescript - Get return from python

I have an applescript which starts an python script.
The python script calculates an integer.
Is it possible to return the result as an integer or string to the applescript?
thx
You should be able to return a value from a python script run from an applescript like this:
set scriptResult to do shell script ("python path/to/script.py")
where scriptResult is then set to any output provided to stdout (e.g., from a print statement) in the python script. So in your python script you could simply:
print yourInteger
and scriptResult would then be set to [integer]. Note that scriptResult will be set to all stdout, so everything you print will be included here. You also may need to do some casting in your applescript to make scriptResult a number:
set scriptResult to (do shell script ("python path/to/script.py")) as integer
In the same genre using do shell script with directly script python
set xy to do shell script "echo 'import sys, time; from Quartz.CoreGraphics import *
def mouseEvent(type, posx, posy): theEvent = CGEventCreateMouseEvent(None, type, (posx,posy), kCGMouseButtonLeft); CGEventPost(kCGHIDEventTap, theEvent);
def mouseclickdn(posx,posy): mouseEvent(kCGEventLeftMouseDown, posx,posy);
def mouseclickup(posx,posy): mouseEvent(kCGEventLeftMouseUp, posx,posy);
ourEvent = CGEventCreate(None); currentpos = CGEventGetLocation(ourEvent); print currentpos[0]; print currentpos[1]; mouseclickdn(currentpos.x, currentpos.y);
mouseclickup(currentpos.x, currentpos.y);' | python "
set xy to do shell script "echo " & xy

How to determine if Python script was run via command line?

Background
I would like my Python script to pause before exiting using something similar to:
raw_input("Press enter to close.")
but only if it is NOT run via command line. Command line programs shouldn't behave this way.
Question
Is there a way to determine if my Python script was invoked from the command line:
$ python myscript.py
verses double-clicking myscript.py to open it with the default interpreter in the OS?
If you're running it without a terminal, as when you click on "Run" in Nautilus, you can just check if it's attached to a tty:
import sys
if sys.stdin and sys.stdin.isatty():
# running interactively
print("running interactively")
else:
with open('output','w') as f:
f.write("running in the background!\n")
But, as ThomasK points out, you seem to be referring to running it in a terminal that closes just after the program finishes. I think there's no way to do what you want without a workaround; the program is running in a regular shell and attached to a terminal. The decision of exiting immediately is done just after it finishes with information it doesn't have readily available (the parameters passed to the executing shell or terminal).
You could go about examining the parent process information and detecting differences between the two kinds of invocations, but it's probably not worth it in most cases. Have you considered adding a command line parameter to your script (think --interactive)?
What I wanted was answered here: Determine if the program is called from a script in Python
You can just determine between "python" and "bash". This was already answered I think, but you can keep it short as well.
#!/usr/bin/python
# -*- coding: utf-8 -*-
import psutil
import os
ppid = os.getppid() # Get parent process id
print(psutil.Process(ppid).name())
I don't think there's any reliable way to detect this (especially in a cross-platform manner). For example on OS X, when you double-click a .py file and it tuns with "Python Launcher", it runs in a terminal, identically to if you execute it manually.
Although it may have other issues, you could package the script up with something like py2exe or Platypus, then you can have the double-clickable icon run a specific bit of code to differentiate (import mycode; mycode.main(gui = True) for example)
If you run python IDLE then "pythonw.exe" is being used to run coding while when you run the command line "python.exe" is used to run coding. The python folder path can vary so you have to revert the path to the python folder. m = '\\' and m = m[0] is to get m to be '\' because of escaping.
import sys
a = sys.executable
m = '\\'
m = m[0]
while True:
b = len(a)
c = a[(b - 1)]
if c == m:
break
a = a[:(b - 1)]
if sys.executable == a + 'pythonw.exe':
print('Running in Python IDLE')
else:
print('Running in Command line')
Update for later versions (e.g. Python 3.6 on Ubuntu 16.04): The statement to get the name has changed to psutil.Process(os.getpid()).parent().name()
I believe this CAN be done. At least, here is how I got it working in Python 2.7 under Ubuntu 14.04:
#!/usr/bin/env python
import os, psutil
# do stuff here
if psutil.Process(os.getpid()).parent.name == 'gnome-terminal':
raw_input("Press enter to close...")
Note that -- in Ubuntu 14 with the Gnome desktop (aka Nautilus) -- you might need to do this:
from a Nautilus window (the file browser), select Edit(menu)->Preferences(item) then Behavior(tab)->Executable Text Files(section)->Ask Each Time(radio).
chmod your script to be executable, or -- from a Nautilus window (the file browser) -- right click on the file->Properties(item) then Permissions(tab)->Execute:Allow executing file as program(checkbox)
double-click your file. If you select "Run in Terminal", you should see the "Type enter to close..." prompt.
now try from a bash prompt; you should NOT see the prompt.
To see how this works, you can fiddle with this (based on the answer by from #EduardoIvanec):
#!/usr/bin/env python
import os
import sys
import psutil
def parent_list(proc=None, indent=0):
if not proc:
proc = psutil.Process(os.getpid())
pid = proc.pid
name = proc.name
pad = " " * indent
s = "{0}{1:5d} {2:s}".format(pad, pid, name)
parent = proc.parent
if parent:
s += "\n" + parent_list(parent, indent+1)
return s
def invoked_from_bash_cmdline():
return psutil.Process(os.getpid()).parent.name == "bash"
def invoked_as_run_in_terminal():
return psutil.Process(os.getpid()).parent.name == "gnome-terminal"
def invoked_as_run():
return psutil.Process(os.getpid()).parent.name == "init"
if sys.stdin.isatty():
print "running interactively"
print parent_list()
if invoked_as_run_in_terminal():
raw_input("Type enter to close...")
else:
with open('output','w') as f:
f.write("running in the background!\n")
f.write("parent list:\n")
f.write(parent_list())
From the idea behind this answer, adding for Win10 compatibility (Ripped from Python 2.7 script; modify as needed):
import os, psutil
status = 1
if __name__ =="__main__":
status = MainFunc(args)
args = sys.argv
running_windowed = False
running_from = psutil.Process(os.getpid()).parent().name()
if running_from == 'explorer.exe':
args.append([DEFAULT OR DOUBLE CLICK ARGS HERE])
running_windowed = True
if running_windowed:
print('Completed. Exit status of {}'.format(status))
ready = raw_input('Press Enter To Close')
sys.exit(status)
There is a number of switch like statements you could add to be more universal or handle different defaults.
This is typically done manually/, I don't think there is an automatic way to do it that works for every case.
You should add a --pause argument to your script that does the prompt for a key at the end.
When the script is invoked from a command line by hand, then the user can add --pause if desired, but by default there won't be any wait.
When the script is launched from an icon, the arguments in the icon should include the --pause, so that there is a wait. Unfortunately you will need to either document the use of this option so that the user knows that it needs to be added when creating an icon, or else, provide an icon creation function in your script that works for your target OS.
My solution was to create command line scripts using setuptools. Here are a the relevant parts of myScript.py:
def main(pause_on_error=False):
if run():
print("we're good!")
else:
print("an error occurred!")
if pause_on_error:
raw_input("\nPress Enter to close.")
sys.exit(1)
def run():
pass # run the program here
return False # or True if program runs successfully
if __name__ == '__main__':
main(pause_on_error=True)
And the relevant parts of setup.py:
setup(
entry_points={
'console_scripts': [
'myScript = main:main',
]
},
)
Now if I open myScript.py with the Python interpreter (on Windows), the console window waits for the user to press enter if an error occurs. On the command line, if I run 'myScript', the program will never wait for user input before closing.
Although this isn't a very good solution, it does work (in windows at least).
You could create a batch file with the following contents:
#echo off
for %%x in (%cmdcmdline%) do if /i "%%~x"=="/c" set DOUBLECLICKED=1
start <location of python script>
if defined DOUBLECLICKED pause
If you want to be able to do this with a single file, you could try the following:
#echo off
setlocal EnableDelayedExpansion
set LF=^
:: The 2 empty lines are necessary
for %%x in (%cmdcmdline%) do if /i "%%~x"=="/c" set DOUBLECLICKED=1
echo print("first line of python script") %LF% print("second and so on") > %temp%/pyscript.py
start /wait console_title pyscript.py
del %temp%/pyscript.py
if defined DOUBLECLICKED pause
Batch code from: Pausing a batch file when double-clicked but not when run from a console window?
Multi-line in batch from: DOS: Working with multi-line strings
Okay, the easiest way I found and made was to simply run the program in the command line, even if it was ran in the Python IDLE.
exist = lambda x: os.path.exists(x) ## Doesn't matter
if __name__ == '__main__':
fname = "SomeRandomFileName" ## Random default file name
if exist(fname)==False: ## exist() is a pre-defined lambda function
jot(fname) ## jot() is a function that creates a blank file
os.system('start YourProgram.py') ## << Insert your program name here
os.system('exit'); sys.exit() ## Exits current shell (Either IDLE or CMD)
os.system('color a') ## Makes it look cool! :p
main() ## Runs your code
os.system("del %s" % fname) ## Deletes file name for next time
Add this to the bottom of your script and once ran from either IDLE or Command Prompt, it will create a file, re-run the program in the CMD, and exits the first instance.
Hope that helps! :)
I also had that question and, for me, the best solution is to set an environment variable in my IDE (PyCharm) and check if that variable exists to know if the script is being executed either via the command line or via the IDE.
To set an environment variable in PyCharm check:
How to set environment variables in PyCharm?
Example code (environment variable: RUNNING_PYCHARM = True):
import os
# The script is being executed via the command line
if not("RUNNING_PYCHARM" in os.environ):
raw_input("Press enter to close.")
I hope it works for you.
Based on existing solutions and using sets:
import psutil
def running_interactively():
"""Return True if any of our parent processes is a known shell."""
shells = {"cmd.exe", "bash.exe", "powershell.exe", "WindowsTerminal.exe"}
parent_names = {parent.name() for parent in psutil.Process().parents()}
# print(parent_names)
# print(f"Shell in parents? {shells & parent_names}")
return bool(shells & parent_names)
if not running_interactively():
input("\nPress ENTER to continue.")
This answer is currently specific to Windows, but it can be reconfigured to work with other operating systems in theory. Rather than installing psutil module like most of these answers recommend, you can make use of the subprocess module and the Windows tasklist command to explicitly get the name of the parent process of your Python program.
import os
import subprocess
shells = {"bash.exe", "cmd.exe", "powershell.exe", "WindowsTerminal.exe"}
# These are standard examples, but it can also be used to detect:
# - Nested python.exe processes (IDLE, etc.)
# - IDEs used to develop your program (IPython, Eclipse, PyCharm, etc.)
# - Other operating system dependent shells
s = subprocess.check_output(["tasklist", "/v", "/fo", "csv", "/nh", "/fi", f"PID eq {os.getppid()}"])
# Execute tasklist command to get the verbose info without the header (/nh) of a single process in CSV format (/fo csv)
# Such that its PID is equal to os.getppid()
entry = s.decode("utf-8").strip().strip('"').split('","')
# Decode from bytes to str, remove end whitespace and quotations from CSV format
# And split along the quote delimited commas
# This process may differ and require adjustment when used for an OS other than Windows
condition = entry and entry[0] in shells
# Check first that entry is not an empty sequence, meaning the process has already ended
# If it still exists, check if the first element -- the executable -- exists as an element of the set of executables you're looking for
I hope this is helpful for anyone looking for an answer to this problem while minimizing the number of dependencies you'd need.
This was tested in Python 3.8 and uses an f-string in the subprocess.check_output line of the code, so please be sure to convert the f-string to a compatible syntax if you're working with a version of Python before f-strings were introduced.

Categories