#( in shell case statement - bash

I am trying to learn how a configure script in the gimp source code works by reading and trying to understand what each statement does. I am at the beginning of the file and came across a case statement to that sets posix to on. I cant undstand what the "#(" means. Is it a comment or something else?
case `(set -o) 2>/dev/null` in #(
*posix*) :
set -o posix ;; #(
*) :
;;
esac

Yes, anything that is after # in a line is considered as a comment. See this for the switch case syntax.

Related

Is it good practice to use if block in while case statement in getopts

#!/bin/bash
while getopts ':s:r:' c
do
case $c in
s) if [ "$OPTARG" -ge 0 ]; then
echo "value is $OPTARG"
else
echo "enter value greater than 0";;
r) echo "something";;
esac
done
Or should i remove if block from case statment and put it after the case while loop?
What is the good practice? Any documentation I can refer?
Is it good practice to use if block in while case statement in getopts
Subjectively: sure, why not. Except you are missing an fi.
should i remove if block from case statement and put it after the case while loop?
Subjectively: it would be preferred. For me.
What is the good practice?
I believe a common way is to set only variables within options parsing, then write sanity checks after options are parsed. But it's not an universal way of doing it and it may be preferable for some applications to do some logic while parsing options. As always, the ultimate answer is: "it depends".
Any documentation I can refer?
Linux man pages and POSIX Programmers manual. man 1p getopts has an excellent example that shows how to set flags first and then write sanity checks.

Creating a 'yes' or 'no' menu in UNIX bash shell scripting

I'm currently writing a script, and at one point I want it check if a file already exists. If the file doesn't exist, then it should do nothing. However, if the file does exist, I want a 'y' or 'n' (yes or no) menu to appear. It should ask "Do you want to overwrite this file?".
So far I've tried writing something similar to this. Take into account that before this a function called:
therestore
exists. I want this function to occur if they type "y". Anyway, this is what I tried:
If [ -f directorypathANDfilename ] ; then
read -p "A file with the same name exists, Overwrite it? Type y/n?" yesorno
case $yesorno in
y*) therestore ;;
n*) echo "File has not been restored" ;;
esac
fi
For some reason though, the menu always pops up, even if the file DOESN'T exist and it doesn't restore it properly if I type yes! (But I know the "therestore" function works fine, because I've tested it plenty of times).
Apologies for the long-winded question. If you need any more details let me know - thanks in advance!
Does your script even run? Doesn't look like valid bash-script to me. If is not a valid keyword, but if is. Also, tests go inside angle-brackets [ ], those are not optional. Moreover you forgot the closing fi.
And another thing, it's not quite clear to me what you're testing for. Is directorypathANDfilename a variable? In that case you have to reference it with the $.
The snippet would probably work better like this:
#!/bin/bash
if [ -f "$directorypathANDfilename" ] ; then
read -p "A file with the same name exists, Overwrite it? Type y/n?" yesorno
case "$yesorno" in
y*) therestore ;;
n*) echo "File has not been restored" ;;
esac
fi

Writing a shell conditional for extensions

