"`$#': not a valid identifier" error in bash script - bash

I am working on a shell script that will allow for the user to input a file name(s) into the command prompt, and the script will locate the executable file and determine if they could have been executed. However, when I attempt to run it, I get a "line 45 `$#': not a valid identifier" error message.
This is my first script and can't figure out how to address this error. I am hopeful that the rest of the script is ok, but I haven't been able to get past this one error to test it out.
Any help is greatly appreciated!
#!/bin/bash
FINDALL=FALSE
while :
do
case $1 in
-a) FINDALL=TRUE
shift ;;
-h) echo “Usage $0 [-a] commands…”
exit 0 ;;
*) break ;;
esac
done
for $# in $file
do
case $file in
*/*) if [ -x “$file” ]
then
echo $file
elif [ -d “$file” ]
then
echo $file is NOT found
fi
shift ;;
*) FOUND=FALSE ;;
esac
done
case $PATH in
:*) PATH=”.:$PATH” ;;
*::*) PATH=`echo $PATH | sed -e ‘s/::/:.:/g’` ;;
*:) PATH=”$PATH:.” ;;
esac
command=”$1”
IFS=$OLDIFS
IFS=:
set -- $PATH
IFS=$OLDIFS
case $command in
*/*) echo $command ;;
*) FOUND=FALSE
for P
do
if [ ! -d “$P/$command” -a -x “$P/$command” ]
then
FOUND=TRUE
echo $command
break
fi
done
if [ “$FOUND” = FALSE ]
then
echo “Command $command not found in your search path”
fi ;;
esac

Related

How to make it manditory for options to be spaced for bash scripts

I have the following script.
I would like to modify it so that if I were to call temp.sh with both the options, I would have to space them. Ie: A call to the script like temp.sh -fc30 should be invalid, rather it should be temp.sh -f -c 30
ARGS=$(getopt -o c:f -l "charlie:fox" -n "temp.sh" -- "$#");
#bad args
if [ $? -ne 0 ];
then
exit 1
fi
eval set --"$ARGS";
while true; do
case "$1" in
-c|--charlie)
shift;
if [ -n "$1" ]; then
echo "-c =: $1";
shift;
fi
;;
-f|--fox)
shift;
echo "fox used";
;;
--)
shift;
break;
;;
esac
done
Just don't use getopt.
#!/bin/bash
# parse options
while [[ $# -gt 0 ]]; do
case $1 in
-c|--charlie)
echo "$1 = $2"
shift
;;
-f|--fox)
echo "fox used"
;;
--)
shift
break
esac
shift
done
# do script

Search for one command line parameter before the rest

I'm reading my command line parameters using getopt, and I'm reading a configuration file using .:
test.sh:
#!/bin/bash
set -- `getopt C:a:b:c: "$#"`
C="default.cfg"
. $C
while [ $# -gt 0 ]; do
case "$1" in
-a) cfg1="$2"; shift;;
-b) cfg2="$2"; shift;;
-c) cfg3="$2"; shift;;
-C) C="$2"; #you'll see what this is for later
shift;;
--) shift;
break;;
-*) echo "invalid option";
exit 1;;
*) break;;
esac
shift
done
echo "cfg1 = $cfg1"
echo "cfg2 = $cfg2"
echo "cfg3 = $cfg3"
exit 0
default.cfg::
cfg1=hello
cfg2=there
cfg3=friend
This all works as expected:
$ ./test.sh
cfg1 = hello
cfg2 = there
cfg3 = friend
$ ./test.sh -b optional
cfg1 = hello
cfg2 = optional
cfg3 = friend
This issue is I want configurations to be prioritized in the following manner:
options given on the command line
options defined in the config file defined by the -C option
options defined in the default config file
So if I have this:
test.cfg:
cfg1=custom_file_1
cfg2=custom_file_2
I want to get this:
$ ./test.sh -b command_line -C test.cfg
cfg1 = custom_file_1
cfg2 = command_line
cfg3 = friend
I just can't figure out how to load the default config file, then search the options for -C, then load the custom config file, overwriting the default, then search the command line parameters AGAIN and overwrite the configs again. I'm pretty new to shell scripting, so forgive me if I'm missing something obvious.
You can preprocess the arguments and pull out the value you're looking for:
#!/bin/bash
args=$(getopt C:a:b:c: "$#")
eval set -- $args
conf="default.cfg"
source "$conf"
# pre-process the arguments and see if we can find -C
found=0
for opt in "$#"; do
if [[ $found -eq 1 ]] && [[ -f "$opt" ]]; then
source "$opt"
break
fi
if [[ "$opt" == "-C" ]]; then
found=1
fi
done
while [ $# -gt 0 ]; do
case "$1" in
-a) cfg1="$2"; shift;;
-b) cfg2="$2"; shift;;
-c) cfg3="$2"; shift;;
-C) shift;; #don't do anything with this
--) shift;
break;;
-*) echo "invalid option";
exit 1;;
*) break;;
esac
shift
done
echo "cfg1 = $cfg1"
echo "cfg2 = $cfg2"
echo "cfg3 = $cfg3"
exit 0
To overwrite variables, try to replace :
-C) C="$2";
with :
-C) . "$2";
And invoke it with :
./test.sh -C test.cfg -a command_line1 -b command_line2
Update :
For options in any order, you can try this :
C="default.cfg"
. $C
while getopts C:a:b:c: OPTION
do
case $OPTION in
a) cfg1_override=$OPTARG;;
b) cfg2_override=$OPTARG;;
c) cfg3_override=$OPTARG ;;
C) . $OPTARG;;
-) break;;
-*) echo "invalid option";
exit 1;;
*) break;;
esac
done
shift $(($OPTIND - 1))
cfg1="${cfg1_override-${cfg1}}"
cfg2="${cfg2_override-${cfg2}}"
cfg3="${cfg3_override-${cfg3}}"
echo "cfg1 = $cfg1"
echo "cfg2 = $cfg2"
echo "cfg3 = $cfg3"
exit 0
Based on Is it possible to specify the order getopts conditions are executed?
First source default.cfg.
Than scan your options for a -C option. Handle this one when found.
Finally use getopts and skip -C when you find it during getopts.

