Select tray with python-cups? - python

How can I select tray when I send a file to the printer with python-cups?
I have a very simple testprogram that looks like this:
#!/usr/bin/env python
import cups
import sys
if __name__ == '__main__':
filename = sys.argv[1]
conn = cups.Connection()
printers = conn.getPrinters()
printer_name = printers.keys()[0]
conn.printFile(printer_name, filename, "TestDoc", {})
print 'File "%s" sent to printer "%s"' % (filename, printer_name)
It sends the given file to the default printer with the default paper (tray=1). But I want the doc to be printed on paper from another tray (tray=5). How to I do that?
OR: Am I doing it the wrong way? Should I go about it some other way to get my doc to the printer? In my final app the doc exist only in memory and not on disk, the only reason for me to write it to disk would be if it was needed by the printing. Note that I would need to switch between trays for the docs I print.

Related

How to integrate a Command Line Interface Window in Tkinter Application [duplicate]

I have a simple CLI based program that I would like to add a GUI to. Optimally I would like to retain the ability to have this script run via the CLI as well. If this can be done, what is the best way to approach this? Disclaimer: I am relatively new to Tkinter!
from argparse import ArgumentParser
from ipaddress import IPv4Network
def Main():
""" Main Program """
parser = ArgumentParser(
description='Provided a list of IP addresses, format and output the correct fortigate commands to create them')
parser.add_argument('VDOM', help='Specify a VDOM', type=str)
parser.add_argument(
'File', help='Specify a file. Each entry should be on its own line, and have no extra characters', typ=str)
args = parser.parse_args()
with open(args.File, 'r') as input_file:
array = input_file.read().splitlines()
with open(args.vdom + '.txt', 'w') as output_file:
output_file.write("config vdom\n")
output_file.write("edit %s\n" % str(args.vdom))
output_file.write("config firewall address\n\n")
for i in range(0, len(array)):
try:
ip_addr = IPv4Network(array[i])
generateip(ip_addr, output_file)
except ValueError:
url = array[i]
generateurl(url, output_file)
def generateip(ip_addr, output_file):
"""
Generate a single IP address object.
ip_addr -- IP address network object
output_file -- an output text file
"""
output_file.write("edit \"%s\"\n" % str(ip_addr.with_prefixlen))
output_file.write("set color 1\n")
output_file.write("set subnet %s %s\n" %
(str(ip_addr.network_address), str(ip_addr.netmask)))
output_file.write("next\n\n")
def generateurl(url, output_file):
"""
Generate a single URL address object.
url -- A valid URL string
output_file -- an output text file
"""
output_file.write("edit %s\n" % url)
output_file.write("set color 1\n")
output_file.write("set type fqdn\n")
output_file.write("set fqdn %s\n" % url)
output_file.write("next\n\n")
if __name__ == '__main__':
Main()
Check out https://github.com/chriskiehl/Gooey . This will automatically convert your ArgParser arguments to a GUI. The GUI will be dependent on the code, so the root of your program still depends on the CLI.

python script not working on cmd

This is a simple code, its function is extracting the metadata of certain images. However, it would work on any linux machine via the terminal fine as expected, whether printing the metadata into the terminal or saving the result into a text file.
When it comes to CMD on windows which is the primary OS for my PC wouldn't work, even the shell IDLE for Python it doesn't create the file or even print out the metadata.
codes explanation, takes two parameters, name of image, and out << means whether it's supposed to output the data into the terminal or into a specified text file.
I can't actually spot where the problem is!!
Please help me to identify the issue !! Thanks
Here's the code:
from time import sleep
import argparse
from PIL import Image
from PIL.ExifTags import TAGS
def getMetaData(imgname, out):
"""supported format of images are: .JPG, .TIF & .WAV"""
try:
metaData = {} # dictionary to hold the tags
imgFile = Image.open(imgname)
print "Getting meta data..."
info = imgFile._getexif()
if info:
print "Found Meta Data! "
for (tag, value) in info.items():
tagname = TAGS.get(tag, tag) # to human readable format
metaData[tagname] = value
if not out:
print tagname, value
if out:
print "outputting to file ..."
with open(out, "w") as f:
for (tagname, value) in metaData.items():
f.write(str(tagname)+'\t'+str(value)+"\n")
except:
print "Failed!!"
def Main():
print "Coded By: GentleMan"
print "Created 2/24/2017"
print "Use it carefully baby"
print "Special for x6x.net"
sleep(3)
parser = argparse.ArgumentParser()
parser.add_argument("img", help="Name of an image File")
parser.add_argument("--output","-o", help="dump data out to File.")
args = parser.parse_args()
if args.img:
print getMetaData(args.img, args.output)
else:
print parser.usage
if __name__ == '__main__':
Main()

