How to create a string=$* without arguments $1 and $2 - linux

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 =).

Related

Using a for loop input to rename an object within the loop in bash

I am currently trying to rename an input argument by the variable "i" in the following for loop:
cd $1
num=$(echo $#)
echo $num
echo $#
echo "This is the next part where I print stuff"
for i in $(seq 2 $num)
do
echo $i
echo ${!i}
Args_array+=$(printf '${arg_%s[#]}' ${i})
echo $Args_array
arg_${i}=$(ls ${!i})
done
The output is as follows:
4
output_folder /path/to/my.tsv /path/to/my2.tsv /path/to/my3.tsv
2
/path/to/my.tsv
${arg_2[#]}
/var/spool/slurm/d/job6985121/slurm_script: line 23: arg_2=/path/to/my.tsv: No such file or directory
But it will not allow me to rename the $2, $3 arguments with "i" like this. Any help would be appreciated.
I want to pass these arguments into R and have to put them in arg_1, arg_2, etc. format.
Not sure I understand what's being attempted with Args_array so focusing solely on OP's comment: 'have to put them in arg_1, arg_2' and skipping arg_1 since OP's code doesn't appear to care about storing $1 anywhere; then again, is R not capable of processing input parameters from the command line?
One bash idea:
$ cat testme
#!/usr/bin/bash
num=$#
for ((i=2;i<=$num;i++))
do
declare args_$i=${!i}
done
for ((i=2;i<=$num;i++))
do
typeset -p args_$i
done
Taking for a test drive:
$ testme output_folder /path/to/my.tsv /path/to/my2.tsv /path/to/my3.tsv
declare -- args_2="/path/to/my.tsv"
declare -- args_3="/path/to/my2.tsv"
declare -- args_4="/path/to/my3.tsv"

What is the difference between $* and $#

Who can simply explain
what is the difference between $* and $#?
Why there are two variables for same content as above?
There is no difference if you do not put $* or $# in quotes. But if you put them inside quotes (which you should, as a general good practice), then $# will pass your parameters as separate parameters, whereas $* will just pass all params as a single parameter.
Take these scripts (foo.sh and bar.sh) for testing:
>> cat bar.sh
echo "Arg 1: $1"
echo "Arg 2: $2"
echo "Arg 3: $3"
echo
>> cat foo.sh
echo '$* without quotes:'
./bar.sh $*
echo '$# without quotes:'
./bar.sh $#
echo '$* with quotes:'
./bar.sh "$*"
echo '$# with quotes:'
./bar.sh "$#"
Now this example should make everything clear:
>> ./foo.sh arg1 "arg21 arg22" arg3
$* without quotes:
Arg 1: arg1
Arg 2: arg21
Arg 3: arg22
$# without quotes:
Arg 1: arg1
Arg 2: arg21
Arg 3: arg22
$* with quotes:
Arg 1: arg1 arg21 arg22 arg3
Arg 2:
Arg 3:
$# with quotes:
Arg 1: arg1
Arg 2: arg21 arg22
Arg 3: arg3
Clearly, "$#" gives the behaviour that we generally want.
More detailed description:
Case 1: No quotes around $* and $#:
Both have same behaviour.
./bar.sh $* => bar.sh gets arg1, arg2 and arg3 as separate arguments
./bar.sh $# => bar.sh gets arg1, arg2 and arg3 as separate arguments
Case 2: You use quotes around $* and $#:
./bar.sh "$*" => bar.sh gets arg1 arg2 arg3 as a single argument
./bar.sh "$#" => bar.sh gets arg1, arg2 and arg3 as a separate arguments
More importantly, $* also ignores quotes in your argument list. For example, if you had supplied ./foo.sh arg1 "arg2 arg3", even then:
./bar.sh "$*" => bar.sh will still receive arg2 and arg3 as separate parameters!
./bar.sh "$#" => will pass arg2 arg3 as a single parameter (which is what you usually want).
Notice again that this difference occurs only if you put $* and $# in quotes. Otherwise they have the same behaviour.
Official documentation: http://www.gnu.org/software/bash/manual/bash.html#Special-Parameters
Aside from the difference as described in the technical documents, it is best shown using some examples:
Lets assume we have four shell scripts, test1.sh:
#!/bin/bash
rm $*
test2.sh:
#!/bin/bash
rm "$*"
test3.sh:
#!/bin/bash
rm $#
test4.sh:
#!/bin/bash
rm "$#"
(I am using rm here instead of echo, because with echo, one can not see the difference)
We call all of them with the following commandline, in a directory which is otherwise empty:
./testX.sh "Hello World" Foo Bar
For test1.sh and test3.sh, we receive the following output:
rm: cannot remove ‘Hello’: No such file or directory
rm: cannot remove ‘World’: No such file or directory
rm: cannot remove ‘Foo’: No such file or directory
rm: cannot remove ‘Bar’: No such file or directory
This means, the arguments are taken as a whole string, joined with spaces, and then reparsed as arguments and passed to the command. This is generally not helpful when forwarding arguments to another command.
With test2.sh, we get:
rm: cannot remove ‘Hello World Foo Bar’: No such file or directory
So we have the same as for test{1,3}.sh, but this time, the result is passed as one argument.
test4.sh has something new:
rm: cannot remove ‘Hello World’: No such file or directory
rm: cannot remove ‘Foo’: No such file or directory
rm: cannot remove ‘Bar’: No such file or directory
This implies that the arguments are passed in a manner equivalent to how they were passed to the the script. This is helpful when passing arguments to other commands.
The difference is subtle, but will bite you when passing arguments to commands which expect information at certain points in the command line and when spaces take part in the game. This is in fact a good example of one of the many pitfalls of most shells.
see this here :
$# Stores the number of command-line arguments that
were passed to the shell program.
$? Stores the exit value of the last command that was
executed.
$0 Stores the first word of the entered command (the
name of the shell program).
$* Stores all the arguments that were entered on the
command line ($1 $2 ...).
"$#" Stores all the arguments that were entered
on the command line, individually quoted ("$1" "$2" ...).
take an example
./command -yes -no /home/username
so now..
$# = 3
$* = -yes -no /home/username
$# = ("-yes" "-no" "/home/username")
$0 = ./command
$1 = -yes
$2 = -no
$3 = /home/username
They are different when quoted:
$ set "a b" c d
$ echo $#
3
$ set "$*"
$ echo $#
1
$ set "a b" c d
$ echo $#
3
$ set "$#"
$ echo $#
3
Here only the second form preserves the argument count.

run shell script with arguments manipulation

I need to get three arguments by test.ksh script
as the following
./test.ksh 12 34 AN
is it possible to set the argument by counter for example ?
for get_arg 1 2 3
do
my_array[get_arg]=$$get_arg
print ${my_array[get_arg]}
done
in this example I want to get three arguments from the user by loop counter "$$get_arg"
in place of $1 $2 $3
is it possible? and how ?
my_array=("$#")
for i in 0 1 2
do
echo "${my_array[$i]}"
done
This assigns all the arguments to array my_array; the loop then selects the first three arguments for echoing.
If you're sure you want the first three arguments in the array, you could use:
my_array=("$1" "$2" "$3")
If you want the 3 arguments at positions 1, 2, 3 in the array (rather than 0, 1, 2), then use:
# One or the other but not both of the two assignments
my_array=("dummy" "$#")
my_array=("dummy" "$1" "$2" "$3")
for i in 1 2 3
do
echo "${my_array[$i]}"
done
bash has a special variable
$#
which contains the arguments of the script it currently executes. I think this is what your'e looking for:
for arg in $# ; do
# code
done
Edit:
My bad ksh:
for arg;do
print $arg
done
Original Post:
Use shift to iterate through shell script parameters:
# cat test.sh
#!/bin/bash
while [ "$1" != "" ]; do
echo $1
shift
done
test run:
# ./test.sh arg1 monkey arg3
arg1
monkey
arg3
source
Even you don't need in $#, this would work the same:
#!/bin/bash
i=0
for arg; do
my_array[i]="$arg"
echo "${my_array[i]}"
(( i++ ))
done
That is,
if in words is not present, the for command executes the commands
once for each positional parameter that is set, as if in $# had been
specified.

Shell Script for counting down passed argument

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

RegEx in bash-script (for-loop)

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.

Resources