How to remove nth element from command arguments in bash - 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

Related

Using a positional parameter as the name of the array I want to loop over

I have been looking at other similar questions but didn't find the proper answer. $1 is the name of the array i want to loop over.
#!/bin/bash
for i in ${"$1"[#]}
do
echo "$i"
done
If I don't misunderstand your not very well written question, you are looking for bash indirection, which is available since bash, version 2.
Below a minimal example
#! /bin/bash
foo=( 1 2 3 4 )
bar=( 5 6 7 8 )
if [[ ( "$1" -ne foo && "$1" -ne bar ) ]];
then
echo "Usage: $0 <foo|bar>"
exit 1
fi
ambito="$1"[#] # here what you are looking for
for i in "${!ambito}"; # here the indirection
do
echo -n "$i "
done
echo ""
And you can call this indirection.sh scrit as:
$ ./indirection.sh foo
1 2 3 4
or
$ ./indirection.sh bar
5 6 7 8
This being said, using indirection may cause confusion. In most cases it could be replaced by associative arrays.
$1 is the first command line argument, not all of them. Probably what you are looking for is $#.
And to create an array holding them, something like:
ambito=($#)

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

List of nums (column) in variable 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!

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

How do I find the number of arguments passed to a Bash script?

How do I find the number of arguments passed to a Bash script?
This is what I have currently:
#!/bin/bash
i=0
for var in "$#"
do
i=i+1
done
Are there other (better) ways of doing this?
The number of arguments is $#
Search for it on this page to learn more:
http://tldp.org/LDP/abs/html/internalvariables.html#ARGLIST
#!/bin/bash
echo "The number of arguments is: $#"
a=${#}
echo "The total length of all arguments is: ${#a}: "
count=0
for var in "$#"
do
echo "The length of argument '$var' is: ${#var}"
(( count++ ))
(( accum += ${#var} ))
done
echo "The counted number of arguments is: $count"
echo "The accumulated length of all arguments is: $accum"
to add the original reference:
You can get the number of arguments from the special parameter $#. Value of 0 means "no arguments". $# is read-only.
When used in conjunction with shift for argument processing, the special parameter $# is decremented each time Bash Builtin shift is executed.
see Bash Reference Manual in section 3.4.2 Special Parameters:
"The shell treats several parameters specially. These parameters may only be referenced"
and in this section for keyword $# "Expands to the number of positional parameters in decimal."
Below is the easy one -
cat countvariable.sh
echo "$#" | awk '{print NF}'
Output :
#./countvariable.sh 1 2 3 4 5 6
6
#./countvariable.sh 1 2 3 4 5 6 apple orange
8

Resources