Python script fails execution on subprocess.run() call only when called from context menu - python

I have a python script that I want to call from the windows file browser context menu (https://www.howtogeek.com/107965/how-to-add-any-application-shortcut-to-windows-explorers-context-menu/)
I am currently debugging calling it from the non-specific context (HKEY_CLASSES_ROOT\Directory\Background\shell) with the command "python "D:\toolbox\mineAudio.py" 0"
(note python3 is on the path as python and the script is at D:\toolbox\mineAudio.py)
When I call the script from cmd it works as expected with that command, and when I make debug modifications to the script (adding os.system("pause") to random lines) I can verify it is running correctly up to the point it hits the line meta=cmd(['ffmpeg','-i',target]) (line 46) where it instantly and silently fails (note ffmpeg is also on the path)
EDIT: it actually gets as far as line 15 result = subprocess.run(command, stdout=subprocess.PIPE,stderr=subprocess.PIPE,startupinfo=startupinfo)
I cant figure out why the program is failing there as that line works fine everywhere else I have tested the script from other than the context menu.
Here is the full script if you want to brows through it
import subprocess
import os
import sys
from sys import argv
from tree import tree
#for command line use:
#mineAudo.py [prompt=1] [dir=cwd]
#first arg prompt will prompt user for dir if 1, otherwise it wont
#second arg is the directory to use, if specified this will override prompt, if not and prompt=0, current working dir is used
def cmd(command):
startupinfo = subprocess.STARTUPINFO()
startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
startupinfo.wShowWindow = subprocess.SW_HIDE
result = subprocess.run(command, stdout=subprocess.PIPE,stderr=subprocess.PIPE,startupinfo=startupinfo)
return result.stderr.decode("utf-8")
def stripStreams(meta):
i=1;
lines=[]
while i>0 :
i=meta.find("Stream",i+1)
lineEnd=meta.find("\n",i)
lines.append(meta[i:lineEnd])
return lines
def mineAudio(streams):
ret=[]
for stream in streams:
if "Audio:" in stream:
start =stream.find("#")+1
end=stream.find("(",start)
ret.append(stream[start:end])
return ret
def convDir(dirTarget):
targets=tree(dirTarget)
convList(targets,dirTarget)
def convList(targets,dirTarget):
print(targets)
#target="2018-05-31 06-16-39.mp4"
i=0
for target in targets:
i+=1
if(target[target.rfind("."):]==".mp4"):
print("("+str(i)+"/"+str(len(targets))+") starting file "+target)
meta=cmd(['ffmpeg','-i',target])
streams=stripStreams(meta)
streams=mineAudio(streams)
count=0
output=target[target.rfind("/")+1:target.rfind(".")]
file=target[target.rfind("/")+1:]
#print (output)
try:
os.mkdir(dirTarget+"\\"+output)
except:
pass
for s in streams:
print("converting track "+str(count+1)+" of "+str(len(streams)));
count+=1
cmd("ffmpeg -i \""+target+"\" -vn -sn -c:a mp3 -ab 192k -map "+s+" \""+dirTarget+"\\"+output+"\\"+output+" Track "+str(count)+".mp3\"")
print("moving "+target+" to "+dirTarget+"\\"+output+"\\"+file)
os.rename(target,dirTarget+"\\"+output+"\\"+file)
print("Finished file "+target)
else:
print("("+str(i)+"/"+str(len(targets))+") skiping non mp4 file "+target)
def prompt():
while True:
dirTarget=input("input target dir: ")
convDir(dirTarget)
if __name__ == "__main__":
sys.setrecursionlimit(2000)
if len(argv)>2:
if os.path.isdir(argv[2]):
convDir(argv[2])
else:
convList([argv[2]],os.path.dirname(argv[2]))
elif(len(argv)>1):
if int(argv[1])==1:
prompt()
else:
convDir(os.getcwd())
else:
prompt()
os.system("pause")
Note that I am not married to this particular implementation, any implementation with the same effect (extracting the .mp3 tracks from an .mp4 file automatically) would be fine too
also, here is the file Tree
#Returns the paths of all files in a directory and all sub directories relative to start directory
import os
def tree(directory,target="f"):
paths=[]
for currentDir,dirs,files in os.walk(directory):
if target=="f":
for file in files:
paths.append(currentDir+"/"+file)
if target=="d":
#paths.append(currentDir)
for dir in dirs:
paths.append(currentDir+"/"+dir)
for i in range(len(paths)):
paths[i]=paths[i].replace("\\","/")
return paths
Can anyone help me get this working?
Edit:
here is a shorter example code that crashes in the same way (still uses ffmpeg though)
import subprocess
import os
def cmd(command):
startupinfo = subprocess.STARTUPINFO()
startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
startupinfo.wShowWindow = subprocess.SW_HIDE
result = subprocess.run(command,stdin=subprocess.DEVNULL, stdout=subprocess.PIPE,stderr=subprocess.PIPE,startupinfo=startupinfo)
return result.stderr.decode("utf-8")
os.system("pause")
out=cmd(['ffmpeg','-i','D:\\ffmpeg test\\test\\2018-05-31 06-16-39\\2018-05-31 06-16-39.mp4'])
print(out)
os.system("pause")
(note the file is hard coded, program output should be
)

I managed to "solve" the problem by the round about hackish way of creating a batch file that calls the python script command, but this does seem a bit of a hack, and I would think there would be a better way.

Related

Print from pdf file to paper [duplicate]

I have a PDF document and I want to print it with my python app.
I have tried the solution in here (Print PDF document with python's win32print module?) but when I install Ghostscript 9.15 that is the actual version, it has no gsprint.exe
The way I am using that works is with the command os.startfile('PDFfile.pdf', "print") but it opens default viewer (mine is Adobe Reader) and after printing it stills open, trying to kill the process with os.system("TASKKILL /F /IM AcroRD32.exe") kills other opened windows and I dont want it.
With the next command, it also prints, but it let the Adobe Reader opened too
currentprinter = win32print.GetDefaultPrinter()
win32api.ShellExecute(0, "print", 'PDFfile.pdf', '/d:"%s"' % currentprinter, ".", 0)
I have seen this answer too but they recommend using gsprint.exe again
Anybody has the gsprint.exe file or any other solution?.
NOTE: When I used another default program to open PDF files like Chrome or Windows Reader, I always get an Exception in the execution of the commands above '(31, 'ShellExecute', 'A device attached to the system is not functioning.')' or [Error 1155] No application is associated with the specified file for this operation: 'PDFfile.pdf' with the startfile command
Finally after hours and hours of searching for the right files, i have found the answer to my problem.
You can download the GSPRINT in HERE
You can download the Ghostscript GPL in HERE
With this extracted files in your PC (Windows) you can print your PDF with this command
GHOSTSCRIPT_PATH = "C:\\path\\to\\GHOSTSCRIPT\\bin\\gswin32.exe"
GSPRINT_PATH = "C:\\path\\to\\GSPRINT\\gsprint.exe"
# YOU CAN PUT HERE THE NAME OF YOUR SPECIFIC PRINTER INSTEAD OF DEFAULT
currentprinter = win32print.GetDefaultPrinter()
win32api.ShellExecute(0, 'open', GSPRINT_PATH, '-ghostscript "'+GHOSTSCRIPT_PATH+'" -printer "'+currentprinter+'" "PDFFile.pdf"', '.', 0)
The GhostScript can also be found in the Official page HERE
I found the gsprint.exe for 64bits HERE
I hope this helps.
I know this is an old question, but in case someone is looking for it here is how I fixed it.
I am using python 3.8 and gs9.52 on windows 10 64-bit and python3-ghostscript library which you can install using pip install python3-ghostscript I am also using pypiwin32 to get the default printer name, you can install it with pip pip install pypiwin32
This is the working script
import tempfile
import win32print
import locale
import ghostscript
import render_to_pdf
pdf = render_to_pdf('print/slip.html', context)
temp1 = tempfile.mktemp('.pdf')
f1 = open(temp1, 'ab')
f1.write(pdf)
f1.close()
args = [
"-dPrinted", "-dBATCH", "-dNOSAFER", "-dNOPAUSE", "-dNOPROMPT"
"-q",
"-dNumCopies#1",
"-sDEVICE#mswinpr2",
f'-sOutputFile#"%printer%{win32print.GetDefaultPrinter()}"',
f'"{temp1}"'
]
encoding = locale.getpreferredencoding()
args = [a.encode(encoding) for a in args]
ghostscript.Ghostscript(*args)
Few things to note here I am using '#' instead of '=' because for some reason it was not working with '='.
If this is not working for you try changing -sDEVICE switch to your printer type for example when I was using HP LaserJet it was giving me prompt so I changed my -sDEVICE to laserjet and it worked, you can get the list of device by running gs -h in terminal
Here's a way to silently print a pdf in the same directory as your python script without gsprint and without win32api. It allows for more GhostScript customization like choosing width/height, etc.
import os
import subprocess
import sys
if sys.platform == 'win32':
args = '"C:\\\\Program Files\\\\gs\\\\gs9.23\\\\bin\\\\gswin64c" ' \
'-sDEVICE=mswinpr2 ' \
'-dBATCH ' \
'-dNOPAUSE ' \
'-dFitPage ' \
'-sOutputFile="%printer%myPrinterName" '
ghostscript = args + os.path.join(os.getcwd(), 'myFile.pdf').replace('\\', '\\\\')
subprocess.call(ghostscript, shell=True)
If you're using the 32 bit version of GhostScript then you would use gswin32c
If you want to print specific pages and some other parameters, you should specify them in the parameters of gsprint as follows:
import win32print
import win32api
GHOSTSCRIPT_PATH = "C:\\path\\to\\GHOSTSCRIPT\\bin\\gswin32.exe"
GSPRINT_PATH = "C:\\path\\to\\GSPRINT\\gsprint.exe"
params = '-ghostscript "'+ GHOSTSCRIPT_PATH +'" -printer "'+currentprinter+'" -from 1 -to 3 -landscape -copies 1 "1.pdf "'
print(params)
win32api.ShellExecute(0, 'open', GSPRINT_PATH, params, '.',0)
The following code will block the current task
for i in range(10):
currentprinter = win32print.GetDefaultPrinter()
win32api.ShellExecute(0, "print", 'PDFfile.pdf', '/d:"%s"' % currentprinter, ".", 0)
and killing the reader after printing help won't block the current task
os.system("TASKKILL /F /IM AcroRD32.exe")
but it will close other pdf files too.
If you can't use gsprint, use the acrobat command
import win32print
import subprocess
import time
pdf_file = 'D:\d1\d1.pdf'
acrobat = 'C:\Program Files (x86)\Adobe\Acrobat Reader DC\Reader\AcroRd32.exe'
name = win32print.GetDefaultPrinter()
cmd = '"{}" /n /o /t "{}" "{}"'.format(acrobat, pdf_file, name)
for i in range(10)):
proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
It won't block the current task and close the other pdf files.
Based on previous answers and other posts, i develop the following script to print .pdf and .ps from a Laravel website.
I used python 3.9 and Ghostscript 9.54 (for 64bits). pywin32 and python3-ghostscript libs are requiered too.
import os
import sys
import win32print
import win32api
import ghostscript
import locale
USELESS_PRINTER = ['OneNote for Windows 10', 'OneNote (Desktop)', 'Microsoft XPS Document Writer',
'Microsoft Print to PDF', 'Fax']
HELP = """pyPrinter - Author: Arthur SICARD - Date: 19/05/2021
\n-help
\tDisplay this message.
\n-list [-virtual]
\tReturn list of available printer (excepted: """ + ", ".join(USELESS_PRINTER) + """)
\n-file filepath [-printer printer_name] [-virtual]
\tPrint specified file on default system printer. Use -printer to specify printer to use. Printer name must be available un -list response.
\n-batch filepath [-printer printer_name] [-virtual]
\tPrint each document specified un batch file on default system printer. Batch file must be a .txt. Each file to print must be write on its own line.
\tUse -printer to specify printer to use. Printer name must be available un -list response.
\n-virtual
\tUse this option after all other arguments to enable printing on virtual printer 'Microsoft Print to PDF'
"""
# Safe accessor to argv. Return None if index is not set
def getArgv(index):
try:
return (sys.argv[1:])[index]
except:
return None
# Return list of local printer available without "virtual printer" define in USELESS_PRINTER list.
def getAvailablePrinters():
printers = win32print.EnumPrinters(win32print.PRINTER_ENUM_LOCAL)
printer_list = []
for x in range(len(printers)):
if printers[x][2] not in USELESS_PRINTER:
printer_list.append(printers[x][2])
return printer_list
# Return printer name to use. If -printer is set it will return this value only if value match with available
# printers list. Return a error if -printer not in list. If no printer specified, retrieve default printer and return
# its name. Sometime default printer is on USELESS_PRINTER list so first printer return by getAvailablePrinters() is
# return. If no printer is return display an error.
def getPrinter():
default_printer = win32print.GetDefaultPrinter()
if default_printer in USELESS_PRINTER:
if len(getAvailablePrinters()) == 0:
print("No printer available, unable to print. Use -virtual if you want enable virtual printer.")
sys.exit(1801)
default_printer = getAvailablePrinters()[0]
if getArgv(2) is not None:
if getArgv(2) == "-printer":
printer = getArgv(3)
if printer in getAvailablePrinters():
return printer
else:
if printer is not None:
print("Given printer not found. Defaut printer configured: ", default_printer)
return default_printer
# Use GhostScript API to silent print .pdf and .ps. Use win32api to print .txt. Return a error if printing failed or
# file ext doesn't match.
def printFile(filepath):
try:
if os.path.splitext(filepath)[1] in [".pdf", ".ps"]:
args = [
"-dPrinted", "-dBATCH", "-dNOSAFER", "-dNOPAUSE", "-dNOPROMPT"
"-q",
"-dNumCopies#1",
"-sDEVICE#mswinpr2",
f'-sOutputFile#"%printer%{getPrinter()}"',
f'"{filepath}"'
]
encoding = locale.getpreferredencoding()
args = [a.encode(encoding) for a in args]
ghostscript.Ghostscript(*args)
elif os.path.splitext(filepath)[1] in [".txt"]:
# '"%s"' % enable to encapsulate string with quote
win32api.ShellExecute(0, "printto", '"%s"' % filepath, '"%s"' % getPrinter(), ".", 0)
return True
except:
print("Printing error for file: ", '"%s"' % filepath, "| Printer: ", '"%s"' % getPrinter())
return False
def main(argv):
if len(argv) in [1, 2, 4, 5]:
cmd1 = getArgv(0)
filepath = getArgv(1)
if argv[-1] == "-virtual":
USELESS_PRINTER.remove('Microsoft Print to PDF')
# Batch printing mode
if cmd1 == "-batch" and len(argv) in [2, 4, 5]:
if not os.path.isfile(filepath) and not os.path.exists(filepath):
print("Path provide for batch file is not a valid file path or doesn't exist.")
sys.exit(2)
if os.path.splitext(filepath)[1] in [".txt"]:
with open(filepath) as fp:
line = fp.readline().strip('\n')
while line:
if not os.path.isfile(line) and not os.path.exists(line):
print("Path provide is not a valid file path or doesn't exist: ", line)
else:
printFile(line)
line = fp.readline().strip('\n')
fp.close()
else:
print("Not supported file format for batch printing.")
sys.exit(50)
# Single file printing mode
elif cmd1 == "-file" and len(argv) in [2, 4, 5]:
if not os.path.isfile(filepath) and not os.path.exists(filepath):
print("Path provide is not a file path.")
sys.exit(2)
if not printFile(filepath):
sys.exit(1)
# Get printers list
elif cmd1 == "-list" and len(argv) in [1, 2]:
for printer in getAvailablePrinters():
print(printer)
# Display help
elif cmd1 == "-help" and len(argv) in [1]:
print(HELP)
sys.exit(0)
else:
print("Unknow option. Use -help to obtain more informations about supported options.")
sys.exit(50)
else:
print("Wrong arguments number. Use -help to obtain more informations about supported options.")
sys.exit(50)
exit(0)
if __name__ == '__main__':
main(sys.argv[1:])
Following command explain how to use it:
python main.py -help
pyPrinter - Author: Arthur - Date: 19/05/2021
-help
Display this message.
-list [-virtual]
Return list of available printer (excepted: OneNote for Windows 10, OneNote (Desktop), Microsoft XPS Document Writer, Microsoft Print to PDF, Fax)
-file filepath [-printer printer_name] [-virtual]
Print specified file on default system printer. Use -printer to specify printer to use. Printer name must be available un -list response.
-batch filepath [-printer printer_name] [-virtual]
Print each document specified un batch file on default system printer. Batch file must be a .txt. Each file to print must be write on its own line.
Use -printer to specify printer to use. Printer name must be available un -list response.
-virtual
Use this option after all other arguments to enable printing on virtual printer 'Microsoft Print to PDF'
To print one file to printer HP1FF6CC (HP OfficeJet Pro 6970)
python main.py -file "D:\my\system\path\to\file\pattern.ps" -printer "HP1FF6CC (HP OfficeJet Pro 6970)"
To print one file to virtual printer Microsoft Print to PDF (generally for text purpose, paper is quickly expensive)
python main.py -file "D:\my\system\path\to\file\pattern.ps" -printer "Microsoft Print to PDF" -virtual
There is another method to send the file to printer without adobe reader also, by SumatraPDF
install SumatraPDF application and add the SumatraPDF.exe location to the path
# import subprocess and os
import subprocess
import os
# file path
file_name = "Document.pdf"
if file_name:
print("exist")
# send data to the printer
try:
subprocess.call(['SumatraPDF.exe', '-print-to', "Barcode",
'-print-settings', "1x", file_name])
except BaseException as msg:
print(msg)
there is no need to have any adobe reader, and for every time it wont open the adobe reader to read data,
but issue is that should try on page orientation and page size,
but if those are same always for pdf seems we can change in the printer properties directly,
only to add exe path to the file path in system
thats it
So this isn't exactly silently, but it will automatically dismiss the dialog box and print anyway, also has a weird dependency on selenium that you wouldn't expect per se, but this actually worked for me since I was in a world where I wasn't allowed to download ghostscript nor could I download adobe's pdf reader. I thought it might help someone else out there, somewhere, some place, some time...
from selenium import webdriver
import win32com.client
import win32print
import time
def printpdf(pdf,printer):
current_printer = win32print.GetDefaultPrinter()
win32print.SetDefaultPrinter(printer)
driver = webdriver.Chrome()
driver.get(pdf)
time.sleep(1) #Adjust as necessary
shell = win32com.client.Dispatch("WScript.Shell")
shell.SendKeys('^p')
time.sleep(1) #Adjust as necessary
shell.SendKeys('{ENTER}') #dismiss the print dialog box
driver.close()
win32print.SetDefaultPrinter(current_printer)
If you have Adobe try this:
import win32api
import winreg
import subprocess
import time
def get_adobe_executable():
with winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE) as conn:
with winreg.OpenKey(conn, r'SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\AcroRd32.exe', 0, winreg.KEY_READ) as hkey:
value = winreg.QueryValue(hkey, None)
if value:
value = '"{}"'.format(value)
return value.strip()
return None
def print_pdf_file(file, printer_name=None, secs=5):
cmd = get_adobe_executable()
if cmd is None:
return False
if printer_name:
cmd = '{} /h /t "{}" "{}"'.format(cmd, file, printer_name)
else:
cmd = '{} /p /h "{}"'.format(cmd, file)
proc = subprocess.Popen(cmd)
time.sleep(secs)
proc.kill()
return True
if __name__ == "__main__":
print_pdf_file("doc.pdf") # print with default printer
print_pdf_file("doc.pdf", "HP LaserJet Pro M102") # setting the printer name
get_adobe_executable
Get the Adobe from the registry(you can also get the print command like the one that you right click the pdf file and select Print from the menu but I wanted to just get the path then configure it according to the printer configuration)
print_pdf_file
If you don't set the printer_name variable, Adobe will print with the default printer
After executing the print command it will wait 5 seconds and then close Adobe program, Adobe it does not have command line option (as today of writing) to exit after printing the file, here you can see the command line options

