Bash Files with parameters

For discussions about programming, programming questions/advice, and projects that don't really have anything to do with Puppy.
Message
Author
User avatar
suttiwit123
Posts: 9
Joined: Sat 04 Jun 2011, 07:08

Bash Files with parameters

#1 Post by suttiwit123 »

How do I make a bash file with parameters on it like:

Code: Select all

myprogram --test
:?:
[b]Suttiwit Sukpinit[/b]
A curious Puppy Linux user.
And a puppy linux lover.

User avatar
GustavoYz
Posts: 883
Joined: Wed 07 Jul 2010, 05:11
Location: .ar

Re: Bash Files with parameters

#2 Post by GustavoYz »

suttiwit123 wrote:How do I make a bash file with parameters on it like:

Code: Select all

myprogram --test
:?:
See fourth Bruce's post here: http://www.murga-linux.com/puppy/viewto ... 1&start=30.

Bruce B

#3 Post by Bruce B »

Here is one way which is commonly used.

Code: Select all

#!/bin/bash

if [ "$1" = "--test" ] ; then
    echo one command
    echo second command
fi
~

User avatar
Karl Godt
Posts: 4199
Joined: Sun 20 Jun 2010, 13:52
Location: Kiel,Germany

#4 Post by Karl Godt »

Code: Select all

j=0
for i in `seq 1 $#` ; do
j=$((j+1))
PARAM=`echo "$*" | cut -f $i -d ' '`
echo '$'$j' ='"$PARAM"
PARAM[$j]="$PARAM"
echo ${PARAM[$j]}
done
for i in $(seq 1 $j) ; do
case ${PARAM[$i]} in
--test) do_test_function ;;
--help) do_help_function ;;
*) do_help_function ;;
esac
done 
Note : parts of this code only work in bash , not in ash .

see also
What is "$@"

Bruce B

#5 Post by Bruce B »

Karl,

[redacted]

Will you please for the benefit of all reading explain every single line of code including the function calls and what functions and arrays are?


Bruce

~
Last edited by Bruce B on Sun 03 Jul 2011, 19:45, edited 1 time in total.

User avatar
suttiwit123
Posts: 9
Joined: Sat 04 Jun 2011, 07:08

Thanks

#6 Post by suttiwit123 »

Thanks for all your posts, I will try and see if it works.

Bruce B

Re: Thanks

#7 Post by Bruce B »

suttiwit123 wrote:Thanks for all your posts, I will try and see if it works.
My code will work as presented. Any questions about it and I'll explain.

The code Karl presented will not work as posted. I don't imagine a bash beginner could read and understand the lines.

I hope Karl will post a working script. If the lines are advanced level scripting and they surely are, then I hope Karl help out and explain what he is doing with the lines and how they work.

I sincerely apologize to Karl for my earlier harsh remarks and I removed them for his and everyone's benefit.


~

A personal explanation for everyone, here is the help request.
suttiwit123 wrote:How do I make a bash file with parameters on it like:

Code: Select all

myprogram --test
:?:
This is a very straightforward question posed by suttiwit123. It is a beginner's level question. I have heart for teaching beginners. I have demonstrated my heart for this subject in hundreds of posts.

People learn bash on a gradient. I believe we should understand this and meet them where they are at. In practical application this means give examples and explanations equivalent to their present level of understanding.

~

User avatar
Moose On The Loose
Posts: 965
Joined: Thu 24 Feb 2011, 14:54

#8 Post by Moose On The Loose »

I'm going to add some comments. I predict that over 50% of them
will be correct.

Code: Select all


# This is a comment
# Make a variable called "j" that is just the text "0"
# Although it looks like a number in other places it can be used
# as just a string
j=0

# This is not the best way to code this but it works as an example
# The "$#" thing returns the number of "words"  as parameters on
# the command line
# "seq 1 5" returns the numbers 1 2 3 4 5
# "for i in 1 2 3 4 5" loops 5 times with "i" being each value
for i in `seq 1 $#` ; do
# The next line looks incorrect
#  j=$(( $j + 1 )) is what I would do
#  "$j" just inserts the text that is currently in "j" "0" the first time
#  "$(( 0 + 1 ))"  does integer math resulting in "1" as a string
#  
j=$((j+1))
#  This next bit is very dreadful but here goes
#  "$*" is just the list of all the parameters as a string
#  echoing it through "|" means it goes to the routine called "cut"
#  cut takes a "-f1" argument to mean the first field in the input
#  the "-d ' '" makes the fields divided by spaces
PARAM=`echo "$*" | cut -f $i -d ' '`
#  This line just composes a message to print
echo '$'$j' ='"$PARAM"
# This makes an array called "PARAM" with each element a string
PARAM[$j]="$PARAM"
# This is how you can get elements from an array variable.
#  For most cases ${XXX} and $XXX work the same but for
# arrays you need to use the "{}" to keep bash from trying to find
# the non-array version.
# echo "$PARAM[1]"     will result in "[1]" because "$PARAM"
# is not found
echo ${PARAM[$j]}
# End this loop
done
# The rest should not be sort of obvious now
for i in $(seq 1 $j) ; do
case ${PARAM[$i]} in
--test) do_test_function ;;
--help) do_help_function ;;
*) do_help_function ;;
esac
done 
Note : parts of this code only work in bash , not in ash .