I'm writing a quick shell script to build and execute my programs in one fell swoop.
I've gotten that part down, but I'd like to include a little if/else to catch bad extensions - if it's not an .adb (it's an Ada script), it won't let the rest of the program execute.
My two-part question is:
How do I grab just the extension? Or is it easier to just say *.adb?
What would the if/else statement look like? I have limited experience in Bash so I understand that's a pretty bad question.
Thanks!
There are ways to extract the extension, but you don't really need to:
if [[ $filename == *.adb ]] ; then
. . . # this code is run if $filename ends in .adb
else
. . . # this code is run otherwise
fi
(The trouble with extracting the extension is that you'd have to define what you mean by "extension". What is the extension of a file named foo? How about a file named report.2012.01.29? So general-purpose extension-extracting code is tricky, and not worth it if your goal is just to confirm that file has a specific extension.)
There are multiple ways to do it. Which is best depends in part on what the subsequent operations will be.
Given a variable $file, you might want to test what the extension is. In that case, you probably do best with:
extn=${file##*.}
This deletes everything up to the last dot in the name, slashes and all, leaving you with adb if the file name was adafile.adb.
If, on the other hand, you want to do different things depending on the extension, you might use:
case "$file" in
(*.adb) ...do things with .adb files;;
(*.pqr) ...do things with .pqr files;;
(*) ...cover the rest - maybe an error;;
esac
If you want the name without the extension, you can do things the more traditional way with:
base=$(basename $file .adb)
path=$(dirname $file)
The basename command gives you the last component of the file name with the extension .adb stripped off. The dirname command gives you the path leading to the last component of the file name, defaulting to . (the current directory) if there is no specified path.
The more recent way to do those last two operations is:
base=${file##/}
path=${file%/*}
The advantage of these is that they are built-in operations that do not invoke a separate executable, so they are quicker. The disadvantage of the built-ins is that if you have a name that ends with a slash, the built-in treats it as significant but the command does not (and the command is probably giving you the more desirable behaviour, unless you want to argue GIGO).
There are other techniques available too. The expr command is an old, rather heavy-weight mechanism that would not normally be used (but it is very standard). There may be other techniques using the (( ... )), $(( ... )) and [[ ... ]] operators to evaluate various sorts of expression.
To get just the extension from the file path and name, use parameter expansion:
${filename##*.} # deletes everything to the last dot
To compare it with the string adb, just do
if [[ ${filename##*.} != adb ]] ; then
echo Invalid extension at "$filename".
exit 1
fi
or, using 'else`:
if [[ ${filename##*.} != adb ]] ; then
echo Invalid extension at "$filename".
else
# Run the script...
fi
Extension:
fileext=`echo $filename | sed 's_.*\.__'`
Test
if [[ x"${fileext}" = "xadb" ]] ; then
#do something
fi

How to parametrize verbosity of debug output (BASH)?

During the process of writing a script, I will use the command's output in varying ways, and to different degrees - in order to troubleshoot the task at hand.. For example, in this snippet, which reads an Application's icon resource and returns whether or not it has the typical .icns extension...
icns=`defaults read /$application/Contents/Info CFBundleIconFile`
if ! [[ $icns =~ ^(.*)(.icns)$ ]]; then
echo -e $icns "is NOT OK YOU IDIOT! **** You need to add .icns to "$icns"."
else
echo -e $icns "\t Homey, it's cool. That shits got its .icns, proper."
fi
Inevitably, as each bug is squashed, and the stdout starts relating more to the actual function vs. the debugging process, this feedback is usually either commented out, silenced, or deleted - for obvious reasons.
However, if one wanted to provide a simple option - either hardcoded, or passed as a parameter, to optionally show some, all, or none of "this kind" of message at runtime - what is the best way to provide that simple functionality? I am looking to basically duplicate the functionality of set -x but instead of a line-by rundown, it would only print the notifications that I had architected specificically.
It seems excessive to replace each and every echo with an if that checks for a debug=1|0, yet I've been unable to find a concise explanation of how to implement a getopts/getopt scheme (never can remember which one is the built-in), etc. in my own scripts. This little expression seemed promising, but there is very little documentation re: 2>$1 out there (although I'm sure this is key to this puzzle)
[ $DBG ] && DEBUG="" || DEBUG='</dev/null'
check_errs() {
# Parameter 1 is the return code Para. 2 is text to display on failure.
if [ "${1}" -ne "0" ]; then
echo "ERROR # ${1} : ${2}"
else
echo "SUCESSS "
fi }
Any concise and reusable tricks to this trade would be welcomed, and if I'm totally missing the boat, or if it was a snake, and it would be biting me - I apologize.
One easy trick is to simply replace your "logging" echo comamnd by a variable, i.e.
TRACE=:
if test "$1" = "-v"; then
TRACE=echo
shift
fi
$TRACE "You passed the -v option"
You can have any number of these for different types of messages if you wish so.
you may check a common open source trace library with support for bash.
http://sourceforge.net/projects/utalm/
https://github.com/ArnoCan/utalm
WKR
Arno-Can Uestuensoez

Parsing input options containing whitespaces in a bash script

I have a bash script parsing input option with a block of code like the following
for WORD in "$#" ; do
case $WORD in
--*) true ;
case $WORD in
--opt1=*)
OPT1=${WORD/--opt1=/}
shift ;;
--opt2=*)
OPT2=${WORD/--opt2=/}
shift ;;
*) echo "Unrecognized argument $WORD"
;;
esac ;;
*) echo "Option $WORD not starting with double dash."
;;
esac
done
The script is invoked by another parent program which creates the entire command line.
The output created by this parent program looks like
./childscript.sh "--opt1=value1 --opt2=value2"
The problems appear when the generated line looks like
./childscript.sh "--opt1='value11 value12' --opt2=value2"
The scripts complains saying
Option value12 not starting with double dash.
How can I modify the child bash code to make it understand white spaces inside the input options?
I don't think the generated line is what you think it is.
Your code works completely fine for me if I simply invoke it directly. With added echoes to check that the values are being stored in the right place:
$ ./child.sh --opt1='v1 v2' --opt2='v3 v4'
OPT1='v1 v2'
OPT2='v3 v4'
You should be able to confirm this. Your problem isn't in making the child script accept arguments like these, it's in having the parent script invoke it correctly.
And by the way, you don't actually want to run something like this:
./childscript.sh "--opt1=value1 --opt2=value2"
That will cause that entire string (--opt1=value1 --opt2=value2) to be read as a single argument. I suspect that you haven't told us the full story on the way the parent script is calling this. If you show us those details, we can probably help out more - or maybe this is enough of a hint.

Resources