Defining flag parameters in bash - linux

I wrote bash script, in which I included some logging, to see what is going on in each step of exectuion.
Now, I can split those logs in debuggin info and user info (that something has completed, etc.). So I'd like to have some flag parameter, like --verbose, which I saw in some other bash functions to enable full logging and usage was like:
some_function --verbose
or
some_function -v
I call it flag parameters and don't know what's the right name, thus I can't find anything useful.
How to define such parameters for bash script?

Case suits better for this
while [[ "$#" ]]; do
case "$1" in
-v|--verbose) verbose="true";;
esac
shift
done
Same can be done in function, but note that in this case it'll process parameters passed to function some_function -v.
some_function () {
while [[ "$#" ]]; do
case "$1" in
-v|--verbose) verbose="true";;
esac
shift
done
}
Then somewhere in script you can check if verbose is set
[[ "$verbose" ]] && echo "verbose mode" || echo "silent mode"

For now, I used workaround and take it as normal positional parameter (as $n). To be exact, I have list of four parameters, so I collect this flag like this:
verbose=$4
if [ ! "$verbose" == "--verbose" ]; then
verbose=""
fi
So, if parameter is not matching a flag, then I leave it empty and if I want to use it, I just compare it against empty string.
There's one big disadvantage for me though: it has to be at 4th position in parameters list.

Related

for what reason do i keep getting "bsd syntax error" when i run ps -f in shell script?

so, i'm a beginner to Shell, i need to write a script that, given a numeric argument, will display info about the processes with the entered numbers (by user ofc), given a non numeric argument, if it's "--help" it will display help (which i really don't know how to do), and if it's something else, it gives info about processes having that argument as names ( for ex, "./script bash" will display info about the bash process) , here is what i've done so far:
#!/bin/sh
case $arg in
$num )
a=""
for arg in $#
do
a="${a} $arg"
done
printf "$(ps -f ${a})\n"
;;
*)
p=$(pidof ${arg})
printf "$(ps -f ${p})"
;;
esac
so, the numeric argument part works, but the other one doesn't, whenever i enter a non numeric name i get that "BSD syntax" error message and i really don't know what to do.
thank you to everyone willing to help me out.

linux device which don't allow to write on it

I have a script which write some warnings to separate file (it's name is passed as an argument). I want to make this script fail if there is a warning.
So, I need to pass some file name which must raise an error if someone try to write there.
I want to use some more or less idiomatic name (to include it to man page of my script).
So, let say my script is
# myScript.sh
echo "Hello" > $1
If I call it with
./myScript.sh /dev/stdin
it is not fail because /dev/stdin is not read-only device (surprisingly!)
After
./myScript.sh /
it is failed, as I want it (because / is a directory, you can't write there). But is is not idiomatic.
Is there some pretty way to do it?
if [ -w "$1" ]
then
echo "$Hello" > "$1" # Mind the double-quotes
fi
is what you're looking for. Below would even be better in case you've only
one argument.
if [ -w "$*" ]
then
echo "$Hello" > "$*" # Mind the double-quotes
fi
$* is used to accommodate nonstandard file names. "$*" combines multiple arguments into a single word. Check [ this ].

Bash how check if an argument is a sed patterns?

I'm working on a bash script that should receive many arguments. those arguments could be simple like:
Myscript [-r] [-l|-u] <dir/file names...>
or it could be just a sed patterns given instead of a simple arguments like:
Myscript <sed pattern> <dir/file names...>
So my question is how to check if the arguments given is a sed patterns and not directory path or filename?
I have done something but it doesn't work for a long path (dir1/dir2/dir)
while [[ $# > 0 ]]
do
key="$1"
case $key in
-ru|-ur)
config_recusive
config_upper
shift
;;
-rl|-lr)
config_recusive
config_lower
shift
;;
-r)
config_recusive
shift
;;
-l)
config_lower
shift
;;
-u)
config_upper
shift
;;
*)
check="^.*/.*/.*$"
if [[ $key =~ $check ]] ; then
config_sed_pattern
else
TARGET=$key
fi
shift
;;
esac
done
To be more clear, here is an example of my problem when I'm trying to run the script like that:
./myscript -u tmp/users/regions
its being confused taking the path (tmp/users/regions) as a sed patterns.
hope that I was clear enough.
waiting for your help :)
thanks
i think you could use try run that script on something
echo a | sed "$pattern" >/dev/null 2>/dev/null
and then check the $?
if [ $? != 0 ]; then # means sed failed
#...
but maybe sed will think it will be its argument too.
btw, handle arguments using getopt will make things easier.
i don't think the way you design argument format is good. maybe you think making arguments in different forms could make your program looks dynamic and awesome. but now you are trying to solve a problem have nothing to do with your serious business and wasting your own time and ours. that's not good. it's better to make things stupid and clear.
in your case, maybe you can add another argument to show it's a sed expression that follows.
YourScript -e <sed expr> <others>
and in such a way, you will have idea about what you have done two weeks later.

Does not work to execute command in double brackets in bash