How to require command line parameters in bash?

I have a bash script where I need to have some parameters. Usage should be only between:
./script.sh --scan [scan type] [keyword]
or
./script.sh --help
In example it should be something like this:
$ ./script.sh
[Usage]
$ ./script.sh --scan
Specify scan type
$ ./script.sh --help --scan
[Usage]
$ ./script.sh --scan short
Specify keyword to search
$ ./script.sh --scan short keyword
[Starts short scanning for "keyword" - go to function where my script is, blah, blah]
$ ./script.sh keyword --scan short
[As above]
$ ./script.sh keyword
[Usage]
How I can achieve this?
This is just off the top of my head:
if [ "$1" = "--scan" ] ; then
if [ "$#" -ge 2 ] ; then
_SCANTYPE=$2
if [ "$#" -ge 3 ] ; then
_KEYWORD=$3
else
echo -n "Specify keyword to search: "
read _KEYWORD
fi
else
echo -n "Specify scan type: "
read _SCANTYPE
echo -n "Specify keyword to search: "
read _KEYWORD
fi
# Actual scan code
else
# Display usage info
fi
This is pretty basic, of course, but hopefully it will get you started.
Almost any Configure script to build a piece of software from source will have what you want. From perl's:
: option parsing
while test $# -gt 0; do
case "$1" in
-d) shift; fastread=yes;;
-e) shift; alldone=cont;;
-f)
shift
cd ..
if test -r "$1"; then
config_sh="$1"
else
echo "$me: cannot read config file $1." >&2
error=true
fi
cd UU
shift;;
--help|\
-h) shift; error=true;;
-r) shift; reuseval=true;;
-s) shift; silent=true; realsilent=true;;
-E) shift; alldone=exit;;
-K) shift; knowitall=true;;
-O) shift; override=true;;
-S) shift; silent=true; extractsh=true;;
-D)
shift
case "$1" in
*=)
echo "$me: use '-U symbol=', not '-D symbol='." >&2
echo "$me: ignoring -D $1" >&2
;;
*=*) echo "$1" | \
sed -e "s/'/'\"'\"'/g" -e "s/=\(.*\)/='\1'/" >> optdef.sh;;
*) echo "$1='define'" >> optdef.sh;;
esac
shift
;;
-U)
shift
case "$1" in
*=) echo "$1" >> optdef.sh;;
*=*)
echo "$me: use '-D symbol=val', not '-U symbol=val'." >&2
echo "$me: ignoring -U $1" >&2
;;
*) echo "$1='undef'" >> optdef.sh;;
esac
shift
;;
-A)
shift
xxx=''
yyy="$1"
zzz=''
uuu=undef
case "$yyy" in
*=*) zzz=`echo "$yyy"|sed 's!=.*!!'`
case "$zzz" in
*:*) zzz='' ;;
*) xxx=append
zzz=" "`echo "$yyy"|sed 's!^[^=]*=!!'`
yyy=`echo "$yyy"|sed 's!=.*!!'` ;;
esac
;;
esac
case "$xxx" in
'') case "$yyy" in
*:*) xxx=`echo "$yyy"|sed 's!:.*!!'`
yyy=`echo "$yyy"|sed 's!^[^:]*:!!'`
zzz=`echo "$yyy"|sed 's!^[^=]*=!!'`
yyy=`echo "$yyy"|sed 's!=.*!!'` ;;
*) xxx=`echo "$yyy"|sed 's!:.*!!'`
yyy=`echo "$yyy"|sed 's!^[^:]*:!!'` ;;
esac
;;
esac
case "$xxx" in
append)
echo "$yyy=\"\${$yyy}$zzz\"" >> posthint.sh ;;
clear)
echo "$yyy=''" >> posthint.sh ;;
define)
case "$zzz" in
'') zzz=define ;;
esac
echo "$yyy='$zzz'" >> posthint.sh ;;
eval)
echo "eval \"$yyy=$zzz\"" >> posthint.sh ;;
prepend)
echo "$yyy=\"$zzz\${$yyy}\"" >> posthint.sh ;;
undef)
case "$zzz" in
'') zzz="$uuu" ;;
esac
echo "$yyy=$zzz" >> posthint.sh ;;
*) echo "$me: unknown -A command '$xxx', ignoring -A $1" >&2 ;;
esac
shift
;;
-V) echo "$me generated by metaconfig 3.5 PL0." >&2
exit 0;;
--) break;;
-*) echo "$me: unknown option $1" >&2; shift; error=true;;
*) break;;
esac
done
For your two examples
./script.sh --scan [scan type] [keyword]
or
./script.sh --help
Try this
#!/bin/bash
usageMsg="usage:ScriptName --scan [scan type] [keyword] OR --help"
case $# in
[123] ) : nothing_may_be_OK ;;
0 ) # no args, display usageMsg
echo "$usageMsg" >&2 ; exit 1 ;;
esac
case "$1" in
--[Hh][Ee][Ll][Pp] ) echo "$usageMsg" >&2 ; exit 1 ;;
--[Ss][Cc][Aa][Nn] )
shift
case $# in
0) # using defaults
scanType="PrimaryScan" # change to your default scanType
keyword="PrimaryKeyWord" # again, change to your default value
;;
1)
echo "$usageMsg" >&2
echo "must provide both [scan type] and [keyword] arguments. Cant continue" >&2
exit 1
;;
2)
scanType="$1"
keyword="$2"
;;
esac # case $#
;;
esac # case $1
#restof your code goes here
echo "running scanner with scanType=$scanType and keyWord=$keyword"
echo ". . . . ."
exit 0
I'm guessing that your usage pattern means --scan OR advanced scan (with details), i.e. --scan [scan type] [keyword]
If you really intend for user to optionally add [scan type] AND/OR [keyword] then you'll need some further use of the techniques in the code above.
IHTH
I finally managed that! The basic version is:
#!/bin/bash
function usage
{
echo "Usage: usage"
exit
}
while [ $# -gt 0 ]; do
case $1 in
-h|--help)
usage
;;
-s|--scan)
scantype="$2"
shift
;;
-*)
usage
;;
*)
keyword="$1"
;;
esac
shift
done
if [ -z "$scantype" ]; then
echo "no scantype"
exit
fi
if [[ "$scantype" != "full" && "$scantype" != "long" && "$scantype" != "extended" && "$scantype" != "short" ]]; then
echo "invalid scantype"
exit
fi
if [ -z "$keyword" ]; then
echo "nokeyword"
exit
fi
echo "scantype: $scantype"
echo "keyword: $keyword"
Anyway thanks for your help :)

