List of nums (column) in variable Bash - bash

I want to create code that creates a variable containing the number of positional arguments as a consecutive list.
For example, if the number of positional arguments ($#) is 5 then:
var='
1
2
3
4
5'

Use seq
echo "$#"
5
var=$(seq 1 $#)
echo "$var"
1
2
3
4
5
Try it online!

Related

In bash how to use the last argument- and adding all other arguments to array

I have a script where the user can add as many arguments as he would like (numbers).
The script will sum all the numbers beside the last number - The last number (argument) is the number that I need to divide by
For example:
./test.sh 2 2 6 5
This will sum the first 3 numbers (2+2+6) and divide the answer by 5 (the last argument)
How can I use the last argument? Echo ????
How can I move loop the first arguments besides the last one – I would like that all 3 arguments will be added to an array and I can loop it
Please note that the number of arguments can be changed
How can I use the last argument? Echo ????
Granting $# > 0, you can use "${!#}".
How can I move loop the first arguments besides the last one – I would
like that all 3 arguments will be added to an array and I can loop it
Again granting $# > 0, you can refer to "${#:1:$# - 1}".
Read the Arrays section in the bash manual to know how to properly expand arrays.
I also recommend learning how quoting works and knowing the dangers of unwanted word splitting and globbing.
Shortly (with bashisms)
As this question is tagged integer-arithmetic and bash:
Here is a small and efficient script:
#!/bin/bash
addVals=${*: 1 : $# - 1}
declare -i intResult=" ( ${addVals// /+} ) / ${#: -1} "
echo $intResult
But there's no loop...
Long answer
How can I use the last argument? Echo ????
You could make your tries in command line:
set -- 2 2 6 5
Then
echo $#
2 2 6 5
echo ${*: 3}
6 5
echo ${*: -1}
5
echo ${*: 1 : -1}
bash: -1: substring expression < 0
echo $#
4
echo ${*: 1 : $# -1}
2 2 6
Ok, then
someVar=${*: 1 : $# -1}
echo ${someVar// /:SpaceReplacment:}
2:SpaceReplacment:2:SpaceReplacment:6
so
declare -i result
result=" ( ${someVar// /+} ) / ${*: -1} "
echo $result
2
How can I move loop the first arguments besides the last one – I would like that all 3 arguments will be added to an array and I can loop it
Still forward, under command line...
someArray=("${#: 1: $# -1 }")
Use declare -p to show $someArray's content:
declare -p someArray
declare -a someArray=([0]="2" [1]="2" [2]="6")
Then
declare -i mySum=0
for i in "${someArray[#]}";do
mySum+=i
done
echo $mySum
10
echo $(( mySum / ${*: -1} ))
2
Please note that the number of arguments can be changed
Please note:
Using double quotes allow processing of strings containing spaces:
set -- foo bar 'foo bar baz'
echo ${2}
bar
echo ${*: $# }
foo bar baz
Difference betweeen use of "$#" (array to array) and "$*" (array to string)
set -- foo bar 'foo bar' 'foo bar baz'
If I take 3 first elements:
someArray=("${#: 1: $# -1 }")
declare -p someArray
declare -a someArray=([0]="foo" [1]="bar" [2]="foo bar")
But
someArray=("${*: 1: $# -1 }")
declare -p someArray
declare -a someArray=([0]="foo bar foo bar")
There are about a thousand ways of doing this. As you would like to make use of integer arithmetic, you can do the following in bash
A short semi-cryptic version would be:
IFS=+
echo $(( ( ${*} - ${#:-1} ) / ${#:-1} ))
Here we make use of the difference between "${*}" and "${#}" to perform the sum by setting IFS=+ (See What is the difference between "$#" and "$*" in Bash?)
A long classic approach would be:
for i in "$#"; do ((s+=i)); done
echo $(( (s-${#:-1})/${#:-1} ))
It's easier to sum all terms and subtract the last term afterwards

What is the bash equivalent to batchs' %*

If I have a batch file and want to just use all given script arguments I can use %*.
Example a.bat
echo %*
Calling a.bat 1 2 3 4 gives:
1 2 3 4
how can I do the same in a bash script?
"$*" will return a single string with all the arguments separated by space.
"$#" will return N strings, one for each argument.
Example a.sh:
echo "$#"
Calling a.sh 1 2 3 4 gives:
1 2 3 4
Another example of a.sh:
printf "%s\n" "$#"
Calling a.sh 1 '2 2' '3 3 3' '4 4 4 4' gives:
1
2 2
3 3 3
4 4 4 4
As opposed to a.sh being:
printf "%s\n" "$*"
which will print:
1 2 2 3 3 3 4 4 4 4

Passing arguments to another script, after having used the "shift" builtin

Consider a simple script program.sh:
$ cat program.sh
echo program: I have "$#" arguments, they are "$#"
$ ./program.sh 1 2 3
program: I have 3 arguments, they are 1 2 3
$ ./program.sh '1 1' 2 3
program: I have 3 arguments. they are 1 1 2 3
Now I want a wrapper script wrapper.sh that invokes program.sh in the same way it was invoked:
$ cat wrapper.sh
echo wrapper: I have "$#" arguments, they are "$#"
./program.sh "$#"
$ ./wrapper.sh 1 2 3
wrapper: I have 3 arguments, they are 1 2 3
program: I have 3 arguments, they are 1 2 3
$ ./wrapper.sh '1 1' 2 3
wrapper: I have 3 arguments. they are 1 1 2 3
program: I have 3 arguments. they are 1 1 2 3
This works perfectly, but I want to use the shift builtin within wrapper.sh, which creates a problem because then I cannot use "$#" anymore in the invocation to program.sh:
$ cat wrapper.sh
echo wrapper: I have "$#" arguments, they are "$#"
shift
./program.sh "$#"
$ ./wrapper.sh 1 2 3
wrapper: I have 3 arguments, they are 1 2 3
program: I have 2 arguments, they are 2 3
$ ./wrapper.sh '1 1' 2 3
wrapper: I have 3 arguments. they are 1 1 2 3
program: I have 2 arguments. they are 2 3
I have tried initially setting the value of $# to a temporary variable, but nothing I tried seems to work for wrapper.sh to invoke program.sh exactly the way it was originally invoked. What is the proper way to handle this?
It is easy to save $#. Just save it as an array:
$ cat wrapper.sh
echo wrapper: I have "$#" arguments, they are "$#"
save=("$#")
shift
./program.sh "${save[#]}"
This produces the output:
$ wrapper.sh '1 1' 2 3
wrapper: I have 3 arguments, they are 1 1 2 3
program: I have 3 arguments, they are 1 1 2 3
The key here is that save is a bash array. Trying to store $# as a shell string does not work. Saving it as an array does work.

How to remove nth element from command arguments in bash

How to I remove the nth element from an argument list in bash?
Shift only appears to remove the first n, but I want to keep some of the first. I want something like:
#!/bin/sh
set -x
echo $#
shift (from position 2)
echo $#
So when I call it - it removes "house" from the list:
my.sh 1 house 3
1 house 3
1 3
Use the set builtin and shell parameter expansion:
set -- "${#:1:1}" "${#:3}"
would remove the second positonal argument.
You could make it generic by using a variable:
n=2 # This variable denotes the nth argument to be removed
set -- "${#:1:n-1}" "${#:n+1}"
a=$1
shift; shift
echo $a $#
If someone knows how to do this in a better way I am all ears!
If you want to use bash you can use a bash array
#!/bin/bash
arg=($0 $#)
or if you don't need argument $0
arg=($#)
# argument to remove
rm_arg=2
arg=(${arg[#]:0:$rm_arg} ${arg[#]:$(($rm_arg + 1))})
echo ${arg[#]}
Now instead of referencing $1 you might use ${arg[1]} .
Remember that bash arguments has an argument $0 and bash arrays have an element [0]. So, if you don't assign bash argument $0 to the first element of a bash array [0] , Then you will find bash argument $1 in your bash array [0] .
arg=($#)
for VAR in $#; do
case $VAR in
A)
echo removing $VAR
arg=($(echo $#| sed "s/$VAR//"))
;;
*)
echo ${arg[#]}
;;
esac
done
Results:
./test.sh 1 2 3 A 4 5
1 2 3 A 4 5
1 2 3 A 4 5
1 2 3 A 4 5
removing A
1 2 3 4 5
1 2 3 4 5

Delete positional parameters in Bash?

You can skip positional parameters with shift but can you delete positional parameters by passing the position?
x(){ CODE; echo "$#"; }; x 1 2 3 4 5 6 7 8
> 1 2 4 5 6 7 8
I would like to add CODE to x() to delete positional parameter 3. I don't want to do echo "${#:1:2} ${#:4:8}". After running CODE, $# should only contain "1 2 4 5 6 7 8".
The best way, if you want to be able to pass on the parameters to another process, or handle space separated parameters, is to re-set the parameters:
$ x(){ echo "Parameter count before: $#"; set -- "${#:1:2}" "${#:4:8}"; echo "$#"; echo "Parameter count after: $#"; }
$ x 1 2 3 4 5 6 7 8
Parameter count before: 8
1 2 4 5 6 7 8
Parameter count after: 7
To test that it works with non-trivial parameters:
$ x $'a\n1' $'b\b2' 'c 3' 'd 4' 'e 5' 'f 6' 'g 7' $'h\t8'
Parameter count before: 8
a
1 2 d 4 e 5 f 6 g 7 h 8
Parameter count after: 7
(Yes, $'\b' is a backspace)
x(){
#CODE
params=( $* )
unset params[2]
set -- "${params[#]}"
echo "$#"
}
Input:
x 1 2 3 4 5 6 7 8
Output:
1 2 4 5 6 7 8
From tldp
# The "unset" command deletes elements of an array, or entire array.
unset colors[1] # Remove 2nd element of array.
# Same effect as colors[1]=
echo ${colors[#]} # List array again, missing 2nd element.
unset colors # Delete entire array.
# unset colors[*] and
#+ unset colors[#] also work.
echo; echo -n "Colors gone."
echo ${colors[#]} # List array again, now empty.
You can call set and reset the positional paramaters at any time
for example
function q {
echo ${#}
set $2 $3 $4
echo ${#}
set $4
echo ${#}
}
q 1 2 3 4
then slice out what you dont want from the array, the below code does that... not sure if its the best way to do it though, was on stack looking for a better way ; )
#!/bin/bash
q=( one two three four five )
echo -e "
(remove) { [:range:] } <- [:list:]
| [:range:] => return list with range removed range is in the form of [:digit:]-[:digit:]
"
function remove {
if [[ $1 =~ ([[:digit:]])(-([[:digit:]]))? ]]; then
from=${BASH_REMATCH[1]}
to=${BASH_REMATCH[3]}
else
echo bad range
fi;shift
array=( ${#} )
local start=${array[#]::${from}}
local rest
[ -n "$to" ] && rest=${array[#]:((${to}+1))} || rest=${array[#]:((${from}+1))}
echo ${start[#]} ${rest[#]}
}
q=( `remove 1 ${q[*]}` )
echo ${q[#]}
while loop over "$#" with shift + set: move each parameter from first to last position, except "test"
# remove option "test" from positional parameters
i=1
while [ $i -le $# ]
do
var="$1"
case "$var" in
test)
echo "param \"$var\" deleted"
i=$(($i-1))
;;
*)
set -- "$#" "$var"
;;
esac
shift
i=$(($i+1))
done

Resources