see also
What is "$@"

jpeps
Posts: 3179
Joined: Sat 31 May 2008, 19:00

#9 Post by jpeps »

Moose On The Loose wrote: # The rest should not be sort of obvious now
:D Thanks for explaining the interesting code.

Bruce B

#10 Post by Bruce B »

Thanks Moose On The Loose

This code goes beyond the scope of the question. It also does something the above code doesn't do, it works as is. It is complete and ready to use.

It also makes use of the function calls. For the first time in this topic, what a function might look like is demonstrated.

~

Code: Select all

#!/bin/bash

help_function() {

    echo "this is the help function"

}

test_function() {

    echo "this is the test function"

}

case $1 in 

    --test) test_function ;;
         *) help_function ;;

esac 
~

User avatar
GatorDog
Posts: 138
Joined: Tue 12 Sep 2006, 16:43

#11 Post by GatorDog »

Can I get in on the Dunigun?
I've used this coding in several programs to retrieve
command line parameters.

Code: Select all

#!/bin/bash
#
# mybashprg v1.0 
# This is just an example of retrieving command line parameters.

# usage:  mybashprg -pn picture_name.png -t 10 [-od output_dir] -test ...

# If you want to set your command line up with this type
# of format, this method of reading the parameters works 
# pretty slick.
#
# Using the "for" and the "case" statements as follows
# lets you enter the parameters in any order.

# -------------------------------------------------------
# This checks to make sure that at least one command line 
# parameter is passed.
# "$#" is the number of command line parameters entered.

if [ $# = 0 ] ; then
   echo "-------------------------------------------"
   echo "       No options / parameters passed   "
   echo "-------------------------------------------"
   exit 1
fi

# -------------------------------------------------------
# Read command line parameters.
#
# The "for arg" will step through each parameter
# on the command line.
#
# The "shift" causes the current $1 to be dropped
# from the arg list, and the remaining parameters
# shifted to the front of the list. So the old $2 
# becomes $1; and so forth.
#
# When there is a "match" in the case statement,
# the block of code following the match is executed.
# The double semi-colons ";;" marks the end of 
# the block of code.
#
# By pre-setting variables, they have a default value 
# in case they aren't assigned on the command line.

display_time=10
output_dir="~/mysaveDir"
crossfade=10

# -------------------------------------------------------

for arg
do
  case "$arg" in
  -pn) shift; picture_name="$1"        ; shift ;;
  -od) shift; output_dir="$1"          ; shift ;;
   -t) shift; display_time="$1"        ; shift ;;
  -ed) shift; effect_delay="$1"        ; shift ;;
   -b) shift; background="$1"          ; shift ;;
   -s) shift; subtitle="$1"            ; shift ;;
   -a) shift; audio_file="$1"          ; shift ;;
   -c) shift; crossfade="$1"           ; shift ;;
  -et) shift; effect_type="$1"         ; shift ;;
-test) echo "I no take stinking test"  ; exit 1;;  
  esac
done

# Here the "block of code" is simply assigning values to
# variable names. But any number of bash commands or 
# function calls could be included in the block of code.
#
# -------------------------------------------------------
# Examples of the case statement are usually written like this:

case $arg in
      A)
       echo "You passed A"
       ;;
  [0-9])
       echo "You passed a number"
       ;;
    Gas)
       ls -la
       du
       cat *.txt
       find / -type d -name Gas -print
       ;;
esac

# Depending on your needs, I think the first example of the
# case statement is easier to decipher and maintain.
That's my story and I'm sticking to it. :)
Rod

jpeps
Posts: 3179
Joined: Sat 31 May 2008, 19:00

#12 Post by jpeps »

GatorDog wrote:
That's my story and I'm sticking to it. :)
Rod
Okay, but if there's a non-matching arg preceding the others, the shifts will be off.

jpeps
Posts: 3179
Joined: Sat 31 May 2008, 19:00

#13 Post by jpeps »

