Why can't get the shell script's parameter count in a function - linux

I'm confused about why can't get the script's parameter count in a function, could anybody help me? thanks in advance:)
test.sh
#!/bin/bash
check(){
echo $#
if [ $# -lt 2 ]; then
echo "Argument missing"
exit 1
fi
}
echo $#
check
Run:
./test.sh aa bb
output:
2
0

Functions have their own local copies of the argument variables, including $#. They are related to the arguments of the function, and the equivalents at the script level are shadowed. If you want to get the script's argument variables then you will need to either store them somewhere else first or pass them to the function.
check "$#"

Related

How would I write an 'if' command that checks to see if there are multiple arguments?

I need to create a script that uses an 'if' command that checks whether there is exactly one argument. If there is more than one argument, I need it to echo “Usage: give exactly 1 argument, the string to be looked for” and then exit immediately.
Welcome to Stackoverflow.
This should do the trick:
if [ "$#" -gt 1 ]; then
echo "Usage: give exactly 1 argument, the string to be looked for"
exit 0
else
echo the expected processing happens in this section of this code
fi

Is it possible to get command arguments now executing?

I want to declare aliases which append argument if no argument specified.
For example,
alias vimtutor=‘vimtutor $([ $# -eq 0 ] && echo ja.utf-8)’
I expected executing vimtutor with no argument is replaced to vimtutor ja.utf-8, but it doesn’t work properly.
So, I tried some tests on bash shell.
$ echo $#
0
$ echo a b c $#
a b c 0
$ echo $0
-bash
And I understood it is impossible to get command arguments now executing in normal way.
Now, I declare shell function showing below,
vimtutor(){ /usr/bin/vimtutor $([ $# -eq 0 ] && echo ja.utf-8) “$#“; }
It woks as expected but feel not smart way.
Is there any idea to solve this problem?
The variables $#, $0, etc when used in an alias references your bash session variables; the arguments used when your bash session was started, not the arguments passed to the alias command.
I suggest you put your command in a script, and alias to that script. I.e.
$ echo 'vimtutor $([ $# -eq 0 ] && echo ja.utf-8)' > ~/vimtutor.sh
$ chmod +x ~/vimtutor.sh
$ alias vimtutor='~/vimtutor.sh'

Arrays in Shell Script, not Bash

I am probably just having a brain fart, but I can not for the life of me figure out how to loop through an array in shell script, not bash. Im sure the answer is on stackoverflow somewhere already, but I can not find a method of doing so without using bash. For my embedded target system bash is not currently an option. Here is an example of what I am attempting to do and the error that is returned.
#!/bin/sh
enable0=1
enable1=1
port=0
while [ ${port} -lt 2 ]; do
if [ ${enable${port}} -eq 1 ]
then
# do some stuff
fi
port=$((port + 1))
done
Whenever I run this script the error "Bad substitution" is returned for line with the if statement. If you guys have any ideas I would greatly appreciate it. Thanks!
a="abc 123 def"
set -- $a
while [ -n "$1" ]; do
echo $1
shift
done
Output via busybox 1.27.2 ash:
abc
123
def
BusyBox provides ash which does not directly provide array support. You could use eval and something like,
#!/bin/busybox sh
enable0=0
enable1=1
for index in 0 1 ; do
eval assign="\$enable$index"
if [ $assign == 1 ]; then
echo "enable$index is enabled"
else
echo "enable$index is disabled"
fi
done
One could use positional parameters for that...
http://pubs.opengroup.org/onlinepubs/009696799/utilities/set.html
#!/bin/sh
enable0=0
enable1=1
set -- $enable0 $enable1
for index in 0 1; do
[ "$1" -eq 1 ] && echo "$1 is enabled." || echo "$1 is disabled."
shift
done
Running on busybox:
~ $ ./test.sh
0 is disabled.
1 is enabled.
It's best not to use eval unless there is no other alternative. (The recent spate of bash exploits is due to the shell internally evaling the contents of environment variables without verifying their contents first). In this case, you seem to be in complete control for the variables involved, but you can iterate over the variable values without using eval.
#!/bin/sh
enable0=1
enable1=1
for port_enabled in "$enable0" "$enable1"; do
if [ "$port_enabled" -eq 1 ]; then
# do some stuff
fi
done

Linux : From where the value will be fed to the linux script file

I am working on a existing build script where the script file contains these (Some part )
As part of the build process , i need to run this script file .
#!/bin/sh
if [ -z $1 ]; then
help
elif [ $1 == 'test' ]; then
test
Could anybody please let me know what does this mean and form where does the value will be fed from ??
If the first argument ($1) is "empty" then call help otherwise if it's "CheckIn" then call CheckIn.
This is very basic stuff, you should read the manual page.
Actually this script says:
If the script is called without arguments, then call function help
Else if the first argument passed to the script is CheckIn then call function CheckIn
An example could be:
#!/bin/sh
CheckIn() {
echo "CheckIn called!!";
}
help () {
echo "Help called!!";
}
if [ -z $1 ]; then
help
elif [ $1 == 'CheckIn' ]; then
CheckIn
fi
So if this script is named, i.e test.sh the output of running it would be:
sh test.sh
output: Help called!!
sh test.sh CheckIn
output: CheckIn called!!
Finally please note that in your logged-in user shell there can be built in scripts named help and CheckIn. In this case these scripts/commands will be called and there is no need for functions named help/CheckIn to be present.
You should call the script with an argument:
/path/to/script.sh argument
If there are more arguments, they are referred to as $1, $2, ...
$1 refers to the first argument that is passed to your script on the command line. For example, if your script is called myscript.sh and you run it with myscript.sh foo, then $1 is foo.
if [ -z $1 ] is an if-statement which checks if the length of $1 is zero. If so, it runs the help function which must be defined (or sourced) somewhere above this point in your script.
elif [ $1 == 'CheckIn' ] checks whether $1 is CheckIn and if so, runs the CheckIn function.
See:
Bash Guide for Beginners: Introduction to if

How to properly handle wildcard expansion in a bash shell script?

#!/bin/bash
hello()
{
SRC=$1
DEST=$2
for IP in `cat /opt/ankit/configs/machine.configs` ; do
echo $SRC | grep '*' > /dev/null
if test `echo $?` -eq 0 ; then
for STAR in $SRC ; do
echo -en "$IP"
echo -en "\n\t ARG1=$STAR ARG2=$2\n\n"
done
else
echo -en "$IP"
echo -en "\n\t ARG1=$SRC ARG2=$DEST\n\n"
fi
done
}
hello $1 $2
The above is the shell script which I provide source (SRC) & desitnation (DEST) path. It worked fine when I did not put in a SRC path with wild card ''. When I run this shell script and give ''.pdf or '*'as follows:
root#ankit1:~/as_prac# ./test.sh /home/dev/Examples/*.pdf /ankit_test/as
I get the following output:
192.168.1.6
ARG1=/home/dev/Examples/case_Contact.pdf ARG2=/home/dev/Examples/case_howard_county_library.pdf
The DEST is /ankit_test/as but DEST also get manupulated due to '*'. The expected answer is
ARG1=/home/dev/Examples/case_Contact.pdf ARG2=/ankit_test/as
So, if you understand what I am trying to do, please help me out to solve this BUG.
I'll be grateful to you.
Thanks in advance!!!
I need to know exactly how I use '*.pdf' in my program one by one without disturbing DEST.
Your script needs more work.
Even after escaping the wildcard, you won't get your expected answer. You will get:
ARG1=/home/dev/Examples/*.pdf ARG2=/ankit__test/as
Try the following instead:
for IP in `cat /opt/ankit/configs/machine.configs`
do
for i in $SRC
do
echo -en "$IP"
echo -en "\n\t ARG1=$i ARG2=$DEST\n\n"
done
done
Run it like this:
root#ankit1:~/as_prac# ./test.sh "/home/dev/Examples/*.pdf" /ankit__test/as
The shell will expand wildcards unless you escape them, so for example if you have
$ ls
one.pdf two.pdf three.pdf
and run your script as
./test.sh *.pdf /ankit__test/as
it will be the same as
./test.sh one.pdf two.pdf three.pdf /ankit__test/as
which is not what you expect. Doing
./test.sh \*.pdf /ankit__test/as
should work.
If you can, change the order of the parameters passed to your shell script as follows:
./test.sh /ankit_test/as /home/dev/Examples/*.pdf
That would make your life a lot easier since the variable part moves to the end of the line. Then, the following script will do what you want:
#!/bin/bash
hello()
{
SRC=$1
DEST=$2
for IP in `cat /opt/ankit/configs/machine.configs` ; do
echo -en "$IP"
echo -en "\n\t ARG1=$SRC ARG2=$DEST\n\n"
done
}
arg2=$1
shift
while [[ "$1" != "" ]] ; do
hello $1 $arg2
shift
done
You are also missing a final "done" to close your outer for loop.
OK, this appears to do what you want:
#!/bin/bash
hello() {
SRC=$1
DEST=$2
while read IP ; do
for FILE in $SRC; do
echo -e "$IP"
echo -e "\tARG1=$FILE ARG2=$DEST\n"
done
done < /tmp/machine.configs
}
hello "$1" $2
You still need to escape any wildcard characters when you invoke the script
The double quotes are necessary when you invoke the hello function, otherwise the mere fact of evaluating $1 causes the wildcard to be expanded, but we don't want that to happen until $SRC is assigned in the function
Here's what I came up with:
#!/bin/bash
hello()
{
# DEST will contain the last argument
eval DEST=\$$#
while [ $1 != $DEST ]; do
SRC=$1
for IP in `cat /opt/ankit/configs/machine.configs`; do
echo -en "$IP"
echo -en "\n\t ARG1=$SRC ARG2=$DEST\n\n"
done
shift || break
done
}
hello $*
Instead of passing only two parameters to the hello() function, we'll pass in all the arguments that the script got.
Inside the hello() function, we first assign the final argument to the DEST var. Then we loop through all of the arguments, assigning each one to SRC, and run whatever commands we want using the SRC and DEST arguments. Note that you may want to put quotation marks around $SRC and $DEST in case they contain spaces. We stop looping when SRC is the same as DEST because that means we've hit the final argument (the destination).
For multiple input files using a wildcard such as *.txt, I found this to work perfectly, no escaping required. It should work just like a native bash app like "ls" or "rm." This was not documented just about anywhere so since I spent a better part of 3 days trying to figure it out I decided I should post it for future readers.
Directory contains the following files (output of ls)
file1.txt file2.txt file3.txt
Run script like
$ ./script.sh *.txt
Or even like
$ ./script.sh file{1..3}.txt
The script
#!/bin/bash
# store default IFS, we need to temporarily change this
sfi=$IFS
#set IFS to $'\n\' - new line
IFS=$'\n'
if [[ -z $# ]]
then
echo "Error: Missing required argument"
echo
exit 1
fi
# Put the file glob into an array
file=("$#")
# Now loop through them
for (( i=0 ; i < ${#file[*]} ; i++ ));
do
if [ -w ${file[$i]} ]; then
echo ${file[$i]} " writable"
else
echo ${file[$i]} " NOT writable"
fi
done
# Reset IFS to its default value
IFS=$sfi
The output
file1.txt writable
file2.txt writable
file3.txt writable
The key was switching the IFS (Internal Field Separator) temporarily. You have to be sure to store this before switching and then switch it back when you are done with it as demonstrated above.
Now you have a list of expanded files (with spaces escaped) in the file[] array which you can then loop through. I like this solution the best, easiest to program for and easiest for the users.
There's no need to spawn a shell to look at the $? variable, you can evaluate it directly.
It should just be:
if [ $? -eq 0 ]; then
You're running
./test.sh /home/dev/Examples/*.pdf /ankit_test/as
and your interactive shell is expanding the wildcard before the script gets it. You just need to quote the first argument when you launch it, as in
./test.sh "/home/dev/Examples/*.pdf" /ankit_test/as
and then, in your script, quote "$SRC" anywhere where you literally want the things with wildcards (ie, when you do echo $SRC, instead use echo "$SRC") and leave it unquoted when you want the wildcards expanded. Basically, always put quotes around things which might contain shell metacharacters unless you want the metacharacters interpreted. :)

Resources