What is the difference between "for i" and "for i in 1 2 3 4" in shell scripting? - bash

I had to print all the arguments parsed in a shell script on a different line. I wrote a script as
for i in 1 2 3 4 5
do
echo $i
done
but this prints
1
2
3
4
5
even if i parse the arguments as "10 20 30 40 50"
and one code on the internet
for i
do
echo $i
done
this code prints the arguments correctly.
Can someone explain me why that code works but mine doesn't?
Also how can I use the value of one variable ($i) as the variable name to print something else. like
i=1
$($i)
should print the value of $1.

for i is equivalent to for i in "$#"
From Bash help for:
for: for NAME [in WORDS ... ] ; do COMMANDS; done
Execute commands for each member in a list.
The 'for' loop executes a sequence of commands for each member in a
list of items. If 'in WORDS ...;' is not present, then 'in "$#"' is
assumed. For each element in WORDS, NAME is set to that element, and
the COMMANDS are executed.
If in WORDS ...; is not present, then in "$#" is assumed
If you want to get the variable from a variable, use indirect expansion:
set -- arg1 arg2 arg3 foo
for i in 3 4 1 2
do
echo "${!i}"
done
# Output: arg3 foo arg2 arg1

Related

Using "set --" in shell script results in "bad number"

Using an existing shell script and attempt to obtain an understanding of
exactly what it is doing.
The first 7 lines were extracted out, but when I run a test, it fails with a "bad number" error.
Here is the code from the script.
#! /bin/ksh
echo $2
DUMMY_INPUT=$2
set -- $DUMMY_INPUT
shift
VAR1=$1;shift;shift
echo $VAR1
This was the first test.
> ./runThis.sh arg1 arg2 arg3 arg4 arg5
arg2
./runThis.sh[6]: shift: (null): bad number
Based on the error, another test was run using numbers only but the same error appears.
> ./runThis.sh 1 2 3 4 5 6 7
2
./runThis.sh[6]: shift: (null): bad number
As a test, the "set --" command was commented out and it now works.
#! /bin/ksh
echo $2
DUMMY_INPUT=$2
#set -- $DUMMY_INPUT <-----Comment out this line
shift
VAR1=$1;shift;shift
echo $VAR1
> ./runThis.sh arg1 arg2 arg3 arg4 arg5
arg2
arg2
> ./runThis.sh 1 2 3 4 5 6 7
2
2
Is the implementation of the "set" command wrong?
Any idea why it is failing?
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
After reading the response, I have a better understanding....
#! /bin/ksh
echo $2
DUMMY_INPUT=$#
set -- $DUMMY_INPUT
shift
VAR1=$1;shift;shift
echo $VAR1
If I followed correctly, the "set" command sets the input arguments to be the content of the DUMMY_INPUT which was set to only a single value in the original code.
This code change sets it to the entire string of arguments.
When you call shift in ksh but there are no positional parameters defined, you get the error
ksh: shift: (null): bad number
For example:
> set -- a b
> echo $1
a
> shift
> echo $1
b
> shift
> echo $1
> shift
ksh: shift: (null): bad number

Pass parameters that contain whitespaces via shell variable

I've got a program that I want to call by passing parameters from a shell variable. Throughout this question, I am going to assume that it is given by
#!/bin/sh
echo $#
i.e. that it prints out the number of arguments that are passed to it. Let's call it count-args.
I call my program like this:
X="arg1 arg2"
count-args $X
This works quite well. But now one of my arguments has a whitespace in it and I can't find a way to escape it, e.g. the following things do not work:
X="Hello\ World"
X="Hello\\ World"
X="'Hello World'"
In all of the cases, my program count-args prints out 2. I want to find a way so I can pass the string Hello World and that it returns 1 instead. How?
Just for clarification: I do not want to pass all parameters as a single string, e.g.
X="Hello World"
count-args $X
should print out 2. I want a way to pass parameters that contain whitespaces.
Use an array to store multiple, space-containing arguments.
$ args=("first one" "second one")
$ count-args "${args[#]}"
2
This can be solved with xargs. By replacing
count-args $X
with
echo $X | xargs count-args
I can use backslashes to escape whitespaces in $X, e.g.
X="Hello\\ World"
echo $X | xargs count-args
prints out 1 and
X="Hello World"
echo $X | xargs count-args
prints out 2.
count-args "$X"
The quotes ensure in bash, that the whole content of variable X is passed as a single parameter.
Your Counting script:
$ cat ./params.sh
#!/bin/sh
echo $#
For completeness here is what happens with various arguments:
$ ./params.sh
0
$ ./params.sh 1 2
2
$ ./params.sh
0
$ ./params.sh 1
1
$ ./params.sh 1 2
2
$ ./params.sh "1 2"
1
And here is what you get with variables:
$ XYZ="1 2" sh -c './params.sh $XYZ'
2
$ XYZ="1 2" sh -c './params.sh "$XYZ"'
1
Taking this a bit further:
$ cat params-printer.sh
#!/bin/sh
echo "Count: $#"
echo "1 : '$1'"
echo "2 : '$2'"
We get:
$ XYZ="1 2" sh -c './params-printer.sh "$XYZ"'
Count: 1
1 : '1 2'
2 : ''
This looks like what you wanted to do.
Now: If you have a script you cannot control and neither can you control the way the script is invoked. Then there is very little you can do to prevent a variable with spaces turning into multiple arguments.
There are quite a few questions around this on StackOverflow which indicate that you need the ability to control how the command is invoked else there is little you can do.
Passing arguments with spaces between (bash) script
Passing a string with spaces as a function argument in bash
Passing arguments to a command in Bash script with spaces
And wow! this has been asked so many times before:
How to pass argument with spaces to a shell script function