Adding a default "*)" for unmatched args exits to prevent shifting errors:

Code: Select all

for arg
do
  case "$arg" in
  -pn) shift; picture_name="$1"        ; shift ;;
  -od) shift; output_dir="$1"          ; shift ;;
   -t) shift; display_time="$1"        ; shift ;;
   *) X=`echo "$1" | grep -oe "-pn" -oe "-od" -oe "-t"`
               [ "$X" ] || exit ;; 
  esac
done


Bruce B

#14 Post by Bruce B »

jpeps wrote:Adding a default "*)" for unmatched args exits to prevent shifting errors:

Code: Select all

for arg
do
  case "$arg" in
  -pn) shift; picture_name="$1"        ; shift ;;
  -od) shift; output_dir="$1"          ; shift ;;
   -t) shift; display_time="$1"        ; shift ;;
   *) X=`echo "$1" | grep -oe "-pn" -oe "-od" -oe "-t"`
               [ "$X" ] || exit ;; 
  esac
done

I think its a good template as a way of demonstrating how to pass certain types of arguments. And you made some good catches and an important point.

I also think you overlooked something. Maybe in copy and paste. I'm stuck figuring out how line one works.

Please go easy on me in your reply. Many people are already planning my demise.

~

jpeps
Posts: 3179
Joined: Sat 31 May 2008, 19:00

#15 Post by jpeps »

Bruce B wrote:
I also think you overlooked something. Maybe in copy and paste. I'm stuck figuring out how line one works.
Hi Bruce,
"for arg" captures arguments. I guess it's clearer to see with an "echo" command, so:

( I'm assuming you wouldn't be commenting on my omission of "#!/bin/sh", since I'm editing a snippet of someone else's code directly above).

Code: Select all

   
#!/bin/sh
 
for arg
do
  case "$arg" in
  -pn) shift; echo "picture_name="$1""        ; shift ;;
  -od) shift; echo "output_dir="$1""          ; shift ;;
   -t) shift; echo "display_time="$1""        ; shift ;;
   *) X=`echo "$1" | grep -oe "-pn" -oe "-od" -oe "-t"`
               [ "$X" ] || exit ;;
  esac
done

Code: Select all

/mnt/sda2/Desktop $ ./testit -pn SomeName  -od /mnt/sda2 -t 8:30PM  
picture_name=SomeName
output_dir=/mnt/sda2
display_time=8:30PM

/mnt/sda2/Desktop $ ./testit -pn SomeName UNMATCHED -od/mnt/sda2 -t 8:30PM  
picture_name=SomeName

Bruce B

#16 Post by Bruce B »

jpeps,

Incredible!

I hate to admit it, but I didn't know arg was a bash keyword. Not knowing it, I looked at the for arg and went, huh?

Thanks

Bruce

~

User avatar
Moose On The Loose
Posts: 965
Joined: Thu 24 Feb 2011, 14:54

#17 Post by Moose On The Loose »

[quote="GatorDog"]

Code: Select all

display_time=10
output_dir="~/mysaveDir"
crossfade=10
I usually set all the variables to my defaults before parsing the parameters just in case the user may have a variable by that same name.

Also:

Code: Select all

# Run the parameter through cut and use the -b1 to take just byte 1
FIRST=`echo "$1" | cut -b1`
# If it starts with a "-" then do as normal
if [[ "$FIRST" == "-" ]] ; then
  case
    ...
    esac
else
  # Without a "-" is assumed to be the file to mung
  FILENAME=$1
  shift
  fi

User avatar
GatorDog
Posts: 138
Joined: Tue 12 Sep 2006, 16:43

#18 Post by GatorDog »

To keep my example as simple as possible I left this piece
of code out. It simply checks if there are an even number
of parameters on the command line. That is, each -option has
a matching parameter.

So with the approach I showed, it checks if there are any
parameters at all, and then that they are matched.

Code: Select all