Python silent print PDF to specific printer

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

Python pass a file as argument

I've been working on a Python problem for sometime now. I'm trying to use the Echoprint API to sort my music out. So i'm writing some code that does that for me.
This is how the API works :
Takes in a song name as a command line arg.
Gives the appropriate result.
But i'm writing a script that has to perform this "internally". As in, the script should take the files and perform the lookup and output the results to the terminal. (basically - NO COMMAND LINE ARGUMENTS SUPPLIED )
So is there anyway as to pass files into a function ?
I know this sounds silly but it's a problem i'm not able to solve.
If i use os.walk(), etc it returns a str object to my lookup function as a parameter. I want the audio file to be passed as a parameter.
Here's the code which takes in the song as a command line arg :
import sys
import os
import pyechonest.config as config
import pyechonest.song as song
config.CODEGEN_BINARY_OVERRIDE = os.path.abspath("/Users/******/python/minger/echoprint-codegen-master/echoprint-codegen")
config.ECHO_NEST_API_KEY='*****'
def lookup(file):
# Note that song.identify reads just the first 30 seconds of the file
fp = song.util.codegen(file)
if len(fp) and "code" in fp[0]:
# The version parameter to song/identify indicates the use of echoprint
result = song.identify(query_obj=fp, version="4.11")
print "Got result:", result
print result[0]
if len(result):
print "Artist: %s (%s)" % (result[0].artist_name, result[0].artist_id)
print "Song: %s (%s)" % (result[0].title, result[0].id)
else:
print "No match. This track may not be in the database yet."
else:
print "Couldn't decode", file
if __name__ == "__main__":
if len(sys.argv) < 2:
print >>sys.stderr, "Usage: %s <audio file>" % sys.argv[0]
sys.exit(1)
lookup(sys.argv[1])
From there, http://echonest.github.io/remix/apidocs/pyechonest.util-module.html#codegen
the method you use has signature
codegen(filename, start=0, duration=30)
so that it is the filename that has to be passed as an argument... not the file itself...
Ex use here http://nullege.com/codes/show/src#p#y#pyechonest-7.1.0#pyechonest#song.py/371/util.codegen
if filename:
if os.path.exists(filename):
query_obj = util.codegen(filename, start=codegen_start, duration=codegen_duration)
if query_obj is None:
raise Exception("The filename specified: %s could not be decoded." % filename)

tail multiple logfiles in python