How to remove first bash argument and pass the others to another command?

In bash $# contains all the arguments used to call the script but I am looking for a solution to remove the first one
./wrapper.sh foo bar baz ...:
#!/bin/bash
# call `cmd` with bar baz ... (withouyt foo one)
I just want to call cmd bar baz ...
You can use shift to shift the argument array. For instance, the following code:
#!/bin/bash
echo $#
shift
echo $#
produces, when called with 1 2 3 prints 1 2 3 and then 2 3:
$ ./example.sh 1 2 3
1 2 3
2 3
shift removes arguments from $#.
shift [n]
Shift positional parameters.
Rename the positional parameters $N+1,$N+2 ... to $1,$2 ... If N is
not given, it is assumed to be 1.
Exit Status:
Returns success unless N is negative or greater than $#.
Environment-variable-expansion! Is a very portable solution.
Remove the first argument: with $#
${##"$1"}
Remove the first argument: with $*
${*#"$1"}
Remove the first and second argument: with $#
${##"$1$2"}
Both $# or $* will work because the result of expansion is a string.
links:
Remove a fixed prefix/suffix from a string in Bash
http://www.tldp.org/LDP/abs/html/abs-guide.html#ARGLIST
Variable expansion is portable because it is defined under gnu core-utils
Search for "Environment variable expansion" at this link:
https://www.gnu.org/software/coreutils/manual/html_node/

Unix Shell equivalency to Java .hasNext()?

Or anything in shell script to implement the same thing?
I was doing an assignment that requires us to write a Bourne shell script that shows the last argument of a bunch, e.g.:
lastarg arg1 arg2 arg3 ..... argN
which would show:
argN
I was not sure if there's any equivalencies to hasNext in Java as it's easy to implement.
Sorry if I was rude and unclear.
#!/bin/bash
all=($#)
# to make things short:
# you can use what's in a variable as a variable name
last=$(( $# )) # get number of arguments
echo ${!last} # use that to get the last argument. notice the !
# while the number of arguments is not 0
# put what is in argument $1 into next
# move all arguments to the left
# $1=foo $2=bar $4=moo
# shift
# $1=bar $2=moo
while [ $# -ne 0 ]; do
next=$1
shift
echo $next
done
# but the problem was the last argument...
# all=($#): put all arguments into an array
# ${all[n]}: get argument number n
# $(( 1+2 )): do math
# ${#all[#]}: get the count of element in an array
echo -e "all:\t ${all[#]}"
echo -e "second:\t ${all[1]}"
echo -e "fifth:\t ${all[4]}"
echo -e "# of elements:\t ${#all[#]}"
echo -e "last element:\t ${all[ (( ${#all[#]} -1 )) ]}"
ok, last edit (omg :p)
$ sh unix-java-hasnext.sh one two three seventyfour sixtyeight
sixtyeight
one
two
three
seventyfour
sixtyeight
all: one two three seventyfour sixtyeight
second: two
fifth: sixtyeight
# of elements: 5
last element: sixtyeight
POSIX-based shell languages don't implement iterators.
The only things you have are for V in words ; do ... ; done or implementing the loop with while and manual stuff to update and test the loop variable.
This is not place to make wild guesses, still: Bash provide shift operator, for loop and more.
(If this is for argument processing you have getopt library. More info in Using getopts in bash shell script to get long and short command line options )

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