Using argparse multiple positional arguments and make one of them "optional" to include in input - arguments

Idea: I'm trying to write a program that after using command line arguments it will send the arguments to database. There are 4 arguments - username, email, address and version. All arguments except username should be mandatory.
What have I complished so far:
My code:
parser = argparse.ArgumentParser()
parser.add_argument("-u", "--username", nargs = '?' , help="Insert your username", action="store_true")
parser.add_argument("uName", help="Insert your username", type=str)
parser.add_argument("-e", "--email", help="Insert your email", action="store_true")
parser.add_argument("eMail", help="Insert your email", type=str)
parser.add_argument("-a", "--address", help="Insert your address", action="store_true")
parser.add_argument("address", help="Insert your address", type=str)
parser.add_argument("-v", "--version", help="Insert your app version", action="store_true")
parser.add_argument("ver", help="Insert your app version", type=str)
args = parser.parse_args()
So if I write cmd using all the arguments:
$python test.py -u tom -e tom#company.com -a 45xx -v 1.1
It gives me correct output. No problem.
Now, if I write cmd leaving one argument out:
$python test.py -u tom -e tom#company.com -a 45xx
It gives me error:
usage: test.py [-h] [-u] [-e] [-b] [-a]
uName eMail aadress ver
test.py: error: the folowing arguments are required: ver
The way I understand it is that in my code all of them are positional arguments that are required to appear in the input and in my case all the "optional" arguments need a positional argument to save the input. Is it possible to make username argument that is not required in the input because the way I understand it in this case it is not and if thats true, can you suggest any alternative approach? Im thinking maybe there is a way to make username to give "Null" if its not in the input but I can't find a way to do it.

Related

Testing if a docopt command-line option is set in nim

I'm trying to write a nim program that can read either from the standard input or from a file given as a command-line option. I use docopt to parse the command line.
import docopt
const doc = """
This program takes input from a file or from stdin.
Usage:
testinput [-i <filename> | --input <filename>]
-h --help Show this help message and exit.
-i --input <filename> File to use as input.
"""
when isMainModule:
let args = docopt(doc)
var inFilename: string
for opt, val in args.pairs():
case opt
of "-i", "--input":
inFilename = $args[opt]
else:
echo "Unknown option" & opt
quit(QuitFailure)
let inputSource =
if inFilename.isNil:
stdin
else:
echo "We have inFilename: " & inFilename
open(inFilename)
The program compiles.
It doesn't crash when I give it a file on the command line:
$ ./testinput -i testinput.nim
We have inFilename: testinput.nim
But I get an IOError if I try to feed it from its stdin:
$ ./testinput < testinput.nim
We have inFilename: nil
testinput.nim(28) testinput
system.nim(2833) sysFatal
Error: unhandled exception: cannot open: nil [IOError]
How come inFilename.isNil is false, and yet the execution of the else branch tells me that inFilename "is" nil?
Is there a correct and elegant way to do this, using docopt?
I'm not familiar with docopt, but it seems to create an entry for each option in the doc, not for the options specified by user so your code's been getting args == {"--input": nil} and stringifying the nil.
The following will work correctly:
import docopt
const doc = """
This program takes input from a file or from stdin.
Usage:
testinput [-i <filename> | --input <filename>]
-h --help Show this help message and exit.
-i --input <filename> File to use as input.
"""
when isMainModule:
let args = docopt(doc)
var inFilename: string
if args["--input"]:
inFilename = $args["--input"]
if not inFilename.isNil:
echo "We have inFilename: " & inFilename
let inputSource =
if inFilename.isNil:
stdin
else:
open(inFilename)
Also note that you don't have to check for "-i" option as docopt knows it's an alias to "--input".
Instead of transforming the value of the option into a string with $, one can keep it as a Value, which is the type returned by docopt.
According to the documentation:
vkNone (No Value)
This kind of Value appears when there is an option which hasn't been set and has no default. It is false when converted toBool
One can apparently use the value of the option in a boolean expression, and it seems to be automatically interpreted as a bool:
import docopt
const doc = """
This program takes input from a file or from stdin.
Usage:
testinput [-i <filename> | --input <filename>]
-h --help Show this help message and exit.
-i --input <filename> File to use as input.
"""
when isMainModule:
let args = docopt(doc)
var inFilename: Value
for opt, val in args.pairs():
case opt
of "-i", "--input":
inFilename = val
else:
echo "Unknown option" & opt
quit(QuitFailure)
let inputSource =
if not bool(inFilename):
stdin
else:
echo "We have inFilename: " & $inFilename
open($inFilename)
Another usage of this behaviour is given in this other anwser, and avoids setting the variable, therefore keeping it nil.