This is probably a bit of a silly excercise for me, but it raises a bunch of interesting questions. I have a directory of logfiles from my chat client, and I want to be notified using notify-osd every time one of them changes.
The script that I wrote basically uses os.popen to run the linux tail command on every one of the files to get the last line, and then check each line against a dictionary of what the lines were the last time it ran. If the line changed, it used pynotify to send me a notification.
This script actually worked perfectly, except for the fact that it used a huge amount of cpu (probably because it was running tail about 16 times every time the loop ran, on files that were mounted over sshfs.)
It seems like something like this would be a great solution, but I don't see how to implement that for more than one file.
Here is the script that I wrote. Pardon my lack of comments and poor style.
Edit: To clarify, this is all linux on a desktop.
Not even looking at your source code, there are two ways you could easily do this more efficiently and handle multiple files.
Don't bother running tail unless you have to. Simply os.stat all of the files and record the last modified time. If the last modified time is different, then raise a notification.
Use pyinotify to call out to Linux's inotify facility; this will have the kernel do option 1 for you and call back to you when any files in your directory change. Then translate the callback into your osd notification.
Now, there might be some trickiness depending on how many notifications you want when there are multiple messages and whether you care about missing a notification for a message.
An approach that preserves the use of tail would be to instead use tail -f. Open all of the files with tail -f and then use the select module to have the OS tell you when there's additional input on one of the file descriptors open for tail -f. Your main loop would call select and then iterate over each of the readable descriptors to generate notifications. (You could probably do this without using tail and just calling readline() when it's readable.)
Other areas of improvement in your script:
Use os.listdir and native Python filtering (say, using list comprehensions) instead of a popen with a bunch of grep filters.
Update the list of buffers to scan periodically instead of only doing it at program boot.
Use subprocess.popen instead of os.popen.
If you're already using the pyinotify module, it's easy to do this in pure Python (i.e. no need to spawn a separate process to tail each file).
Here is an example that is event-driven by inotify, and should use very little cpu. When IN_MODIFY occurs for a given path we read all available data from the file handle and output any complete lines found, buffering the incomplete line until more data is available:
import os
import select
import sys
import pynotify
import pyinotify
class Watcher(pyinotify.ProcessEvent):
def __init__(self, paths):
self._manager = pyinotify.WatchManager()
self._notify = pyinotify.Notifier(self._manager, self)
self._paths = {}
for path in paths:
self._manager.add_watch(path, pyinotify.IN_MODIFY)
fh = open(path, 'rb')
fh.seek(0, os.SEEK_END)
self._paths[os.path.realpath(path)] = [fh, '']
def run(self):
while True:
self._notify.process_events()
if self._notify.check_events():
self._notify.read_events()
def process_default(self, evt):
path = evt.pathname
fh, buf = self._paths[path]
data = fh.read()
lines = data.split('\n')
# output previous incomplete line.
if buf:
lines[0] = buf + lines[0]
# only output the last line if it was complete.
if lines[-1]:
buf = lines[-1]
lines.pop()
# display a notification
notice = pynotify.Notification('%s changed' % path, '\n'.join(lines))
notice.show()
# and output to stdout
for line in lines:
sys.stdout.write(path + ': ' + line + '\n')
sys.stdout.flush()
self._paths[path][1] = buf
pynotify.init('watcher')
paths = sys.argv[1:]
Watcher(paths).run()
Usage:
% python watcher.py [path1 path2 ... pathN]
Simple pure python solution (not the best, but doesn't fork, spits out 4 empty lines after idle period and marks everytime the source of the chunk, if changed):
#!/usr/bin/env python
from __future__ import with_statement
'''
Implement multi-file tail
'''
import os
import sys
import time
def print_file_from(filename, pos):
with open(filename, 'rb') as fh:
fh.seek(pos)
while True:
chunk = fh.read(8192)
if not chunk:
break
sys.stdout.write(chunk)
def _fstat(filename):
st_results = os.stat(filename)
return (st_results[6], st_results[8])
def _print_if_needed(filename, last_stats, no_fn, last_fn):
changed = False
#Find the size of the file and move to the end
tup = _fstat(filename)
# print tup
if last_stats[filename] != tup:
changed = True
if not no_fn and last_fn != filename:
print '\n<%s>' % filename
print_file_from(filename, last_stats[filename][0])
last_stats[filename] = tup
return changed
def multi_tail(filenames, stdout=sys.stdout, interval=1, idle=10, no_fn=False):
S = lambda (st_size, st_mtime): (max(0, st_size - 124), st_mtime)
last_stats = dict((fn, S(_fstat(fn))) for fn in filenames)
last_fn = None
last_print = 0
while 1:
# print last_stats
changed = False
for filename in filenames:
if _print_if_needed(filename, last_stats, no_fn, last_fn):
changed = True
last_fn = filename
if changed:
if idle > 0:
last_print = time.time()
else:
if idle > 0 and last_print is not None:
if time.time() - last_print >= idle:
last_print = None
print '\n' * 4
time.sleep(interval)
if '__main__' == __name__:
from optparse import OptionParser
op = OptionParser()
op.add_option('-F', '--no-fn', help="don't print filename when changes",
default=False, action='store_true')
op.add_option('-i', '--idle', help='idle time, in seconds (0 turns off)',
type='int', default=10)
op.add_option('--interval', help='check interval, in seconds', type='int',
default=1)
opts, args = op.parse_args()
try:
multi_tail(args, interval=opts.interval, idle=opts.idle,
no_fn=opts.no_fn)
except KeyboardInterrupt:
pass

Categories

Resources