Quoted string as optional argument to Python script - python

I have simple utility script that I use to download files given a URL. It's basically just a wrapper around the Linux binary "aria2c".
Here is the script named getFile:
#!/usr/bin/python
#
# SCRIPT NAME: getFile
# PURPOSE: Download a file using up to 20 concurrent connections.
#
import os
import sys
import re
import subprocess
try:
fileToGet = sys.argv[1]
if os.path.exists(fileToGet) and not os.path.exists(fileToGet+'.aria2'):
print 'Skipping already-retrieved file: ' + fileToGet
else:
print 'Downloading file: ' + fileToGet
subprocess.Popen(["aria2c-1.8.0", "-s", "20", str(fileToGet), "--check-certificate=false"]).wait() # SSL
except IndexError:
print 'You must enter a URI.'
So, for example, this command would download a file:
$ getFile http://upload.wikimedia.org/wikipedia/commons/8/8e/Self-portrait_with_Felt_Hat_by_Vincent_van_Gogh.jpg
What I want to do is permit an optional second argument (after the URI) that is a quoted string. This string will be the new filename of the downloaded file. So, after the download finishes, the file is renamed according to the second argument. Using the example above, I would like to be able to enter:
$ getFile http://upload.wikimedia.org/wikipedia/commons/8/8e/Self-portrait_with_Felt_Hat_by_Vincent_van_Gogh.jpg "van-Gogh-painting.jpg"
But I don't know how to take a quoted string as an optional argument. How can I do this?

Just test the length of sys.argv; if it is more than 2 you have an extra argument:
if len(sys.argv) > 2:
filename = sys.argv[2]

The shell will pass it as a second argument (normally) if you provide spaces between them.
For example, here is test.py:
import sys
for i in sys.argv:
print(i)
And here is the result:
$ python test.py url "folder_name"
test.py
url
folder_name
The quotes doesn't matter at all, as it's handled in the shell, not python. To get it, just take sys.argv[2].
Hope this helps!

Related

How to print the first N lines of a file in python with N as argument

How would I go about getting the first N lines of a text file in python? With N have to give as argument
usage:
python file.py datafile -N 10
My code
import sys
from itertools import islice
args = sys.argv
print (args)
if args[1] == '-h':
print ("-N for printing the number of lines: python file.py datafile -N 10")
if args[-2] == '-N':
datafile = args[1]
number = int(args[-1])
with open(datafile) as myfile:
head = list(islice(myfile, number))
head = [item.strip() for item in head]
print (head)
print ('\n'.join(head))
I wrote the program, can let me know better than this code
Assuming that the print_head logic you've implemented need not be altered, here's the script I think you're looking for:
import sys
from itertools import islice
def print_head(file, n):
if not file or not n:
return
with open(file) as myfile:
head = [item.strip() for item in islice(myfile, n)]
print(head)
def parse_args():
result = {'script': sys.argv[0]}
args = iter(sys.argv)
for arg in args:
if arg == '-F':
result['filename'] = next(args)
if arg == '-N':
result['num_lines'] = int(next(args))
return result
if __name__ == '__main__':
script_args = parse_args()
print_head(script_args.get('filename', ''), script_args.get('num_lines', 0))
Running the script
python file.py -F datafile -N 10
Note: The best way to implement it would be to use argparse library
You can access argument passed to the script through sys
sys.argv
The list of command line arguments passed to a Python script. argv[0] is the script name (it is operating system dependent whether this is a full pathname or not). If the command was executed using the -c command line option to the interpreter, argv[0] is set to the string '-c'. If no script name was passed to the Python interpreter, argv[0] is the empty string.
So in code it would look like this:
import sys
print("All of argv")
print(sys.argv)
print("Last element every time")
print(sys.argv[-1])
Reading the documentation you'll see that the first values stored in the sys.argv vary according to how the user calls the script. If you print the code I pasted with different types of calls you can see for yourself the kind of values stored.
For a basic first approach: access n through sys.argv[-1] which returns the last element every time, assuming. You still have to do a try and beg for forgiveness to make sure the argument passed is a number. For that you would have:
import sys
try:
n = int(sys.argv[-1])
except ValueError as v_e:
print(f"Please pass a valid number as argument, not ${sys.argv[-1]}")
That's pretty much it. Obviously, it's quite basic, you can improve this even more by having the users pass values with flags, like --skip-lines 10 and that would be your n, and it could be in any place when executing the script. I'd create a function in charge of translating sys.argv into a key,value dictionary for easy access within the script.
Arguments are available via the sys package.
Example 1: ./file.py datafile 10
#!/usr/bin/env python3
import sys
myfile = sys.argv[1]
N = int(sys.argv[2])
with open("datafile") as myfile:
head = myfile.readlines()[0:args.N]
print(head)
Example 2: ./file.py datafile --N 10
If you want to pass multiple optional arguments you should have a look at the argparse package.
#!/usr/bin/env python3
import argparse
parser = argparse.ArgumentParser(description='Read head of file.')
parser.add_argument('file', help='Textfile to read')
parser.add_argument('--N', type=int, default=10, help='Number of lines to read')
args = parser.parse_args()
with open(args.file) as myfile:
head = myfile.readlines()[0:args.N]
print(head)

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.