# Check if each option has a parameter passed with it.
# (ie check for even number of parameters on cmd line)
#
if [ $(( $# % 2 )) != 0 ] ; then
   echo ; echo
   echo "-----------------------------------------------"
   echo "Check your command line. Option(s) specified "
   echo "without matching parameter(s) (or vice-versa ;^)"
   echo "-----------------------------------------------"
   echo $0 $*
   echo "-------------------"
   exit
fi
What this piece of code does: $(( $# % 2 )) != 0
$# Is the number of command line arguments.

% This does division and returns the remainder. (Sometimes called modulo)
In this case, divide by 2. If the remainder
isn't zero, it's not an even number.

$(( )) Evaluate the enclosed expression and use it here.

!= Not equal to.


echo $0 $* This prints out the command and parameters you entered.

$0 Is the command name, in this case the name of the bash
script you are running.

$* Is all the parameters in a single string.
These checks aren't bulletproof. But what they don't catch
borderlines on intentional error. If you need more certainty, you
could do a lookup of each option. Say you're running a video processing
script that takes several hours to run. It's a bummer to get to the
end of the process and find you mis-fired on an option ;)

At the risk of being obvious, I'll mention that the "case" statement
used in this way also works well for parameters without the use
of "-xx" options.

rod

User avatar
technosaurus
Posts: 4853
Joined: Mon 19 May 2008, 01:24
Location: Blue Springs, MO
Contact:

#19 Post by technosaurus »

The most versatile method is probably: while $1 combined with case statements
You just evaluate each arg 1 at time by using shift ... add a second shift for 2 part args.

this is a wrapper template for a defaultfilemanager (to replace /usr/local/bin/rox)

Code: Select all

#!/bin/sh
#template to wrap other filemanagers for use in place of ROX-Filer

USERF=""

while ([ $1 ]) do
case $1 in
  -b)ARGS=$ARGS" "$1" ";shift;USERF=1;;
  --border=*)ARGS=$ARGS" "$1;USERF=1;;
  -B)ARGS=$ARGS" "$1" "$2;shift;USERF=1;;
  --bottom=*)ARGS=$ARGS" "$1;USERF=1;;
  -c)ARGS=$ARGS" "$1" "$2;shift;USERF=1;;
  --client-id=*)ARGS=$ARGS" "$1;USERF=1;;
  -d)shift;ARGS=$ARGS" "$1;; #as far as I can tell -d is useless or should use dirname
  --dir=*)ARGS=$ARGS" "`echo $1 |cut -d "=" -f2`;;
  -D)ARGS=$ARGS" "$1" "$2;shift;USERF=1;;
  --close=*)ARGS=$ARGS" "$1;USERF=1;;
  -h|--help)ARGS=$ARGS" "$1;;
  -l)ARGS=$ARGS" "$1" "$2;shift;USERF=1;;
  --left=*)ARGS=$ARGS" "$1;USERF=1;;
  -m)ARGS=$ARGS" "$1" "$2;shift;USERF=1;; #alternatively use file or xdg-*
  --mime-type=*)ARGS=$ARGS" "$1;USERF=1;;
  -n|--new)ARGS=$ARGS" "$1;USERF=1;;
  -p)ARGS=$ARGS" "$1" "$2;shift;USERF=1;; #PCMan-FM has its own "pinboard"
  --pinboard=*)ARGS=$ARGS" "$1;USERF=1;;
  -r)ARGS=$ARGS" "$1" "$2;shift;USERF=1;;
  --right=*)ARGS=$ARGS" "$1;USERF=1;;
  -R)ARGS=$ARGS" "$1;USERF=1;;
  --RPC)ARGS=$ARGS" "$1;USERF=1;;
  -s)ARGS=$ARGS" "$1" "$2;shift;USERF=1;;
  --show=*)ARGS=$ARGS" "$1;USERF=1;;
  -S)ARGS=$ARGS" "$1;USERF=1;;
  --rox-session)ARGS=$ARGS" "$1;USERF=1;;
  -t)ARGS=$ARGS" "$1" "$2;shift;USERF=1;;
  --top=*)ARGS=$ARGS" "$1;USERF=1;;
  -u)ARGS=$ARGS" "$1" "$2;shift;USERF=1;;
  --user)ARGS=$ARGS" "$1;USERF=1;;
  -U)ARGS=$ARGS" "$1" "$2;shift;USERF=1;; #alternatively use defaultbrowser ...
  --url=*)ARGS=$ARGS" "$1;USERF=1;;
  -v|--version)ARGS=$ARGS" "$1;; #most file managers have this switch
  -x)ARGS=$ARGS" "$1" "$2;shift;USERF=1;;
  --examine=*)ARGS=$ARGS" "$1;USERF=1;;
  *)ARGS=$ARGS" "$1;; #not a rox option, so probably ok to just pass it.
esac
shift
done
[ $USERF ] && /usr/local/apps/ROX-Filer/ROX-Filer $ARGS || defaultfilemanager $ARGS
Check out my [url=https://github.com/technosaurus]github repositories[/url]. I may eventually get around to updating my [url=http://bashismal.blogspot.com]blogspot[/url].

Bruce B

#20 Post by Bruce B »

Come on guys. Some of us only have a 95 IQ

I'm afraid I'll have to get married to technosaurus last post. A lifetime anyway to figure what he is saying. That's sort of like marriage isn't it?


~

Post Reply