#!/bin/bash
menu=0
dir=""
size=""
name=""
modif=""
while [ $menu -ne 6 ]
do
echo "1. Name: $name"
echo "2. Directory $dir"
echo "3. Last modified: $modif"
echo "4. Minimum size: $size"
echo "5. Search"
echo "6. End"
read menu
case "$menu" in
"1") read name ;;
"2") read dir ;;
"3") read modif ;;
"4") read size;;
"5") if [ -z $name ]
then
option1=""
else
option1="-name $name"
fi
find "$option1";;
"6") ;;
*) echo "Wrong number!"
esac
done
I need to make a script which will be working like find command, but I've encountered a problem. When user doesnt input for example name the find command should have name option disabled.
I came up with something like this above, but it doestn work when the variable name doesnt contain anything (user didnt input anything). I keep getting error:
find: paths must precede expression: BASH ....
I honestly have no idea how to make it work instead of having 2^4 IF's and executing find with only those specified options that user has choosen.
Is there any way to make it easier?
edit: Now i modified it to:
#!/bin/bash
menu=0
dir=""
modif=""
while [ $menu -ne 6 ]
do
echo "1. Name: $name"
echo "2. Directory $dir"
echo "3. Last modified: $modif"
echo "4. Minimum size: $size"
echo "5. Search"
echo "6. End"
read menu
case "$menu" in
"1")
read name
;;
"2") read dir ;;
"3") read modif ;;
"4")
read size
;;
"5")
if [ -z $name ]
then
unset tablica[0]
else
tablica[0]="-name $name"
fi
find "${tablica[#]}"
;;
"6") ;;
*) echo "Wrong number!"
esac
done
But another error:
find: unknown predicate `-name example.txt'
You can build an array of parameters:
myarray=()
if [[ $size ]]
then
myarray+=(-size +"$size")
fi
if [[ $name ]]
then
myarray+=(-name "$name")
fi
...
find "$dir" "${myarray[#]}"
The benefit of this approach is that it also handles spaces correctly, and doesn't allow code injection.
Related
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
This code is not running it shows error:- whl_case_lop1: 25: whl_case_lop1: Syntax error: "elif" unexpected (expecting ";;")
#if [ -z $1 ]
#then
#rental=****unknown item****
#elif [ -n $1 ]
#then
#rental=$1
#else [ -n $2 ]
#then
#rent=$2
#fi
echo "1. Vehicle_on_rent"
echo "2. Living_house"
echo -n "choose option [1 or 2]? "
read cate;
if [ $cate -eq 1 ];
then
echo "Enter your vehicle type"
read rental
case $rental in
"car") echo "for $rental in 20/km";;
"van") echo "for $rental in 15/km";;
"jeep") echo "for $rental in 10/km";;
"bike") echo "for $rental in 5/km";;
*) echo "We can't find $rental for your vehicle"
elif [ $cate -eq 2 ];
then
echo "Enter your Room requirement"
read rent
case $rent in
"1BHK") echo "for $rent is 10k";;
"2BHK") echo "for $rent is 15k";;
"3BHK") echo "for $rent is 20k";;
*) echo "we can't find $rent for your Requirement"
else
echo "Please check your requirements! Maybe you choose a wrong option"
fi
You need a ;; in your case's *) branch, and the case statement ends with the esac keyword: https://www.gnu.org/software/bash/manual/bashref.html#Conditional-Constructs
You might want to use the select statement, to restrict the user's responses:
PS3="Enter your vehicle type: "
select rental in car van jeep bike; do
case $rental in
car) echo "for $rental in 20/km"; break ;;
van) echo "for $rental in 15/km"; break ;;
jeep) echo "for $rental in 10/km"; break ;;
bike) echo "for $rental in 5/km"; break ;;
*) echo "We can't find $rental for your vehicle" ;;
esac
done
Indent your code to aid maintainability.
I want to make an argument as optional in getopt bash so that if the user didn't specify it, then it still runs without killing the program. How can i do that. Here is my previous code
while getopts ":l:q:s:e:hg:" opt; do
case $opt in
l)
lincRNAfasta=$OPTARG
;;
q)
query_species=$OPTARG
;;
s)
subject_species=$OPTARG
;;
e)
subject_gff=$OPTARG
;;
h)
echo "USAGE : open script in text editor"
exit 1
;;
g)
subject_genome=$OPTARG
;;
\?)
echo "Invalid option: -$OPTARG" >&2
exit 1
;;
:)
echo "Option -$OPTARG requires an argument." >&2
exit 1
;;
esac
done
Sorry, had to update my answer, I misunderstood the question.
I was looking at the documentation for getopts.c which does support ::
But the code that you have works whether I specify 1, 2, or 3 arguments, and in any order.
If it is exiting with an error one reason could be that the variables need to be defined:
#! /bin/bash
lincRNAfasta=
query_species=
subject_species=
subject_gff=
subject_genome=
help='''
USAGE : open script in text editor
-l lincRNAfasta
-q query_species
-s subject_species
-e subject_gff
-g subject_genome
'''
while getopts ":l:q:s:e:hg:" opt; do
case $opt in
l)
lincRNAfasta=$OPTARG
;;
q)
query_species=$OPTARG
;;
s)
subject_species=$OPTARG
;;
e)
subject_gff=$OPTARG
;;
h)
printf "$help"
exit 1
;;
g)
subject_genome=$OPTARG
;;
\?)
echo "Invalid option: -$OPTARG" >&2
exit 1
;;
:)
echo "Option -$OPTARG requires an argument." >&2
exit 1
;;
esac
done
echo "doing something else"
if [ -z "$lincRNAfasta" ];then
echo "its empty"
echo "lincRNAfasta: $lincRNAfasta"
echo
else
echo "not empty"
echo "lincRNAfasta: $lincRNAfasta"
echo
fi
if [ -z "$query_species" ];then
echo "its empty"
echo "query_species: $query_species"
echo
else
echo "not empty"
echo "query_species: $query_species"
echo
fi
if [ -z "$subject_species" ];then
echo "its empty"
echo "subject_species: $subject_species"
echo
else
echo "not empty"
echo "subject_species: $subject_species"
echo
fi
if [ -z "$subject_gff" ];then
echo "its empty"
echo "subject_gff: $subject_gff"
echo
else
echo "not empty"
echo "subject_gff: $subject_gff"
echo
fi
if [ -z "$subject_genome" ];then
echo "its empty"
echo "subject_genome: $subject_genome"
echo
else
echo "not empty"
echo "subject_genome: $subject_genome"
echo
fi
# do something once variables have been set
# any variable not set will be empty
echo "doing something else"
Output:
bob#squids:~/Desktop$ ./1.sh -h
USAGE : open script in text editor
-l lincRNAfasta
-q query_species
-s subject_species
-e subject_gff
-g subject_genome
bob#squids:~/Desktop$ ./1.sh -s a -g h -e e -q q
doing something else
its empty
lincRNAfasta:
not empty
query_species: q
not empty
subject_species: a
not empty
subject_gff: e
not empty
subject_genome: h
doing something else
bob#squids:~/Desktop$ ./1.sh -s a -g h -e e
doing something else
its empty
lincRNAfasta:
its empty
query_species:
not empty
subject_species: a
not empty
subject_gff: e
not empty
subject_genome: h
doing something else
bob#squids:~/Desktop$ ./1.sh -s a -e e -g 123
doing something else
its empty
lincRNAfasta:
its empty
query_species:
not empty
subject_species: a
not empty
subject_gff: e
not empty
subject_genome: 123
doing something else
Or instead of initializing the variables as empty, you could initialize them as a string like "NOTSPECIFIEDONSTART". And when starting the script you could pass an empty string like -g ''
I'm creating a shell script that takes in user input and text's people using the mail function. I am looking to make it more advanced. Right now it just text's one person at a time, I want it to have the ability to text multiple people or even everyone with a user input of 'All'.
#!/bin/sh
# Prefix the numbers with something
number_Joe=8881235555
number_Bob=8881235556
echo "Who do you want to text?:(i.e. Joe, Bob, etc)"
read name
echo "What do you want to say?:"
read quote
# Remove any dangerous characters that the user enters
sanitized=$(printf "%s" "$name" | tr -cd 'a-zA-Z')
#Look up by evaluating e.g. "number=$number_Joe"
eval "number=\$number_$sanitized"
if [ "$number" ]
then
echo "texting $name ($number) with $quote"
printf "%s\n" "$quote" | mailx -s "Text Message via email" "$number#txt.att.net"
else
echo "Unknown user"
exit 1
fi
Also, is there a cleaner method of bringing in a external txt file that houses the numbers instead of the script?
(note: we still have bash <4, thus why I'm not using a associative array)
Here's a rewrite.
Should work fine in bash3.
#!/bin/bash
# Prefix the numbers with something
names=()
names+=(Joe); numberJoe=8881235555
names+=(Bob); numberBob=8881235556
domain=txt.att.example.com
usage () {
echo "usage: $(basename $0) names message ..."
echo "where: names is a comma-separated list of names (no spaces)"
echo
echo "example: $(basename $0) Jim,Fred hello lads, this is my message"
}
while getopts ":hl" opt; do
case $opt in
h) usage; exit ;;
l) IFS=,; echo "known names: ${names[#]}"; exit ;;
esac
done
shift $((OPTIND - 1))
if (( $# < 2 )); then
usage
exit
fi
IFS=, read -ra usernamelist <<<"$1"
shift
message="$*"
# validate names
namelist=()
for name in "${usernamelist[#]}"; do
if [[ " ${names[#]} " == *" $name "* ]]; then
namelist+=("$name")
else
echo "unknown name: $name" >&2
fi
done
if (( ${#namelist[#]} == 0 )); then
echo "no valid names given" >&2
exit 1
fi
# generate the recipient list
echo "texting '$message' to:"
recipients=()
for name in "${namelist[#]}"; do
numvar="number$name"
echo " $name -> ${!numvar}"
recipients+=( "${!numvar}#$domain" )
done
# send it
printf "%s\n" "$message" | mailx -s "Text Message via email" "$(IFS=,; echo "${recipients[*]}")"
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.