This question already has answers here:
getting positional arguments in bash
(4 answers)
Closed 5 years ago.
I'd like to join and send the remaining parameters for a script to a command. Eg:
if [ "$2" == "exec" ]; then
other_script execute "$3 $4 $5 $6 $7 (etc etc etc)"
fi
Please advise.
One way is to use bash array slice notation:
foo() {
echo "[$1]"
echo "[$2]"
echo "[${#:3}]"
}
Produces:
$ foo a b c d ef
[a]
[b]
[c d ef]
Which you would implement in your code as:
if [ "$2" == "exec" ]; then
other_script execute "${#:3}"
fi
If you needed say the 3rd and 4th arguments, you could apply a length to the slice:
other_script execute "${#:3:2}" # :2 is a length specification
Another way, if you didn't need arguments $1 or $2 any longer, is to just shift them out of the way:
foo=${1:?Missing argument one}
bar=${2:-Default}
shift 2
echo "$#" # the first two args are gone, so this is now args #3 on
I prefer this way, honestly, for a couple of reasons:
Numbered args are hard to remember: named args are more clear.
The array slice notation isn't well-known (based on my experience), so it may cause some confusion with future maintenance.
Related
I have a script that takes in several arguments.
I need everything but $1 and $2 in a string.
I have tried this:
message="$*"
words= $(grep -v "$2"|"$3" $message)
but it doesn't work, it gives me the error:
./backup: line 26: First: command not found
Use shift 2 to shift the arguments along (it drops the first n arguments).
If you need "$1" and "$2" for later, save them in variables first.
Note that in shell, assignments to variables cannot have whitespace either side of the =.
First=$1
Second=$2
shift 2
Message=$#
Maybe something like this?
[root#tsekmanrhel771 ~]# cat ./skip1st2.sh
#!/bin/bash
COUNT=0
for ARG in "$#"
do
COUNT=$[COUNT + 1]
if [ ${COUNT} -gt 2 ]; then
RESULT="${RESULT} ${ARG}"
fi
done
echo ${RESULT}
[root#tsekmanrhel771 ~]# ./skip1st2.sh first second third 4 5 6 7
third 4 5 6 7
You can use a subarray:
$ set -- arg1 arg2 arg3 arg4
$ str=${*:3}
$ echo "$str"
arg3 arg4
More often than not, it's good practice to preserve the arguments as separate elements, though, which you can do by using $# and assigning to a new array:
$ arr=("${#:3}")
$ declare -p arr
declare -a arr=([0]="arg3" [1]="arg4")
Notice that in str=${*:3}, quoting isn't necessary, but in arr=("${#:3}"), it is (or the arguments would be split on whitespace).
As for your error message: your command
words= $(grep -v "$2"|"$3" $message)
does the following:
It sets a variable words to the empty string for the environment of the command (because there is a blank after =).
It tries to set up a pipeline consisting of two commands, grep -v "$2" and "$3" $message. The first of these commands would just hang and wait for input; the second one tries to run the contents of $3 as a command; presumably, based on your error message, $3 contains First.
If the pipeline would actually run, its output would be run as a command (again because of the blank to the right of =).
This question already has answers here:
Bash If-statement to check If string is equal to one of several string literals [duplicate]
(2 answers)
Closed 6 years ago.
I want to check in my bash script, if a variable is equal to value 1 OR equal to value 2.
I don't want to use something like this, because the 'if true statements' are the same (some big echo texts), when the variable is equal to 1 or 2. I want to avoid data redundancy.
if [ $1 == 1 ] ; then echo number 1 ; else
if [ $1 == 2 ] ; then echo number 2 ; fi
More something like
if [ $1 == 1 OR 2 ] ; then echo number 1 or 2 ; fi
Since you are comparing integer values, use the bash arithmetic operator (()), as
(( $1 == 1 || $1 == 2 )) && echo "number 1 or 2"
For handling-strings using the regex operator in bash
test="dude"
if [[ "$test" =~ ^(dude|coolDude)$ ]]; then echo "Dude Anyway"; fi
# literally means match test against either of words separated by | as a whole
# and not allow for sub-string matches.
Probably the most easy to extend option is a case statement:
case $1 in
[12])
echo "number $1"
esac
The pattern [12] matches 1 or 2. For larger ranges you could use [1-5], or more complicated patterns like [1-9]|[1-9][0-9] to match any number from 1 to 99.
When you have multiple cases, you should separate each one with a ;;.
This question already has answers here:
What does the "$#" special parameter mean in Bash?
(2 answers)
Closed 8 years ago.
I have a script with this:
login {
# checking parameters -> if not ok print error and exit script
if [ $# -lt 2 ] || [ $1 == '' ] || [ $2 == '' ]; then
echo "Please check the needed options (username and password)"
echo ""
echo "For further Information see Section 13"
echo ""
echo "Press any key to exit"
read
exit
fi
} # /login
But I really dont know what the $# means on the third line.
The pound sign counts things.
If it's just $#, it's the number of positional parameters, like $1, $2, $3. (Not counting $0, mind you.)
If it's ${#var}, it's the number of characters in the expansion of the parameter. (String length)
If it's ${#var[#]}, it's the number of elements in the array. Since bash arrays are sparse, this can be different from the index of the last element plus one.
It's the number of arguments passed.
You can read it here, search for "Detecting command line arguments"
So I was working on a project tonight and assumed based on my poor understanding that the requirement was to create a script to take a number and count down to 1 with commas on the same line.
A few people here introduced me to the seq command and I was on my way.
Turns out it needs to take the variable integer from a command line argument.
What I have now:
#!/bin/bash
#countdown
read -p "Enter a Number great than 1: " counter
seq -s, $counter -1
Needs to work by taking an argument after the line, such as /assign1p1 5 and then outputting 5,4,3,2,1
I've seen the $1 used as an argument marker? Is that how to work from it?
Use Three Arguments
The correct call to seq for your use case is:
seq [OPTION]... FIRST INCREMENT LAST
To decrement your starting value down to 1 using the defined separator, try something similar to this example:
$ set -- 5
$ seq -s, $1 -1 1
5,4,3,2,1
Obviously, the call to set won't be needed inside the script, but is a great way to test at the command line.
The command-line arguments passed to your script are $1, $2, etc.
#!/bin/bash
seq -s, $1 1
echo
If you want to make this more robust you might want to verify that the user passed in the correct number of arguments, which is the variable $#.
#!/bin/bash
if (( $# != 1 )); then
echo "Usage: $0 num" >&2
exit 1
fi
seq -s, $1 1
echo
Arguments passed to the script from the command line include : $0, $1, $2, $3 . . .
$0 is the name of the script itself, $1 is the first argument, $2 the second, $3 the third, and so forth. [2] After $9, the arguments must be enclosed in brackets, for example, ${10}, ${11}, ${12}.
If for whatever reason you do not want to use seq
a=$1
for (( b = a; b > 0; b-- ))
do
(( b == a )) || printf ,
printf $b
done
I want to parse the arguments given to a shell script by using a for-loop. Now, assuming I have 3 arguments, something like
for i in $1 $2 $3
should do the job, but I cannot predict the number of arguments, so I wanted use an RegEx for the range and $# as the number of the last argument. I don't know how to use these RegEx' in a for-loop, I tried something like
for i in $[1-$#]
which doesn't work. The loop only runs 1 time and 1-$# is being calculated, not used as a RegEx.
Basic
A for loop by default will loop over the command-line arguments if you don't specify the in clause:
for arg; do
echo "$arg"
done
If you want to be explicit you can get all of the arguments as "$#". The above loop is equivalent to:
for arg in "$#"; do
echo "$arg"
done
From the bash man page:
Special Parameters
$# — Expands to the positional parameters, starting from one. When the expansion occurs within
double quotes, each parameter expands to a separate word. That is, "$#" is equivalent to "$1" "$2" .... If the double-quoted expansion occurs within a word, the expansion of the first
parameter is joined with the beginning part of the original word, and the expansion of the
last parameter is joined with the last part of the original word. When there are no positional parameters, "$#" and $# expand to nothing (i.e., they are removed).
Advanced
For heavy-duty argument processing, getopt + shift is the way to go. getopt will pre-process the command-line to give the user some flexibility in how arguments are specified. For example, it will expand -xzf into -x -z -f. It adds a -- argument after all the flags which separates flags from file names; this lets you do run cat -- -my-file to display the contents of -my-file without barfing on the leading dash.
Try this boilerplate code on for size:
#!/bin/bash
eval set -- "$(getopt -o a:bch -l alpha:,bravo,charlie,help -n "$0" -- "$#")"
while [[ $1 != -- ]]; do
case "$1" in
-a|--alpha)
echo "--alpha $2"
shift 2
;;
-b|--bravo)
echo "--bravo"
shift
;;
-c|--charlie)
echo "--charlie"
shift
;;
-h|--help)
echo "Usage: $0 [-a ARG] [-b] [-c]" >&2
exit 1
;;
esac
done
shift
Notice that each option has a short a long equivalent, e.g. -a and --alpha. The -a flag takes an argument so it's specified as a: and alpha: in the getopt call, and has a shift 2 at the end of its case.
Another way to iterate over the arguments which is closer to what you were working toward would be something like:
for ((i=1; i<=$#; i++))
do
echo "${#:i:1}"
done
but the for arg syntax that John Kugelman showed is by far preferable. There are, however, times when array slicing is useful. Also, in this version, as in John's, the argument array is left intact. Using shift discards its elements.
You should note that what you were trying to do with square brackets is not a regular expression at all.
I suggest doing something else instead:
while [ -n "$1" ] ; do
# Do something with $1
shift
# Now whatever was in $2 is now in $1
done
The shift keyword moves the content of $2 into $1, $3 into $2, etc. pp.
Let's say the arguments where:
a b c d
After a shift, the arguments are now:
b c d
With the while loop, you can thus parse an arbitrary number of arguments and can even do things like:
while [ -n "$1" ] ; do
if [ "$1" = "-f" ] ; then
shift
if [ -n "$1" ] ; then
myfile="$1"
else
echo "-f needs an additional argument"
end
fi
shift
done
Imagine the arguments as being an array and $n being indexes into that array. shift removes the first element, so the index 1 now references the element that was at index 2 prior to shift. I hope you understand what I want to say.