using getopts did not get the input value

I am running the below script, but it looks like the $filename or $srvname did not get the input value.
say for eg: ./test.sh -n abcd.net gives the output echo 'Filename or node name must be defined.'
it means that, the $srvname did not get the value "abcd.net", please advise am i doing anything wrong. ?
set -x
usage () {
echo "usage: $0 -n <nodename>"
echo "usage: $0 -f <filename>"
echo "usage: $0 -h <help>"
}
while getopts ":nfh:" opt; do
case "$opt" in
n) srvname="$OPTARG" ;;
f) filename="$OPTARG" ;;
h) # help
usage
exit 0
;;
:) echo "Error: -$OPTARG requires an argument"
usage
exit 1
;;
?) echo "Error: unknown option -$OPTARG"
usage
exit 1
;;
esac
done
function dosomecheck {
echo "do some checks"
}
if [ "$filename" != "" ] ; then
# read file
for x in `cat $filename` ; do
dosomecheck $x
done
fi
if [ "$srvname" != "" ] ; then
# read file
for x in $srvname ; do
dosomecheck $x
done
fi
Thanks in advance
Try doing:
while getopts ":n:f:h" opt;
because -n and -f takes argument while -h doesn't.

How to escape path separators in file path?

I'm writing my first bash script and having trouble assigning a file path to a variable:
$target="/etc/httpd/conf/httpd.conf"
It seems bash wants to interpret this with the "=" assignment operator resulting in the script throwing an error to the effect "No such file or directory."
Is there an easy way to do this? I've discovered I can assign a full path to a constant like this:
readonly TARGET=/etc/httpd/conf/httpd.conf
but that seems rather cumbersome. How would I perform string ops to modify/manipulate?
I've also discovered I can put full paths in an array like this:
declare -a cfile=('/root/.bashrc' '/etc/fstab')
All well and good, but how do I assign a file path to a variable?
== == == ==
finished! my first bash script - a basic config file manager
#!/bin/bash
# cfmgr.sh - configuration file manager bash script
# options: -get, -put
# '-get' creates SOURCEDIR/USERDIR and copies config files to USERDIR
# '-put' copies files in SOURCEDIR/USERDIR to system-defined locations on server
# purpose: helps with moving LAMP VMs to different hosts, bulk edits of
# of config files in editors like Notepad++, and backing up config files.
readonly SOURCEDIR=/usr/bin/_serverconfig
while [[ $# > 0 ]]
do
arg="$1"
shift
case $arg in
-put)
put=true
;;
-get)
get=true
;;
*)
badarg=true
;;
esac
done
clear
if [ $badarg ]; then
echo "Invalid argument. Use either 'scf.sh -put' or 'scf.sh -get' to put"\
"or get config files."
exit
elif [ $get ]; then
echo "Enter directory name to store files cfmgr will GET from this server:"
elif [ $put ]; then
echo "Enter directory name containing files cfmgr will PUT to this server:"
else
echo "Use either 'scf.sh -put' or 'scf.sh -get' to put or get config files."
exit
fi
read -e -i $SOURCEDIR"/" USERDIR
pattern=" |'"
if [[ $USERDIR =~ $pattern ]]; then
echo "Spaces not allowed. Please try again."
exit
fi
declare -a cfile=('/root/.bashrc' '/etc/fstab' '/etc/hosts' '/etc/networks'\
'/etc/php.ini' '/etc/nsswitch.conf' '/etc/ntp.conf' '/etc/resolv.conf'\
'/etc/sysctl.conf' '/etc/httpd/conf/httpd.conf' '/etc/selinux/config'\
'/etc/samba/smb.conf' '/etc/samba/smbusers' '/etc/security/limits.conf'\
'/etc/sysconfig/network' '/etc/sysconfig/network-scripts/ifcfg-eth0'\
'/etc/sysconfig/network-scripts/ifcfg-eth1')
if [ $get ]; then
if [[ -d "$USERDIR" ]]; then
echo $USERDIR "directory already exists. Please try again."
exit
else
mkdir -m 755 $USERDIR
fi
for file in ${cfile[#]}
do
if [ -e $file ]; then
rsync -q $file $USERDIR
if [ $? -eq 0 ]; then
sleep 0.1
printf "# "$file"\n"
fi
else
printf "not found: "$file"\n"
fi
done
elif [ $put ]; then
if [[ ! -d "$USERDIR" ]]; then
echo $USERDIR "directory does not exist. Please try again."
exit
fi
id=0
cd $USERDIR
for item in *
do
if [[ -f $item ]]; then
cdir[$id]=$item
id=$(($id+1))
fi
done
for file in ${cdir[#]}
do
case $file in
.bashrc)
idx=0
;;
fstab)
idx=1
;;
hosts)
idx=2
;;
networks)
idx=3
;;
php.ini)
idx=4
;;
nsswitch.conf)
idx=5
;;
ntp.conf)
idx=6
;;
resolv.conf)
idx=7
;;
sysctl.conf)
idx=8
;;
httpd.conf)
idx=9
;;
config)
idx=10
;;
smb.conf)
idx=11
;;
smbusers)
idx=12
;;
limits.conf)
idx=13
;;
network)
idx=14
;;
ifcfg-eth0)
idx=15
;;
ifcfg-eth1)
idx=16
;;
*)
printf "not found: "$file"\n"
continue
esac
target=${cfile[$idx]}
if [[ -e $target ]]; then
dtm=$(date +%Y-%m-%d)
mv $target $target"."$dtm
fi
source=$USERDIR"/"$file
dos2unix -q $source
rsync -q $source $target
if [ $? -eq 0 ]; then
sleep 0.1
printf "# "$target"\n"
fi
done
read -p "reboot now? (y|n)" selection
case $selection in
[Yy]*)
`reboot`
;;
*)
exit
;;
esac
fi
exit 0
Instead of
$target="/etc/httpd/conf/httpd.conf"
Use:
target="/etc/httpd/conf/httpd.conf"
When bash sees the former, it first substitutes in for "$target". If target was empty, then the line that bash tries to execute, after the variable substitution and quote removal steps, is:
=/etc/httpd/conf/httpd.conf
Since there is no file named "=/etc/httpd/conf/httpd.conf", bash returns with a "No such file or directory" error.

Resources