Passing a string as an argument to a python script

I want to pass a string of ZPL codes from one python script to another python script. The string becomes malformed when used in the second script. How can I pass a string literal as an argument to another python script without it being malformed?
Original String
^XA^FO20,20^BQ,2,3^FDQA,001D4B02107A;1001000;49681207^FS^FO50,50^ADN,36,20^FDMAC: 001D4B02107A^FS^FO50,150^ADN,36,20^FDSN: 1001000^FS^FO50,250^ADN,36,20^FDCode: 49681207^FS^XZ
Malformed string
XAFO20,20BQ,2,3FDQA,001D4B02107A;1001000;49681207FSFO50,50ADN,36,20FDMAC:
Code where I call the second script
def printLabel():
label = "^XA"+"^FO20,20^BQ,2,3^FDQA,"+"001D4B02107A;1001000;49681207"+"^FS"+"^FO50,50"+"^ADN,36,20"+"^FD"+"MAC: "+"001D4B02107A"+"^FS"+"^FO50,150"+"^ADN,36,20"+"^FD"+"SN: "+"1001000"+"^FS"+"^FO50,250"+"^ADN,36,20"+"^FD" + "Code: "+"49681207"+"^FS"+"^XZ"
command = "zt320print.py "+label
print command
sys.stdout.flush()
exitCode = os.system(str(command))
Code that receives the argument
if __name__ == "__main__":
zplString = str(sys.argv[1])
print zplString
printZPL(zplString)
If your code needs to be written just as it is (including the rather odd way of stringing together the ZPL code, and calling a separate script via a shell intermediary, and the avoidance of subprocess, for that matter), you can resolve your issue with a few small adjustments:
First, wrap your code string in double-quotes.
label= '"^XA'+"^FO20,20^BQ,2,3^FDQA,"+"001D4B02107A;1001000;49681207"+"^FS"+"^FO50,50"+"^ADN,36,20"+"^FD"+"MAC: "+"001D4B02107A"+"^FS"+"^FO50,150"+"^ADN,36,20"+"^FD"+"SN: "+"1001000"+"^FS"+"^FO50,250"+"^ADN,36,20"+"^FD" + "Code: "+"49681207"+"^FS"+'^XZ"'
Second, make sure you're actually calling python from the shell:
command = "python script2.py "+label
Finally, if you're concerned about special characters not being read in correctly from the command line, use unicode_escape from codecs.decode to ensure correct transmission.
See this answer for more on unicode_escape.
# contents of second script
if __name__ == "__main__":
from codecs import decode
import sys
zplString = decode(sys.argv[1], 'unicode_escape')
print(zplString)
Now the call from your first script will transmit the code correctly:
import sys
import os
sys.stdout.flush()
exitCode = os.system(str(command))
Output:
^XA^FO20,20^BQ,2,3^FDQA,001D4B02107A;1001000;49681207^FS^FO50,50^ADN,36,20^FDMAC: 001D4B02107A^FS^FO50,150^ADN,36,20^FDSN: 1001000^FS^FO50,250^ADN,36,20^FDCode: 49681207^FS^XZ
Some demo code:
import sys
if __name__ == "__main__":
for i, arg in enumerate(sys.argv):
print("{}: '{}'".format(i, arg))
when called like
python test.py ^this^is^a^test
it gives
0: 'test.py'
1: 'thisisatest'
when called like
python test.py "^this^is^a^test"
it gives
0: 'test.py'
1: '^this^is^a^test'
Solution: enclose your parameter string in double-quotes, ie
label = '"' + label + '"'
You can put your string inside a double-quotes, or just import the other python script:
a.py
import sys, os
text = "a b c d"
# or '{} {} "{}"'.format("python", "b.py", text)
command = "python b.py \"" + text + "\""
os.system(str(command))
b.py
import sys
if __name__ == "__main__":
first_argument = str(sys.argv[1])
print(first_argument)
Output
a b c d