Jacl script behaves wrongly in case of special characters \ ! ; $

Hi I have written one jacl script which is not working properly in case of ; $ ! \
test.jacl
set password [ lindex $argv 1 ]
puts "Hello your entered password is $password"
Script OUTPUT
When I run
../test.jacl testUser Foo\b4r*
it returns
Hello your entered password is Foob4r*
CASE 2:
../test.jacl testUser Foo$b4r*
OUTPUT
Hello your entered password is Foo*
CASE 3:
../test.jacl testUser Foo;b4r*
OUTPUT
Hello your entered password is Foo
CASE 3:
../test.jacl testUser Foo!b4r*
OUTPUT
-bash: !b4r*: event not found
Kindly suggest best way to overcome this problem

Changing an AIX password via script?

I am trying to change a password of a user via script. I cannot use sudo as there is a feature that requires the user to change the password again if another user changes their password.
AIX is running on the system.
unfortunately, chpasswd is unavailable.
I have expected installed, but I am having trouble with that also.
here is what I thought would work
echo "oldpassword\nnewpasswd123\nnewpasswd123" | passwd user
However once run the script I am prompted with please enter user's old password
shouldn't they all be echoed in?
I am a beginner with shell scripting and this has been baffled.
You can try:
echo "USERNAME:NEWPASSWORD" | chpasswd
Use GNU passwd stdin flag.
From the man page:
--stdin
This option is used to indicate that passwd should read the new password from standard input, which can be a pipe.
NOTE: Only for root user.
Example
$ adduser foo
$ echo "NewPass" |passwd foo --stdin
Changing password for user foo.
passwd: all authentication tokens updated successfully.
Alternatively you can use expect, this simple code will do the trick:
#!/usr/bin/expect
spawn passwd foo
expect "password:"
send "Xcv15kl\r"
expect "Retype new password:"
send "Xcv15kl\r"
interact
Results
$ ./passwd.xp
spawn passwd foo
Changing password for user foo.
New password:
Retype new password:
passwd: all authentication tokens updated successfully.
In addition to the other suggestions, you can also achieve this using a HEREDOC.
In your immediate case, this might look like:
$ /usr/bin/passwd root <<EOF
test
test
EOF
You need echo -e for the newline characters to take affect
you wrote
echo "oldpassword\nnewpasswd123\nnewpasswd123" | passwd user
you should try
echo -e "oldpassword\nnewpasswd123\nnewpasswd123" | passwd user
more than likely, you will not need the oldpassword\n portion of that command, you should just need the two new passwords. Don't forget to use single quotes around exclamation points!
echo -e "new"'!'"passwd123\nnew"'!'"passwd123" | passwd user
You can try :
echo -e "newpasswd123\nnnewpasswd123" | passwd user
Just this
passwd <<EOF
oldpassword
newpassword
newpassword
EOF
Actual output from ubuntu machine (sorry no AIX available to me):
user#host:~$ passwd <<EOF
oldpassword
newpassword
newpassword
EOF
Changing password for user.
(current) UNIX password: Enter new UNIX password: Retype new UNIX password:
passwd: password updated successfully
user#host:~$
This is from : Script to change password on linux servers over ssh
The script below will need to be saved as a file (eg ./passwdWrapper) and made executable (chmod u+x ./passwdWrapper)
#!/usr/bin/expect -f
#wrapper to make passwd(1) be non-interactive
#username is passed as 1st arg, passwd as 2nd
set username [lindex $argv 0]
set password [lindex $argv 1]
set serverid [lindex $argv 2]
set newpassword [lindex $argv 3]
spawn ssh $serverid passwd
expect "assword:"
send "$password\r"
expect "UNIX password:"
send "$password\r"
expect "password:"
send "$newpassword\r"
expect "password:"
send "$newpassword\r"
expect eof
Then you can run ./passwdWrapper $user $password $server $newpassword which will actually change the password.
Note: This requires that you install expect on the machine from which you will be running the command. (sudo apt-get install expect) The script works on CentOS 5/6 and Ubuntu 14.04, but if the prompts in passwd change, you may have to tweak the expect lines.
Here is the script...
#!/bin/bash
echo "Please enter username:"
read username
echo "Please enter the new password:"
read -s password1
echo "Please repeat the new password:"
read -s password2
# Check both passwords match
if [ $password1 != $password2 ]; then
echo "Passwords do not match"
exit
fi
# Does User exist?
id $username &> /dev/null
if [ $? -eq 0 ]; then
echo "$username exists... changing password."
else
echo "$username does not exist - Password could not be updated for $username"; exit
fi
# Change password
echo -e "$password1\n$password1" | passwd $username
Refer the link below as well...
http://www.putorius.net/2013/04/bash-script-to-change-users-password.html
You can try
LINUX
echo password | passwd username --stdin
UNIX
echo username:password | chpasswd -c
If you dont use "-c" argument, you need to change password next time.
If you can use ansible, and set the sudo rights in it, then you can easily use this script. If you're wanting to script something like this, it means you need to do it on more than one system. Therefore, you should try to automate that as well.
For me this worked in a vagrant VM:
sudo /usr/bin/passwd root <<EOF
12345678
12345678
EOF
printf "oldpassword/nnewpassword/nnewpassword" | passwd user
#!/usr/bin/python
import random
import string
import smtplib
import sys
import os
from subprocess import call
import socket
user = sys.argv[1]
receivers = ["%s#domain.com" %user]
'''This will generate a 30 character random password'''
def genrandpwd():
return ''.join(random.SystemRandom().choice(string.ascii_lowercase + string.digits + string.ascii_uppercase + string.punctuation) for _ in range(30))
def change_passwd(user, password):
p = os.popen("/usr/bin/passwd %s" %user, "w")
p.write(password)
p.write("\n")
p.write(password)
p.close()
def chage(user):
agepasswd = call(["/usr/bin/chage", "-d", "0", "%s" %user])
def mailpwd(user, password):
sender = "admin#%s" %socket.gethostname()
subj = "!!!IMPORTANT!!!, Unix password changed for user %s" %user
text = "The password for the %s user has changed, the new password is:\n\n %s \n\n Note: The system will force to change the password upon initial login. Please use the password provided in the mail as your current password and type the password of your choice as the New password" %(user, password)
message = message = 'Subject: %s\n\n%s' % (subj, text)
smtpObj = smtplib.SMTP('mailrelay-server.domain.com')
smtpObj.sendmail(sender, receivers, message)
smtpObj.quit()
def main():
newpwd = genrandpwd()
change_passwd(user, newpwd)
chage(user)
mailpwd(user, newpwd)
if __name__ == "__main__":
main()