In an attempt to stay consistent i have tried to use double brackets [[ ]] in all my if statements. I did however get into a problem when i was going to check the return value for a command i wanted to run. After testing several ways of creating an if statement i found that only without brackets could i execute a command.
The following does not work:
if [[ $command ]] ; then
echo "something"
fi
if [[ $(command) ]] ; then
echo "something"
fi
if [[ ${command} ]] ; then
echo "something"
fi
and the code above makes the if loop true even when the command was not run.
since the code above doesnt work with braces it doesnt work to use this either:
[[ $command ]] || echo "failed"
and it doesnt work in a subshell either.
The following works:
if $command ; then
echo "something"
fi
if $(command) ; then
echo "something"
fi
Why doesnt it work to place a command in an if loop with brackets, and why does the if loops above report true when it didnt even run the command ? I'm using bash version 4.1.9. Ive tried this many times and the if loops are just as simple as the ones i typed above, it just checks if a command was run successfully and exits if it wasnt.
The short answer is:
[ and [[ expect an expression.
if expects a command.
Saying:
[[ $(command) ]]
would essentially execute:
[[ -n <command_output> ]]
which may or may not be what you want. On the other hand, saying:
$command && echo something || echo other
would echo something or other based on the return code of the command (0 and non-zero respectively).
Double braces are a shortcut for test. In your examples, what's happening is that you're testing the shell variable $command for existence.
if [[ $PWD ]]; then
echo PWD is set to a value
fi
if [[ $NOT_A_REAL_VAR ]]; then
echo Nope, its not set
fi
In your second example, you're using command substitution to check that command output something on standard output.
if [[ $(echo hi) ]]; then
echo "echo said hi'
fi
if [[ $(true) ]]; then #true is a program that just quits with successful exit status
echo "This shouldn't execute"
fi
Your third example is the same as your first, pretty much. You use the curly braces if you want to group your variables. for example if you want to put an 's' after something.
WORD=Bike
echo "$WORDS" #won't work because "WORDS" isn't a variable
echo "${WORD}S" # will output "BikeS"
Then in your fifth example, you are running the program that is sitting inside command.
So, if you want to test some strings, use [[ ]] or [ ]. If you just want to test the exit status of a program, then don't use those, just use a bare if.
Check man test for details on the braces.
If you're just checking the return value of the command, drop the double brackets.
if $command
then
echo "Command succeeded"
else
echo "Command failed: $!"
fi
The double brackets are a test command. (Well, not really, but their a takeoff of the single square brackets that were an alias to the test command.) In early Bourne shell, you would see things like:
if test -z "$string"
then
echo "This is an empty string"
fi
The square brackets were syntactic sugar:
if [ -z "$string" ]
then
echo "This is an empty string"
fi
So, if you're not doing an actual test, you can eliminate the double or single square brackets.
If you're using square brackets, you should use the double ones and not the single ones because the double ones are a bit more forgiving and can do a bit more:
if [ -z $string ] # No quotes: This will actually fail if string is zero bytes!
if [[ -z $string ]] # This will work despite the lack of quotes

issue in getopt , Unix shell script

Hi can someone fix this issue, i am not able to get outpt.
I am not able to get output of -p.
#!/bin/bash
args=`getopt c:m:p $*`
if [ $? != 0 -o $# == 0 ]
then
echo 'Usage: -c <current-dir> -m <my dir> -p <argument>'
exit 1
fi
set -- $args
for i
do
case "$i" in
-c) shift;CURRDIR=$1;shift;shift ;;
-m) MYDIR=$1;shift;;
-p) ARGVAL=$OPTARG;;
esac
done
echo "CURRDIR = $CURRDIR"
echo "MYDIR = $MYDIR"
echo "ARGVAL = $ARGVAL"
./1.sh -c "def" -m "ref" -p "ref -k ref"
Expected output
output -c = "def"
-m ="ref"
-p ="ref -k ref"
getopt
args=`getopt c:m:p $*`
You need to add a colon after the p to indicate that -p takes an argument. Also you should change $* to "$#" for better handling of spaces.
args=`getopt c:m:p: "$#"`
You are also mixing up getopt and getopts. $OPTARG is a getopts feature. With plain getopt and set you should simply use $2 and then shift off the argument.
-p) ARGVAL=$2; shift 2;;
At this point you've done as good as you can with getopt. Unfortunately it doesn't handle the multi-word argument to -p no matter what you do. For that, we need to use getopts.
getopts
From getopt and getopts:
Easier to use and generally better than getopt, though of course not available in csh-like shells. You shouldn't be using those anyway.
This works rather differently than "getopt". First, because it's a built-in, you usually won't find a separate man page for it, though "help getopts" may give you what you need.
The old "getopt" is called once, and it modifies the environment as we saw above. The builtin "getopts" is called each time you want to process an argument, and it doesn't change the original arguments .
Using getopts is a lot simpler. Your entire loop can be simplified to this:
while getopts c:m:p: flag
do
case "$flag" in
c) CURRDIR=$OPTARG;;
m) MYDIR=$OPTARG;;
p) ARGVAL=$OPTARG;;
esac
done
No shifting needed, you just read $OPTARG each time to get each option's value.

Resources