How do I add sys.argv to a function opening a text file in Python

I need to use sys.argv to check for an argument from the command line, which would be the filename in my case. My code is as follows. I'm not allowed to import argparse, only allowed to use sys. I know I'm doing something wrong here. Appreciate any help.
def get_inputfile_object( ):
'''
Check the command line for an argument. If one was there, use it as the
filename. Otherwise, use DEFAULT_INPUT_FILENAME. Open the file.
If file is successfully opened:
print MSG_OPENING_FILE
Return: a file object for that file
If the file cannot be opened:
print MSG_ERROR_OPENNING_FILE
Return: True
'''
if sys.argv > 1:
pass
else:
input_filename = DEFAULT_INPUT_FILENAME
input_filename = DEFAULT_INPUT_FILENAME
if os.path.isfile(input_filename) and os.access(input_filename,os.R_OK):
#Prints the opening file message, and the name of the file
print (MSG_OPENING_FILE,input_filename)
return open(input_filename,'r')
else:
print (MSG_ERROR_OPENING_FILE)
return True
sys.argv is a list of arguments.
You need to check the length of the list:
if len(sys.argv) > 1:
You should check out argparse.
The argparse module also automatically generates help and usage
messages and issues errors when users give the program invalid
arguments.
Haven't tested it, but you can try something similar to this:
import argparse
# setup the parser
parser = argparse.ArgumentParser(description='Describe script')
# add positional argument
parser.add_argument('filename', type=str, help='filename description')
# parse the args
args = parser.parse_args()
print(args.filename)

Calling a perl file with arguments from for loop

My title might be misleading, as it's just a part of my question.
My current code is as shown below, it is not completed as i am not good with for loop
import os
import subprocess
listfile = []
path = "C:\\Users\\A\\Desktop\\Neuer Ordner (3)"
for f in os.listdir(path):
if f.endswith(".par"):
listfile.append(f)
print listfile
it's output is as shown below:
C:\app\Tools\exam\Python25>python nwe.py
['abc.par', 'abc2.par', 'abc3.par', 'abc4.par', 'abc5.par', 'abc6.par', 'abc7.pa
r', 'abc8.par', 'abc9.par']
Now i have to call a perl file, say "Newperl.pl" with arguments
arg1 as abc.par and arg2 as abc.svl
arg1 as abc2.par and arg2 as abc2.svl
arg1 as abc3.par and arg2 as abc3.svl
It has to take all the files which ends with ".par" extension and pass the the filename as argument while adding ".par" to the 1st argument and ".svl" to the 2nd argument.
i can use the one below for for loop.
for i in range(0,len(listfile), 1)
newcode = subprocess.call()
2nd line calls the perl file, but i am not sure as in how to split the ".par" and add ".par" and ".svl" again and pass each one at a time.
any logic or help would be appriciated
You could write your subprocess call as below:
import os
for l in listfile:
newcode = subprocess.call("perl newperl.pl "+l+" "+os.path.splitext(l)[0]+'.svl')
os.path.splittext remove the extension for par files and adds svl extension.

Categories