Ruby optparse odd behavior with exception handling

I was working on making a CLI ruby tool a bit more robust with error handling of the CLI component of the tool. I'm using optparse and the documentation shows that flags can have mandatory and optional arguments. I'm just seeing some odd/annoying behavior.
For one case where the argument of the flag is mandatory (using the equal sign), I was trying to make it fail but it still worked, it just grabbed the next flag ('-u') as the argument to that flag instead which causes the rest of the parsing to barf in a less than ideal way. I guess this is kind of OK since '-....' can be valid but since I have it using the equal sign for setting the value of the switch, I'd assume that the 'space' style assignment wouldn't work.
op = OptionParser.new do |x|
x.on("-u", "--user=USER", "user flag") do |user| options[:user] = user end
x.on("-d", "--db=DATABASE", "database flag") do |db| options[:db] = db end
end
If I pass the following CLI input:
myprog -u -d mydb positionalarg
Then during parsing, it sets options[:user] to be -d, and options[:db] is nil since it is not encountered and there are 2 positional arguments instead of 1. Obviously this is a user error but I want to catch it and display a real error and not just return an error (in the case of the tool) that only one positional argument should be passed from the following list: ... The true issue is that the -u flag is missing it's required argument so given that optparse has a ArgumentMissing exception, I'd assume that's what .parse! would throw.
However, with a flag that has an optional argument (using the equal sign), it is in fact optional if you do: -a -b=something but this does not work: -a something -b=somethingelse. The only way to force the value is to use the equal sign: -a=something -b=somethingelse. Given the previous behavior with the mandatory argument with an equal sign, I'd wouldn't think would be the case. Example:
op = OptionParser.new do |x|
x.on("-u", "--user[=USER]", "user flag") do |user| options[:user] = user unless user.nil? end
x.on("-d", "--db=DATABASE", "database flag") do |db| options[:db] = db end
end
So with parsing:
myprog -u -d mydb positionalarg
Then options[:user] is nil (ok) and options[:db] is mydb and there is one positional arg left over.
With this parsing:
myprog -u myuser -d mydb positionalarg
Then options[:user] is nil (not ok) and options[:db] is mydb and there are two positional args left over: myuser and positionalarg (not ok). My error checking, again, barfs with the positional arg count. It seems that if with mandatory flag arguments, that if both space and = works, then for optional flag args, it should be the same but this is not the case.
The other issue is that the flags with optional args (using space) are fine except if they are at the end of the command and then takes the positional argument as the flags argument.
Example:
op = OptionParser.new do |x|
x.on("-u", "--user [USER]", "user flag") do |user| options[:user] = user unless user.nil? end
x.on("-d", "--db=DATABASE", "database flag") do |db| options[:db] = db end
end
So with parsing:
myprog -d mydb -u positionalarg
Then options[:db] is mydb (ok) and options[:user] is positionalarg and there are no positional arguments left over. Note that -d mydb works with the space even those I specify it with an equals sign.
Seems a lot of people do ruby CLIs by doing optparse .parse! and take the remaining entries in ARGV as the positional arguments, but I'm thinking it might be better to strip the positional arguments off the far end first before passing ARGV to optparse (except this fails in the event of variable number of positional arguments.
I'm confident I can program around all this but I'd prefer not to if there are ways to do it in optparse that I'm unaware of.
Maybe the best thing would be to avoid flags with optional arguments :), but any advice would be appreciated.
Why do you use the = in the definition of the options? The example in the documentation don't use them.
If I define this MWE as test.rb:
require 'optparse'
puts "\n=Call with #{ARGV.inspect}"
options = {}
op = OptionParser.new do |x|
x.on("-u", "--user [USER]", "user flag") do |user| options[:user] = user unless user.nil? end
x.on("-d", "--db DATABASE", "database flag") do |db| options[:db] = db end
end
op.parse!
puts "Options: #{options.inspect}"
puts "ARGV #{ARGV.inspect}"
And call it with this batfile (Windows, remove the #echo off for Linux):
#echo off
test.rb -h
test.rb -u -d mydb positionalarg
test.rb -u myuser -d mydb positionalarg
test.rb --user=myuser -d mydb positionalarg
I get:
=Call with ["-h"]
Usage: test [options]
-u, --user [USER] user flag
-d, --db DATABASE database flag
=Call with ["-u", "-d", "mydb", "positionalarg"]
Options: {:db=>"mydb"}
ARGV ["positionalarg"]
=Call with ["-u", "myuser", "-d", "mydb", "positionalarg"]
Options: {:user=>"myuser", :db=>"mydb"}
ARGV ["positionalarg"]
=Call with ["--user=myuser", "-d", "mydb", "positionalarg"]
Options: {:user=>"myuser", :db=>"mydb"}
ARGV ["positionalarg"]

OptionParser's make_switch error with '-?'

I'm running into an issue with OptionParser's make_switch.
My code parses three arguments and runs a test to see if my MANDATORY argument is here:
#!/usr/bin/env ruby
require 'optparse'
require 'ostruct'
options = OpenStruct.new
#argv = ARGV
optparse = OptionParser.new do |opts|
#opts=opts
usage = "USAGE: ./#{File.basename($0)} [-v] -p xxxxxx"
#opts.banner = usage
#opts.on( '-p', '--pdu [PDU]', 'Specify a PDU to configure') do |res|
options.pdu = true
$pdu_name = res
end
#opts.on( '-v', '--[no-]verbose', 'Run verbosely') do
options.verbose = true
end
#opts.on( '-?', '-help','Show this message') do
puts "Help Me!"
puts #opts
exit 1
end
end
begin
if not #argv.empty?
optparse.order!(#argv)
if !options.pdu
$stderr.puts "Options -p missing."
$stderr.puts "#{#opts}\n\n"
exit 1
end
else
$stderr.puts "ERROR: Arguments Required."
$stderr.puts "#{#opts}\n\n"
exit 1
end
rescue OptionParser::InvalidOption
$stderr.puts "ERROR: Invalid option."
$stderr.puts "#{#opts}\n\n"
exit 1
end
Everything works except -?:
xxx$ ./myscript.rb -?
/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/optparse.rb:451:in
`parse': missing argument: -? (OptionParser::MissingArgument)
from /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/optparse.rb:1295:in `parse_in_order'
from /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/optparse.rb:1254:in `catch'
from /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/optparse.rb:1254:in `parse_in_order'
from /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/optparse.rb:1248:in `order!'
blabla
However -help works perfectly:
xxxx$ ./myscript.rb -help
Help me!
USAGE: ./myscript.rb [-v] -p xxxxxx
-p, --pdu [PDU] Specify a PDU to configure
-v, --[no-]verbose Run verbosely
-?, -help Show this message
More surprisingly, -? -v works too:
xxxx$ ./myscript.rb -? -v
Help Me!
USAGE: ./myscript.rb [-v] -p xxxxxx
-p, --pdu [PDU] Specify a PDU to configure
-v, --[no-]verbose Run verbosely
-?, -help Show this message
What did I do wrong?
The same issue occurs if I replace -? with -h in the code.
Perhaps a quick look at the (somewhat confusing) documentation would shed some light on the situation. If you look at the docs, you'll end up at OptionParser#make_switch where you'll find an explanation of what the opt.on arguments look like:
Long style switch:
Specifies a long style switch which takes a mandatory, optional or no argument. It’s a string of the following form:
"--switch=MANDATORY" or "--switch MANDATORY"
"--switch[=OPTIONAL]"
"--switch"
Short style switch:
Specifies short style switch which takes a mandatory, optional or no argument. It’s a string of the following form:
"-xMANDATORY"
"-x[OPTIONAL]"
"-x"
Note the -xMANDATORY and then look closer at your #opts.on call:
#opts.on( '-?', '-help','Show this message') do
# ---------------^^^^^
That -help defines a -h option with a required elp argument. Presumably the option parser is interpreting that to mean that -h is an alias for -? and since -h is defined with a required argument, -? also requires an argument. If you use --help (i.e. a long style switch) then you'll probably have a better time:
#opts.on('-?', '--help', 'Show this message') do
I working from the Ruby 2.0 version but I doubt much has changed in the option parser since the older version of Ruby that you appear to be using.

Resources