I don't use Bash very frequently but I need to work on a bit of Bash that has to make a curl request to a web service with a query string tail built from command arguments contained in the script command arguments variable $#. So if the command argument string is something like -e something -v -s somethingelse, then the query string that must be produced is e=something&v=blank&s=somethingelse.
At the moment my test script looks like this:
#!/bin/bash
set -e
echo "${#}"
query_string=""
for arg in "${#}" ; do
if [[ "${arg}" =~ "-" ]] ; then
query_string+="${arg}="
else
if [ -z "${arg}" ] ; then
query_string+="blank&"
else
query_string+="${arg}&"
fi
fi
done
echo "${query_string}" | tr -d '-'
and produces the incorrect output
e=something&v=s=somethingelse&
I'm not sure where I'm going wrong. Any suggestions would be appreciated.
How about:
#!/bin/bash
set -e
echo "${#}"
option=true
query_string=""
for arg in "${#}" ; do
if $option ; then
query_string+="${arg}="
option=false
else
if [[ "${arg}" =~ "-" ]] ; then
query_string+="blank&${arg}="
else
query_string+="${arg}&"
option=true
fi
fi
done
echo "${query_string::-1}" | tr -d '-'
Every iteration you have to check the previous arg to see if it was a switch, not a value:
#!/bin/bash
set -e
echo "${#}"
prev_arg=""
query_string=""
for arg in "${#}" ; do
if [[ $arg == -* ]] ; then
# Current arg is a switch and the previous one was also a switch
# which means the value for it is blank
if [[ $prev_arg == -* ]] ; then
query_string+="blank&"
fi
query_string+="${arg}="
else
query_string+="${arg}&"
fi
prev_arg="$arg"
done
echo "${query_string::-1}" | tr -d '-'
This produces the following output:
something -v -s somethingelse
e=something&v=blank&s=somethingelse
Related
i have the following bash script contains multiple functions
#!/usr/bin/bash
#checks if the arguments is directory and extract the domain name from it
if [[ -d "$1" ]]; then
domain=$(echo "$1" | grep -iEo '[[:alnum:]-]+\.[a-z]+')
WORKING_DIR="$1"
else
domain="$1"
echo "it is domain name"
fi
example_fun1(){
ping -c 4 $domain
}
example_fun2(){
nslookup $domain
}
for x in "$#" ;do
example_fun1 $x
example_fun2 $x
done
and run as following
./script.sh ./pathtofolder/example.com/ ./pathtofolder/test.com/
Or
./script.sh example.com test.com
and working probably BUT i need to add more feature which is check if certain word based in arguments like fun1 it will execute function example_fun1 only
desired execution
./script.sh fun1 ./pathtofolder/example.com/ ./pathtofolder/test.com/
OR
./script.sh fun1 example.com test.com
Thanks
Try the following
#!/usr/bin/bash
function=""
if [[ $( echo $1 | grep fun1 ) ]]
then
function="example_fun1"
shift
elif [[ $( echo $1 | grep fun2 ) ]]
then
function="example_fun2"
shift
fi
#checks if the arguments is directory and extract the domain name from it
example_fun1(){
ping -c 4 $domain
}
example_fun2(){
nslookup $domain
}
if [[ "$function" != "" ]]
then
for input in "$#"; do
if [[ -d "$1" ]]; then
domain=$(echo "$1" | grep -iEo '[[:alnum:]-]+\.[a-z]+')
WORKING_DIR="$1"
else
domain="$1"
echo "it is domain name"
fi
"$function"
shift
done
else
for input in "$#"; do
if [[ -d "$1" ]]; then
domain=$(echo "$1" | grep -iEo '[[:alnum:]-]+\.[a-z]+')
WORKING_DIR="$1"
else
domain="$1"
echo "it is domain name"
fi
example_fun1
example_fun2
shift
done
fi
This way you can pass fun1 and execute only fun1
Or if you don't pass any of these for example both of them will be executed
Assign the first parameter to a variable, then use that when calling the function.
func="example_$1"
shift
for x in "$#"; do
"$func" "$x"
done
And your functions need to use their parameters, not a variable that's set in the main script:
example_fun1(){
ping -c 4 "$1"
}
example_fun2(){
nslookup "$1"
}
I'm writing a little script that takes several command line arguments and substitutes their values into some files.
I have a requirement where the user can either specify a file on their machine, or fetch it over http(s), but the problem is my script eats up the wget as a parameter, and doesn't actually execute it.
Here's what I'm using to parse the arguments:
while [[ "$#" -gt 0 ]] ; do
if [[ "$1" == '--ip-address' ]] ; then
shift
ip_address="$1"
fi
if [[ "$1" == '--hostname' ]] ; then
shift
hostname="$1"
fi
shift
done
What I'm looking for is something like
script.sh --file wget http://foo.bar/file.txt and it would first download the file and then pass it as a parameter.
What about something like:
if [[ "$1" == '--file' ]] ; then
shift
filename="$1"
if [ `echo $filename|grep '://'` != "" ]; then
wget --no-check-certificate -O /tmp/file "$filename"
filename=/tmp/file
fi
fi
I seem to have this problem. This code breaks at line 119 in my script with bash associative arrays. I am sorry for the comments but I am kind to new to bash scripting. This is the code:
#!/bin/bash
# Aliases file
# Command usage: cpRecent/mvRecent -d {dirFrom},{dirTo} -n {numberofFiles} -e {editTheNames}
# Error codes
NO_ARGS="You need to pass in an argument"
INVALID_OPTION="Invaild option:"
NO_DIRECTORY="No directory found"
# Return values
fullpath=
directories=
numfiles=
interactive=
typeset -a files
typeset -A filelist
# Advise that you use relative paths
__returnFullPath(){
local npath
if [[ -d $1 ]]; then
cd "$(dirname $1)"
npath="$PWD/$(basename $1)"
npath="$npath/" #Add a slash
npath="${npath%.*}" #Delete .
fi
fullpath=${npath:=""}
}
__usage(){
wall <<End-Of-Message
________________________________________________
<cpRecent/mvRecent> -d "<d1>,<d2>" -n <num> [-i]
-d First flag: Takes two arguments
-n Second flag: Takes one argument
-i Takes no arguments. Interactive mode
d1 Directory we are reading from
d2 Directory we are writing to
num Number of files
________________________________________________
End-Of-Message
}
__processOptions(){
while getopts ":d:n:i" opt; do
case $opt in
d ) IFS=',' read -r -a directories <<< "$OPTARG";;
n ) numfiles=$OPTARG;;
i ) interactive=1;;
\? ) echo "$INVALID_OPTION -$OPTARG" >&2 ; return 1;;
: ) echo "$NO_ARGS"; __usage; return 1;;
* ) __usage; return 1;;
esac
done
}
__getRecentFiles(){
# Check some conditions
(( ${#directories[#]} != 2 )) && echo "$INVALID_OPTION Number of directories must be 2" && return 2
#echo ${directories[0]} ${directories[1]}
# Get the full paths of the directories to be read from/written to
__returnFullPath "${directories[0]}"
directories[0]="$fullpath"
__returnFullPath "${directories[1]}"
directories[1]="$fullpath"
if [[ -z ${directories[0]} || -z ${directories[1]} ]]; then
echo $NO_DIRECTORY
return 3
fi
[[ numfiles != *[!0-9]* ]] && echo "$INVALID_OPTION Number of files cannot be a string" && return 4
#numfiles=$(($numfiles + 0))
(( $numfiles == 0 )) && echo "$INVALID_OPTION Number of files cannot be zero" && return 4
local num="-"$numfiles""
# Get the requested files in directory(skips directories)
if [[ -n "$(ls -t ${directories[0]} | head $num)" ]]; then
# For some reason using local -a or declare -a does not seem to split the string into two
local tempfiles=($(ls -t ${directories[0]} | head $num))
#IFS=' ' read -r -a tempfiles <<< "$string"
#echo ${tempfiles[#]}
for index in "${!tempfiles[#]}"; do
echo $index ${tempfiles[index]}
[[ -f "${directories[0]}${tempfiles[index]}" ]] && files+=("${tempfiles[index]}")
done
fi
}
####################################
# The problem is this piece of code
__processLines(){
local name
local answer
local dirFrom
local dirTo
if [[ -n $interactive ]]; then
for (( i=0; i< ${#files[#]}; i++ )); do
name=${files[i]}
read -n 1 -p "Old name: $name. Do you wish to change the name(y/n)?" answer
[[ answer="y" ]] && read -p "Enter new name:" name
dirFrom="${directories[0]}${files[i]}"
dirTo="${directories[1]}$name"
fileslist["$dirFrom"]="$dirTo"
done
else
for line in $files; do
dirFrom="${directories[0]}$line"
echo $dirFrom # => /home/reclusiarch/Documents/test
dirTo="${directories[1]}$line"
echo $dirTo # => /home/reclusiarch/test
fileslist["$dirFrom"]="$dirTo" # This is the offending line
done
fi
}
###########################################################
cpRecent(){
__processOptions $*
__getRecentFiles
__processLines
for line in "${!filelist[#]}"; do
cp $line ${filelist[$line]}
done
echo "You have copied ${#fileList[#]} files"
unset files
unset filelist
return
}
mvRecent(){
__processOptions $*
__getRecentFiles
__processLines
for line in "${!filelist[#]}"; do
mv $line ${filelist[$line]}
done
echo "You have copied ${#fileList[#]} files"
unset files
unset filelist
return
}
cpRecent "$*"
I have tried a lot of things. To run the script,
$ bash -x ./testing.sh -d "Documents,." -n 2
But nothing seems to work:
The error is this(when using bash -x):
./testing.sh: line 119: /home/reclusiarch/Documents/test: syntax error: operand expected (error token is "/home/reclusiarch/Documents/test")
If I run that section on the command line, it works:
$ typeset -A filelist
$ filelist["/home/reclusiarch/Documents/test"]=/home/reclusiarch/test
$ echo ${filelist["/home/reclusiarch/Documents/test"]}
/home/reclusiarch/test
Thanks for your help!!
Edit: I intially pared down the script to the piece of offending code but that might make it not run. Again, if you want to test it, you could run the bash command given. (The script ideally would reside in the user's $HOME directory).
Edit: Solved (Charles Duffy solved it) It was a simple mistake of forgetting which name was which.
Your declaration is:
typeset -A filelist
However, your usage is:
fileslist["$dirFrom"]="$dirTo"
fileslist is not filelist.
I was running a small bash script, but I couldn't figure out why it was entering a if block even when condition should be false.
$ cat script.sh
#!/bin/bash
if [[ "$#"="-h" || "$#"="--help" ]]
then
echo 'Show help'
exit
fi
echo 'Do stuff'
$ ./script.sh
Show help
$ bash -x script.sh
+ [[ -n =-h ]]
+ echo 'Show help'
Show help
+ exit
$ bash -x script.sh -option
+ [[ -n -option=-h ]]
+ echo 'Show help'
Show help
+ exit
So why is $# equal to -n when I didn't pass any arguments? Also even if it is, how does -n =-h evaluate to true? When I do pass an argument -option, why is it evaluated to true, too?
Whitespace is significant. Spaces between the arguments to [[ are mandatory.
if [[ "$#" = "-h" || "$#" = "--help" ]]
Also, "$#" means "all of the command-line arguments". It would be better to just check a single argument.
if [[ "$1" = "-h" || "$1" = "--help" ]]
And for what it's worth, variable expansions in [[ don't have to be quoted. It doesn't hurt, and quoting your variables actually a good habit to develop, but if you want you can remove the quotes.
if [[ $1 = -h || $1 = --help ]]
[[ string ]] return true if string is not empty, i.e. it's a shorcut for
[[ -n string ]]
In your case, the string was =-h, that's why you see
[[ -n =-h ]]
To test for string equiality, you have to use the = (or ==) operator, that must be preceded and followed by whitespace.
[[ "$#" = "-h" ]]
Note that "$#" means all the arguments:
set -- a b c
set -x
[[ "$#" == 'a b c' ]] && echo true
gives
+ [[ a b c == \a\ \b\ \c ]]
+ echo true
true
The other answers have already explained the problems with your code. This one shows that
bashisms such as [[ ... ]] are not needed,
you can gain flexibility by using a for loop to check whether at least one of the command-line argument matches -h or --help.
Script
#!/bin/sh
show_help=0
for arg in "$#"; do
shift
case "$arg" in
"--help")
show_help=1
;;
"-h")
show_help=1
;;
*)
;;
esac
done
if [ $show_help -eq 1 ]; then
printf "Show help\n"
exit
fi
Tests
After making the script (called "foo") executable, by running
chmod u+x foo
I get the following results
$ ./foo
$ ./foo -h
Show help
$ ./foo --help
Show help
$ ./foo bar
$ ./foo bar --help
Show help
$ ./foo bar --help baz -h
Show help
I've recently started working with the getopts command in bash. I am confused as to why my script runs the dafult action "cat ~bin/Temp/log.txt | ~bin/Scripts/report.pl" when arguments have been provided. I only want that to run if no arguments were passed to the shell script. I've used getopts:Std in perl where I was able to code somthing like:
unless ($opts{d}) {
do something...}
How would I code something like that in a shell script? Also, how would I code logic such as this:
if ($opts{c}) {
cat ~bin/Temp/mag.txt | ~bin/Scripts/report.pl -c
}
elsif ($opts{d} {
cat ~bin/Temp/mag.txt | ~bin/Scripts/report.pl -d
My code:
#!/bin/sh
while getopts cd name
do
case $name in
c)copt=1;;
d)dopt=1;;
*)echo "Invalid arg";;
esac
done
if [[ ! -z $copt ]] #Specifies what happens if the -c argument was provided
then
echo "CSV file created!"
cat "~/bin/Temp/log.txt" | ~/bin/Scripts/vpnreport/report.pl -c
fi
if [[ ! -z $dopt ]] #Specifies what happens if the -d argument was provided
then
echo "Debug report and files created"
cat ~bin/Temp/mag.txt | ~bin/Scripts/report.pl -d
fi
if [[ ! -z $name ]] #Specifies what happens if no argument was provided
then
echo "Running standard VPN report"
cat ~bin/Temp/log.txt | ~bin/Scripts/report.pl
fi
shift $(($OPTIND -1))
My Output:
[~/bin/Scripts/report]$ sh getoptstest.sh
Running standard report
[~/bin/Scripts/report]$ sh getoptstest.sh -d
Debug report and files created
Running standard report
[~/bin/Scripts/report]$
The two getopts commands are vasty different from bash to perl and I just can't seem to get the hang of the bash varient even after reading several tutorials. Any help would be greatly appreciated!
On the final run of getopts, your variable (name) will be set to "?".
#!/bin/bash
while getopts abc foo; do :; done
echo "<$foo>"
Output of the above:
$ ./mytest.sh
<?>
$ ./mytest.sh -a
<?>
Insead, use elif, which is like Perl's elsif:
if [[ ! -z $copt ]]
then
# ...
elif [[ ! -z $dopt ]]
then
# ...
else
# ...
fi
Or test if [[ -z $copt && -z $dopt ]], or so forth. Other notes:
See the official if and case documentation in the Bash manual under "Conditional Constructs".
[[ ! -z $name ]] means the same as the more-direct [[ -n $name ]].
Use #!/bin/bash instead of #!/bin/sh, or switch off of [[ in favor of [. The double square bracket (and your use thereof) is specific to bash, and rarely works with sh.
I took Jeff's answer and rewrote my script so it works now:
#!/bin/bash
while getopts cd name
do
case $name in
c)carg=1;;
d)darg=1;;
*)echo "Invalid arg";;
esac
done
#Specifies what happens if the -c argument was provided:
if [[ ! -z $carg ]]
then
if [[ -z $darg ]]
then
echo "CSV created"
cat ~bin/Temp/log.txt | ~bin/Scripts/report.pl -c
else
echo "Debug CSV created"
cat ~bin/Temp/log.txt | ~bin/Scripts/report.pl -cd
fi
fi
#Specifies what happens if the -d argurment was provided:
if [[ ! -z $darg ]]
then
echo "Debug report created"
cat ~bin/Temp/log.txt | ~bin/Scripts/report.pl -d
#Specifies what happens if no argument was provided:
else
echo "Standard report created"
cat ~bin/Temp/logs.txt | ~bin/Scripts/report.pl
fi
Thank you again for your assistance!