Using subprocess.check_output to run flake8

I am trying to make a basic linter script which I can run on Python files in the current directory. So far my script looks like this:
import subprocess
from os import listdir
from os.path import isfile, join
if __name__ == "__main__":
subprocess.check_output(["black", "-l", "100", "./"])
files = [f for f in listdir("./") if isfile(join("./", f))]
for file in files:
if file.endswith(".py"):
subprocess.check_output(["flake8", file])
I am wanting to run the code via the command line with a call such as main.py. Black performs fine and finds the .py files in the current directory and formats them without a problem. However, when trying to run a similar command with flake8, it also runs on children of the directory such as the venv folder which I am not interested in.
Therefore, the script includes a check to get the files in the current directory and then find the .py files. However, once I get those files, I cannot seem to use my flake8 command with subprocess.check_output. The error I get is as follows:
Traceback (most recent call last):
File "linter.py", line 18, in <module>
subprocess.check_output(["flake8", file], shell=False)
File "C:\Users\calum\AppData\Local\Programs\Python\Python38\lib\subprocess.py", line 411, in check_output
return run(*popenargs, stdout=PIPE, timeout=timeout, check=True,
File "C:\Users\calum\AppData\Local\Programs\Python\Python38\lib\subprocess.py", line 512, in run
raise CalledProcessError(retcode, process.args,
subprocess.CalledProcessError: Command '['flake8', 'flask_server_controller.py']' returned non-zero exit status 1.
Could someone please explain the error and/or provide a solution to my problem. I would also like to add other linting tools to the script such as pylint, however, I am worried I will run into the same problem without understanding it properly.
Thanks in advance.
subprocess.check_output is giving you that because of the check_ aspect
this means that when the executable you're running returns nonzero (for example, flake8 returns nonzero when it detects lint failures) an exception will be raised
To avoid this behaviour, I'd suggest using subprocess.run instead, and forwarding along the return code. Something like this:
import os
import subprocess
import sys
def main():
ret = 0
output = b''
for filename in os.listdir('.'):
if filename.endswith('.py'):
proc_ret = subprocess.run(
('flake8', filename),
stdout=subprocess.PIPE,
)
ret |= proc_ret.returncode
output += proc_ret.stdout
sys.stdout.buffer.write(output)
return ret
if __name__ == '__main__':
exit(main())
Note that this is going to be prohibitively slow, you have to incur the startup cost of flake8 for every file.
One way you can improve this is by passing all the filenames to flake8 at once:
import os
import subprocess
import sys
def main():
filenames = (fname for fname in os.listdir('.') if fname.endswith('.py'))
proc_ret = subprocess.run(('flake8', *filenames), stdout=subprocess.PIPE)
sys.stdout.buffer.write(proc_ret.stdout)
return proc_ret.returncode
if __name__ == '__main__':
exit(main())
but also this brings up another interesting point, why are you collecting the output at all? if you let the output go to stdout it will be printed automatically:
import os
import subprocess
def main():
filenames = (fname for fname in os.listdir('.') if fname.endswith('.py'))
return subprocess.call(('flake8', *filenames))
if __name__ == '__main__':
exit(main())
and hmmm, you probably don't need to do this at all since flake8 has its own inclusion / exclusion code -- you probably just want to configure exclude properly
# setup.cfg / tox.ini / .flake8
[flake8]
# for example, exclude recursing into the venv
exclude = venv
and then you can use flake8 . as normal
(disclaimer: I am the current maintainer of flake8)

how can i pass a file name in python tkinter using subprocess

below is my code this is a mini IDE for a class project I am stuck here am trying to build an IDE that compiles java. I downloaded JDK and am using subprocess to pipe cmd and communicate with javac but I need to pass the file name with extension so it just shows output and I also need help with outputting a console because it tends to only open in visual studio terminal please help me because I will be submitting on Thursday.
femi.femiii#gmail.com
from tkinter import filedialog
from tkinter import messagebox
import subprocess
import os
name_file = os.path.basename(__file__)
# run button that opens command line
def run(self, *args):
p1 = subprocess.Popen('cmd', shell=True, stdin=None, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
p2 = subprocess.Popen('javac name_file; java name_file', shell=True, stdin=p1.stdout)
p1.stdout.close()
out, err = p2.communicate()
if __name__ == "__main__":
master = tk.Tk()
pt = PyText(master)
master.mainloop()```
The easiest way I can think of would be to use sys argv's to pass the file name to a node or java routine
modify your python script to pass info to command line in argv
fname = file
#file you want to pass
pythonCall = 'node javascript.js -- '+fname
#the -- passes what follows into the sys argvs
#this is python notation, java/node may not need the '--' but just a space
runCommand = 'cmd.exe /c ' + pythonCall
subprocess.Popen(runCommand)
then at the start of your javascript, this will give you the file name that was passed into argv, its from the link I included below.
var args = process.argv.slice(2);
if you need more help accessing process.argv in java:
How do I pass command line arguments to a Node.js program?

How to open external programs in Python

Duplicate edit: no, i did that but it doesnt want to launch firefox.
I am making a cortana/siri assistant thing, and I want it to lets say open a web browser when I say something. So I have done the if part, but I just need it to launch firefox.exe I have tried different things and I get an error . Here is the code. Please help! It works with opening notepad but not firefox..
#subprocess.Popen(['C:\Program Files\Mozilla Firefox\firefox.exe']) opens the app and continues the script
#subprocess.call(['C:\Program Files\Mozilla Firefox\firefox.exe']) this opens it but doesnt continue the script
import os
import subprocess
print "Hello, I am Danbot.. If you are new ask for help!" #intro
prompt = ">" #sets the bit that indicates to input to >
input = raw_input (prompt) #sets whatever you say to the input so bot can proces
raw_input (prompt) #makes an input
if input == "help": #if the input is that
print "*****************************************************************" #says that
print "I am only being created.. more feautrues coming soon!" #says that
print "*****************************************************************" #says that
print "What is your name talks about names" #says that
print "Open (name of program) opens an application" #says that
print "sometimes a command is ignored.. restart me then!"
print "Also, once you type in a command, press enter a couple of times.."
print "*****************************************************************" #says that
raw_input (prompt) #makes an input
if input == "open notepad": #if the input is that
print "opening notepad!!" #says that
print os.system('notepad.exe') #starts notepad
if input == "open the internet": #if the input is that
print "opening firefox!!" #says that
subprocess.Popen(['C:\Program Files\Mozilla Firefox\firefox.exe'])
The short answer is that os.system doesn't know where to find firefox.exe.
A possible solution would be to use the full path. And it is recommended to use the subprocess module:
import subprocess
subprocess.call(['C:\Program Files\Mozilla Firefox\\firefox.exe'])
Mind the \\ before the firefox.exe! If you'd use \f, Python would interpret this as a formfeed:
>>> print('C:\Program Files\Mozilla Firefox\firefox.exe')
C:\Program Files\Mozilla Firefox
irefox.exe
And of course that path doesn't exist. :-)
So either escape the backslash or use a raw string:
>>> print('C:\Program Files\Mozilla Firefox\\firefox.exe')
C:\Program Files\Mozilla Firefox\firefox.exe
>>> print(r'C:\Program Files\Mozilla Firefox\firefox.exe')
C:\Program Files\Mozilla Firefox\firefox.exe
Note that using os.system or subprocess.call will stop the current application until the program that is started finishes. So you might want to use subprocess.Popen instead. That will launch the external program and then continue the script.
subprocess.Popen(['C:\Program Files\Mozilla Firefox\\firefox.exe', '-new-tab'])
This will open firefox (or create a new tab in a running instance).
A more complete example is my open utility I publish via github. This uses regular expressions to match file extensions to programs to open those files with. Then it uses subprocess.Popen to open those files in an appropriate program. For reference I'm adding the complete code for the current version below.
Note that this program was written with UNIX-like operating systems in mind. On ms-windows you could probably get an application for a filetype from the registry.
"""Opens the file(s) given on the command line in the appropriate program.
Some of the programs are X11 programs."""
from os.path import isdir, isfile
from re import search, IGNORECASE
from subprocess import Popen, check_output, CalledProcessError
from sys import argv
import argparse
import logging
__version__ = '1.3.0'
# You should adjust the programs called to suit your preferences.
filetypes = {
'\.(pdf|epub)$': ['mupdf'],
'\.html$': ['chrome', '--incognito'],
'\.xcf$': ['gimp'],
'\.e?ps$': ['gv'],
'\.(jpe?g|png|gif|tiff?|p[abgp]m|svg)$': ['gpicview'],
'\.(pax|cpio|zip|jar|ar|xar|rpm|7z)$': ['tar', 'tf'],
'\.(tar\.|t)(z|gz|bz2?|xz)$': ['tar', 'tf'],
'\.(mp4|mkv|avi|flv|mpg|movi?|m4v|webm)$': ['mpv']
}
othertypes = {'dir': ['rox'], 'txt': ['gvim', '--nofork']}
def main(argv):
"""Entry point for this script.
Arguments:
argv: command line arguments; list of strings.
"""
if argv[0].endswith(('open', 'open.py')):
del argv[0]
opts = argparse.ArgumentParser(prog='open', description=__doc__)
opts.add_argument('-v', '--version', action='version',
version=__version__)
opts.add_argument('-a', '--application', help='application to use')
opts.add_argument('--log', default='warning',
choices=['debug', 'info', 'warning', 'error'],
help="logging level (defaults to 'warning')")
opts.add_argument("files", metavar='file', nargs='*',
help="one or more files to process")
args = opts.parse_args(argv)
logging.basicConfig(level=getattr(logging, args.log.upper(), None),
format='%(levelname)s: %(message)s')
logging.info('command line arguments = {}'.format(argv))
logging.info('parsed arguments = {}'.format(args))
fail = "opening '{}' failed: {}"
for nm in args.files:
logging.info("Trying '{}'".format(nm))
if not args.application:
if isdir(nm):
cmds = othertypes['dir'] + [nm]
elif isfile(nm):
cmds = matchfile(filetypes, othertypes, nm)
else:
cmds = None
else:
cmds = [args.application, nm]
if not cmds:
logging.warning("do not know how to open '{}'".format(nm))
continue
try:
Popen(cmds)
except OSError as e:
logging.error(fail.format(nm, e))
else: # No files named
if args.application:
try:
Popen([args.application])
except OSError as e:
logging.error(fail.format(args.application, e))
def matchfile(fdict, odict, fname):
"""For the given filename, returns the matching program. It uses the `file`
utility commonly available on UNIX.
Arguments:
fdict: Handlers for files. A dictionary of regex:(commands)
representing the file type and the action that is to be taken for
opening one.
odict: Handlers for other types. A dictionary of str:(arguments).
fname: A string containing the name of the file to be opened.
Returns: A list of commands for subprocess.Popen.
"""
for k, v in fdict.items():
if search(k, fname, IGNORECASE) is not None:
return v + [fname]
try:
if b'text' in check_output(['file', fname]):
return odict['txt'] + [fname]
except CalledProcessError:
logging.warning("the command 'file {}' failed.".format(fname))
return None
if __name__ == '__main__':
main(argv)
If you want to open Google or something on the web just import webbrowser and open the URL. I will give you a quick example.
import webbrowser
webbrowser.open("www.google.com")
You can open any program like this:
import subprocess
# This will pause the script until fortnite is closed
subprocess.call(['C:\Program Files\Fortnite\fortnite.exe'])
# But this will run fortnite and continue the script
subprocess.Popen(['C:\Program Files\Fortnite\fortnite.exe', '-new-tab'])
But here's How to find the .exe file of any program (Windows only)
I only know how to find the .exe file in windows
First, click the windows icon then write task manager, open it, then find Fortnite if there's an arrow on it's left click it, if there's another one click it.
Make sure that you press the More Details button at the bottom.
Then scroll to the side until you find Command line written on the top row, here there will be the path of the .exe file of every running task
There might be -(SOME_LETTER) but these are some options for choosing what the exe file will do when it's ran. These are different for every exe file
(Little tips)
You can press Ctrl+Shift+Esc to open the task manager.
Go in the task manager then press Options (at the top) then press Always on top, which will make the app appear on top of every thing which is pretty useful when an app crashes and you need to close it using the task manager
Thanks.

Logging last Bash command to file from script

I write lots of small scripts to manipulate files on a Bash-based server. I would like to have a mechanism by which to log which commands created which files in a given directory. However, I don't just want to capture every input command, all the time.
Approach 1: a wrapper script that uses a Bash builtin (a la history or fc -ln -1) to grab the last command and write it to a log file. I have not been able to figure out any way to do this, as the shell builtin commands do not appear to be recognized outside of the interactive shell.
Approach 2: a wrapper script that pulls from ~/.bash_history to get the last command. This, however, requires setting up the Bash shell to flush every command to history immediately (as per this comment) and seems also to require that the history be allowed to grow inexorably. If this is the only way, so be it, but it would be great to avoid having to edit the ~/.bashrc file on every system where this might be implemented.
Approach 3: use script. My problem with this is that it requires multiple commands to start and stop the logging, and because it launches its own shell it is not callable from within another script (or at least, doing so complicates things significantly).
I am trying to figure out an implementation that's of the form log_this.script other_script other_arg1 other_arg2 > file, where everything after the first argument is logged. The emphasis here is on efficiency and minimizing syntax overhead.
EDIT: iLoveTux and I both came up with similar solutions. For those interested, my own implementation follows. It is somewhat more constrained in its functionality than the accepted answer, but it also auto-updates any existing logfile entries with changes (though not deletions).
Sample usage:
$ cmdlog.py "python3 test_script.py > test_file.txt"
creates a log file in the parent directory of the output file with the following:
2015-10-12#10:47:09 test_file.txt "python3 test_script.py > test_file.txt"
Additional file changes are added to the log;
$ cmdlog.py "python3 test_script.py > test_file_2.txt"
the log now contains
2015-10-12#10:47:09 test_file.txt "python3 test_script.py > test_file.txt"
2015-10-12#10:47:44 test_file_2.txt "python3 test_script.py > test_file_2.txt"
Running on the original file name again changes the file order in the log, based on modification time of the files:
$ cmdlog.py "python3 test_script.py > test_file.txt"
produces
2015-10-12#10:47:44 test_file_2.txt "python3 test_script.py > test_file_2.txt"
2015-10-12#10:48:01 test_file.txt "python3 test_script.py > test_file.txt"
Full script:
#!/usr/bin/env python3
'''
A wrapper script that will write the command-line
args associated with any files generated to a log
file in the directory where the files were made.
'''
import sys
import os
from os import listdir
from os.path import isfile, join
import subprocess
import time
from datetime import datetime
def listFiles(mypath):
"""
Return relative paths of all files in mypath
"""
return [join(mypath, f) for f in listdir(mypath) if
isfile(join(mypath, f))]
def read_log(log_file):
"""
Reads a file history log and returns a dictionary
of {filename: command} entries.
Expects tab-separated lines of [time, filename, command]
"""
entries = {}
with open(log_file) as log:
for l in log:
l = l.strip()
mod, name, cmd = l.split("\t")
# cmd = cmd.lstrip("\"").rstrip("\"")
entries[name] = [cmd, mod]
return entries
def time_sort(t, fmt):
"""
Turn a strftime-formatted string into a tuple
of time info
"""
parsed = datetime.strptime(t, fmt)
return parsed
ARGS = sys.argv[1]
ARG_LIST = ARGS.split()
# Guess where logfile should be put
if (">" or ">>") in ARG_LIST:
# Get position after redirect in arg list
redirect_index = max(ARG_LIST.index(e) for e in ARG_LIST if e in ">>")
output = ARG_LIST[redirect_index + 1]
output = os.path.abspath(output)
out_dir = os.path.dirname(output)
elif ("cp" or "mv") in ARG_LIST:
output = ARG_LIST[-1]
out_dir = os.path.dirname(output)
else:
out_dir = os.getcwd()
# Set logfile location within the inferred output directory
LOGFILE = out_dir + "/cmdlog_history.log"
# Get file list state prior to running
all_files = listFiles(out_dir)
pre_stats = [os.path.getmtime(f) for f in all_files]
# Run the desired external commands
subprocess.call(ARGS, shell=True)
# Get done time of external commands
TIME_FMT = "%Y-%m-%d#%H:%M:%S"
log_time = time.strftime(TIME_FMT)
# Get existing entries from logfile, if present
if LOGFILE in all_files:
logged = read_log(LOGFILE)
else:
logged = {}
# Get file list state after run is complete
post_stats = [os.path.getmtime(f) for f in all_files]
post_files = listFiles(out_dir)
# Find files whose states have changed since the external command
changed = [e[0] for e in zip(all_files, pre_stats, post_stats) if e[1] != e[2]]
new = [e for e in post_files if e not in all_files]
all_modded = list(set(changed + new))
if not all_modded: # exit early, no need to log
sys.exit(0)
# Replace files that have changed, add those that are new
for f in all_modded:
name = os.path.basename(f)
logged[name] = [ARGS, log_time]
# Write changed files to logfile
with open(LOGFILE, 'w') as log:
for name, info in sorted(logged.items(), key=lambda x: time_sort(x[1][1], TIME_FMT)):
cmd, mod_time = info
if not cmd.startswith("\""):
cmd = "\"{}\"".format(cmd)
log.write("\t".join([mod_time, name, cmd]) + "\n")
sys.exit(0)
You can use the tee command, which stores its standard input to a file and outputs it on standard output. Pipe the command line into tee, and pipe tee's output into a new invocation of your shell:
echo '<command line to be logged and executed>' | \
tee --append /path/to/your/logfile | \
$SHELL
i.e., for your example of other_script other_arg1 other_arg2 > file,
echo 'other_script other_arg1 other_arg2 > file' | \
tee --append /tmp/mylog.log | \
$SHELL
If your command line needs single quotes, they need to be escaped properly.
OK, so you don't mention Python in your question, but it is tagged Python, so I figured I would see what I could do. I came up with this script:
import sys
from os.path import expanduser, join
from subprocess import Popen, PIPE
def issue_command(command):
process = Popen(command, stdout=PIPE, stderr=PIPE, shell=True)
return process.communicate()
home = expanduser("~")
log_file = join(home, "command_log")
command = sys.argv[1:]
with open(log_file, "a") as fout:
fout.write("{}\n".format(" ".join(command)))
out, err = issue_command(command)
which you can call like (if you name it log_this and make it executable):
$ log_this echo hello world
and it will put "echo hello world" in a file ~/command_log, note though that if you want to use pipes or redirection you have to quote your command (this may be a real downfall for your use case or it may not be, but I haven't figured out how to do this just yet without the quotes) like this:
$ log_this "echo hello world | grep h >> /tmp/hello_world"
but since it's not perfect, I thought I would add a little something extra.
The following script allows you to specify a different file to log your commands to as well as record the execution time of the command:
#!/usr/bin/env python
from subprocess import Popen, PIPE
import argparse
from os.path import expanduser, join
from time import time
def issue_command(command):
process = Popen(command, stdout=PIPE, stderr=PIPE, shell=True)
return process.communicate()
home = expanduser("~")
default_file = join(home, "command_log")
parser = argparse.ArgumentParser()
parser.add_argument("-f", "--file", type=argparse.FileType("a"), default=default_file)
parser.add_argument("-p", "--profile", action="store_true")
parser.add_argument("command", nargs=argparse.REMAINDER)
args = parser.parse_args()
if args.profile:
start = time()
out, err = issue_command(args.command)
runtime = time() - start
entry = "{}\t{}\n".format(" ".join(args.command), runtime)
args.file.write(entry)
else:
out, err = issue_command(args.command)
entry = "{}\n".format(" ".join(args.command))
args.file.write(entry)
args.file.close()
You would use this the same way as the other script, but if you wanted to specify a different file to log to just pass -f <FILENAME> before your actual command and your log will go there, and if you wanted to record the execution time just provide the -p (for profile) before your actual command like so:
$ log_this -p -f ~/new_log "echo hello world | grep h >> /tmp/hello_world"
I will try to make this better, but if you can think of anything else this could do for you, I am making a github project for this where you can submit bug reports and feature requests.

Categories