bash script with case statement - bash

I have a file called Namebook, with following data inside:
$ cat Namebook
Kamala Hasan 123
Rajini kanth 345
Vijay 567
Ajith kumar 908
$
Then I have a bash script to add a new name in the file Namebook, called add_name.sh
$ cat add_name.sh
#!/bin/bash
echo "$1 $2" >> Namebook
$
Next I have a script to look someone from this Namebook, called look_up.sh
$ cat look_up.sh
#!/bin/bash
grep "$1" Namebook
$
Then again I have a script to remove someone from this Namebook, called remove.sh
$ cat remove.sh
#!/bin/bash
grep -v "$1" Namebook > tmp/Namebook
mv tmp/Namebook Namebook
$
These scripts add, lookup and remove users from the Namebook file.
Based on the combination of these three script, I created a single script, all_action.sh, to perform all said actions
$cat all_action.sh
#!/bin/bash
echo 'Select the option
1. Lookup some one from the Namebook
2. Add name to Namebook
3. Remove name from the Namebook
Select the options range of (1-3): \c '
read choice
case "$choice"
in
1) "Enter the name to lookup: \c"
read name
look_up "$name" ;;
2) "Enter the name to be add: \c"
read name
"Enter the number to be add: \c"
read number
add_name "$name" "$number" ;;
3) "Enter the name to be remove: \c"
read name
remove "$name ;;
esac
My question: when I execute the program all_action.sh, it throws an error
For example: I am going to run ./all_action.sh
Select the option
1. Lookup some one from the Namebook
2. Add name to Namebook
3. Remove name from the Namebook
Select the options range of (1-3): \c
1
Enter name to be lookup
Kamala Hasa
./all_action.sh: line no : look_up: command not found
Could you please any one help on this ?

The commands: look_up, add_name, remove are not found. Put the complete path in your script and specify the Interpreter:
sh /home/myuser/myscript.sh
or with relative path:
sh ./myscript.sh

Related

Select and Case statements using BASH

The idea behind this code is to print options to the user and allow them to execute commands. The file name is example.sh. I simply run it by ./example.sh and it gives 3 options: author, type, exit.
NAME="Mark Zuck"
PS3="enter command: "
select var in author type exit
do
case $var in
author)
echo $NAME
;;
type)
cat $2
;;
exit)
exit
;;
esac
done
Whenever I type author, the output is Mark Zuck, but I am trying to execute the command "type".
After putting 2 <"file name">, it should give me the contents of the file.
cat $2 doesn't work. I don't know how to solve this issue.
1) author
2) type
3) exit
enter command: 2 file1
enter command:
The output is above.
It should give me the content of the "file1" (file1 does exist)
select returns two values:
The first is in your custom var and contains the token associated to the number in select list
The second is in REPLY (like built-in read command) and contains the number
Ok? it's the standard test case.
If you type another response on select prompt, var is nul (empty) and REPLY contains the response!
So, I think, this script is for you.
Of course, the coma (,) is not adapted for you, it's just an example. Use what you want.
#! /usr/bin/env bash
NAME="Mark Zuck"
PS3="enter command: "
select var in author type exit
do
case "$var,$REPLY" in
"author,1")
echo "$NAME"
;;
",2 "*)
filename="${REPLY#2 }"
echo "filename=<$filename>"
cat "${filename}"
;;
"exit,3")
exit
;;
esac
done
Test:
> bash --version
GNU bash, version 5.2.9(1)-release (x86_64-redhat-linux-gnu)
> ./test_select.sh
1) author
2) type
3) exit
enter command: 1
Mark Zuck
enter command: 2 test_select.sh
filename=<test_select.sh>
#! /usr/bin/env bash
NAME="Mark Zuck"
PS3="enter command: "
select var in author type exit
do
case "$var,$REPLY" in
"author,1")
echo "$NAME"
;;
",2 "*)
filename="${REPLY#2 }"
echo "filename=<$filename>"
cat "${filename}"
;;
"exit,3")
exit
;;
esac
done
enter command: 3
>

awk variables print as options in bash

I'm trying to make a script which reads your firefox profile.ini file, gives you options for which profile you want to unlock and execute a simple rm on the .lock file of the given profile (useful when you run multiple firefox sessions between workstations in different buildings and you did not logout correctly
I have the following file profile.ini for example
[General]
StartWithLastProfile=0
[Profile0]
Name=default
IsRelative=1
Path=rkfddmkn.default
Default=1
[Profile1]
Name=NX
IsRelative=1
Path=sf18055j.NX
[Profile14]
Name=gutter
IsRelative=1
Path=sf18055judsfsdfdfds.gutter
[Profile556]
Name=Jerusalem
IsRelative=1
Path=234920fgffdg38.Jerusalem
And this is the first part
ini=$HOME/.mozilla/firefox/profiles.ini
The script checks if profiles.ini exists otherwise exit
if [ -f $ini ]
then
The script reads the profiles.ini file and return multiple profiles as options followed by the name of the profiles
profiles=`cat $ini | grep "Profile[0-9*]"`
echo $profiles | awk -v RS=" " '{print}'
names=`cat $ini | grep Name | sed s/Name=/\/`
echo $names | awk -v RS=" " '{print}'
echo $options = $profiles . ' ' . $names; | awk -v RS=" " '{print}'
I'm not sure if I'm going down the right path.
How can I prompt the user to select an option by pairing the awk strings ?
A native-bash parser that breaks the relevant part of your input file into three associative arrays might look something like the following:
#!/usr/bin/env bash
set -x; PS4=':$LINENO+'
section_re='^\[Profile(.*)\][[:space:]]*$'
kv_re='^([[:alnum:]]+)=(.*)'
declare -A profilesByName=( )
declare -A profileNames=( )
declare -A profilePaths=( )
current_section=
while IFS= read -r line; do : line="$line"
[[ $line =~ $section_re ]] && { current_section=${BASH_REMATCH[1]}; continue; }
[[ $line =~ $kv_re ]] || continue
[[ $current_section ]] || continue ## ignore k/v pairs if not in a section
key=${BASH_REMATCH[1]}; value=${BASH_REMATCH[2]}
case $key in
Name) profileNames[$current_section]=$value
profilesByName[$value]=$current_section ;;
Path) profilePaths[$current_section]=$value ;;
esac
done
Then, if you want to delete the lockfile associated with a given profile name, it becomes as simple as:
deleteLockForName() {
local desiredName=$1
local selectedProfile selectedPath
selectedProfile=${profilesByName[$desiredName]}
[[ $selectedProfile ]] || { echo "ERROR: No profile with name $desiredName found" >&2; return 1; }
selectedPath=${profilePaths[$selectedProfile]}
echo "Deleting $selectedPath.lck" >&2
rm -f -- "$selectedPath.lck"
}
...as used in:
deleteLockForName Jerusalem
You can see it running at https://ideone.com/d0QFYa -- in the above example invocation, emitting Deleting 234920fgffdg38.Jerusalem.lck.
#CharlesDuffy is always a tough act to follow (try it...), but I took a somewhat different approach to solving your problem.
First, you don't need to deal with $HOME/.mozilla/firefox/profiles.ini at all. All you need to deal with is those profiles that have an existing lock symlink within their profile directory. So create an array just holding the names of the directories with with a lock symlink present to display to the user to remove. You can do that with find and sed in a simple command-substitution, e.g.
#!/bin/bash
moz="$HOME/.mozilla/firefox"
## locate all lockfile links that exist below "$moz"
readarray -t lockfiles < <(find "$moz" -type l -name lock -printf '%P\n')
Next, the select loop in bash will create a numbered menu with entries being the profile directories in your lockfiles array. The user can select the number corresponding to the profile directory name to remove (unlink) the lock symlink in that directory. For example:
## set prompt for select menu
PS3="Selection: "
## prompt
printf "\nThe following lockfiles found, select to remove, Quit to end:\n\n"
## create menu listing existing lockfile links
select lockname in "${lockfiles[#]%/*}" "Quit"; do
[ "$lockname" = "Quit" ] && break
if [ -h "$moz/$lockname/lock" ]; then
printf "\ndeleting lock %s\n" "$moz/$lockname/lock"
## uncomment to actually remove link
# unlink "$moz/$lockname/lock"
break
else
printf "\nerror: invalid selection.\n" >&2
fi
done
(note: setting PS3 controls the prompt displayed by the select loop, instead of the generic #?. Also note the /lock was trimmed from the contents of the array to display only the profile directory name in the select loop declaration with "${lockfiles[#]%/*}")
Putting it altogether, you would have:
#!/bin/bash
moz="$HOME/.mozilla/firefox"
## locate all lockfile links that exist below "$moz"
readarray -t lockfiles < <(find "$moz" -type l -name lock -printf '%P\n')
## set prompt for select menu
PS3="Selection: "
## prompt
printf "\nThe following lockfiles found, select to remove, Quit to end:\n\n"
## create menu listing existing lockfile links
select lockname in "${lockfiles[#]%/*}" "Quit"; do
[ "$lockname" = "Quit" ] && break
if [ -h "$moz/$lockname/lock" ]; then
printf "\ndeleting lock %s\n" "$moz/$lockname/lock"
## uncomment to actually remove link
# unlink "$moz/$lockname/lock"
break
else
printf "\nerror: invalid selection.\n" >&2
fi
done
Example Use/Output
$ bash ff_rm_lock.sh
The following lockfiles found, select to remove, Quit to end:
1) 3cblo6ag.dcr_new
2) Quit
Selection: 1
deleting lock /home/david/.mozilla/firefox/3cblo6ag.dcr_new/lock
or using "Quit" leaving all lock symlinks in place:
$ bash ff_rm_lock.sh
The following lockfiles found, select to remove, Quit to end:
1) 3cblo6ag.dcr_new
2) Quit
Selection: 2
(note: you must uncomment the line unlink "$moz/$lockname/lock" to actually remove the link -- I commented it to allow testing without removing my Firefox lockfile)
A different approach, but given your problem description, this should eliminate listing profiles with not associate lock symlink present. Let me know if you have questions.

How to execute a file that is located in $PATH

I am trying to execute a hallo_word.sh that is stored at ~/bin from this script that is stored at my ~/Desktop. I have made both scripts executable. But all the time I get the problem message. Any ideas?
#!/bin/sh
clear
dir="$PATH"
read -p "which file you want to execute" fl
echo ""
for fl in $dir
do
if [ -x "$fl" ]
then
echo "executing=====>"
./$fl
else
echo "Problem"
fi
done
This line has two problems:
for fl in $dir
$PATH is colon separated, but for expects whitespace separated values. You can change that by setting the IFS variable. This changes the FIELD SEPARATOR used by tools like for and awk.
$fl contains the name of the file you want to execute, but you overwrite its value with the contents of $dir.
Fixed:
#!/bin/sh
clear
read -p "which file you want to execute" file
echo
IFS=:
for dir in $PATH ; do
if [ -x "$dir/$file" ]
then
echo "executing $dir/$file"
exec "$dir/$file"
fi
done
echo "Problem"
You could also be lazy and let a subshell handle it.
PATH=(whatever) bash command -v my_command
if [ $? -ne 0 ]; then
# Problem, could not be found.
else
# No problem
fi
There is no need to over-complicate things.
command(1) is a builtin command that allows you to check if a command exists.
The PATH value contains all the directories in which executable files can be run without explicit qualification. So you can just call the command directly.
#!/bin/sh
clear
# r for raw input, e to use readline, add a space for clarity
read -rep "Which file you want to execute? " fl || exit 1
echo ""
"$fl" || { echo "Problem" ; exit 1 ; }
I quote the name as it could have spaces.
To test if the command exists before execution use type -p
#!/bin/sh
clear
# r for raw input, e to use readline, add a space for clarity
read -rep "Which file you want to execute? " fl || exit 1
echo ""
type -p "$fq" >/dev/null || exit 1
"$fl" || { echo "Problem" ; exit 1 ; }

define "$key" use it in a script, but also store it

How can I create a specific line in another file using bash please? Like
echo "Please input the days you want to keep "
$key= ?
touch .beebrc; keep="$key"
where the file ".beebrc" has a line 'keep= x' and "$key" is created in the main script.
But how do I define "$key" please? And write it into ".beebrc" as a new line at position/line 8? The full function is -
function trim {
echo;
read -t "$temi" -n1 -p ""$bldgrn" Do you want to delete some of your download history? [y/n/q/r] $(tput sgr0)" ynqr ;
case "$ynqr" in
[Yy]) echo
read -t "$temi" -n3 -p ""$bldgrn" Please input the days you want to keep $(tput sgr0)" key ## ask
if test -e .beebrc && grep -q "^keep=" .beebrc 2>/dev/null ; then
sed -i "s/^keep=.*/keep=$key/" .beebrc
else
echo "keep=$key" >> .beebrc
#fi
cd /home/$USER/.get_iplayer
eval "$player" --trim-history "$key"; cd; ques;
#echo;;
[Nn]) ques;;
[Qq]) endex;;
[Rr]) exec "$beeb";;
* ) echo ""$bldgrn" Thank you $(tput sgr0)";;
esac
fi
};
Does this help in defining it all? (Sorry, should've put it in at first)
Perhaps:
read -p "Please input the days you want to keep: " key ## Ask.
echo "keep=\"$key\"" > .beebrc ## Store.
Use read to capture user input into a variable, and then write it to your file.
For example:
echo "Please input the days you want to keep "
read key
echo $key > .beebrc
#!/bin/bash
read -p "Please input the days you want to keep: " key
if test -e .beebrc && grep -q "^keep=" .beebrc 2>/dev/null ; then
sed -i "s/^keep=.*/keep=$key/" .beebrc
else
echo "keep=$key" >> .beebrc
fi
This script:
Prompts for input and stores the value in $key
Tests if .beebrc exists and that a line beginning "keep=" exists in it. If so, replace the keep= line with keep=$key
Otherwise append a new line/create the file with keep=$key.
This will need validation added because user input should not be trusted. (this answer might help)

Finding and adding a word to a particular line using shell script with exact format of a file

My problem is to add a username to a file, I really stuck to proceed, please help.
Problem: I am having a file called usrgrp.dat. The format of this file is like:
ADMIN:srikanth,admin
DEV:dev1
TEST:test1
I am trying to write a shell script which should give me the output like:
Enter group name: DEV
Enter the username: dev2
My expected output is:
User added to Group DEV
If I see the contents of usrgrp.dat, it should now look like:
DEV:dev1,dev2
TEST:test1
And it should give me error saying user already present if I am trying to add already existing user in that group. I am trying this out with the following script:
#!/bin/sh
dispgrp()
{
groupf="/home/srikanth/scm/auths/group.dat"
for gname in `cat $groupf | cut -f1 -d:`
do
echo $gname
done
echo "Enter the group name:"
read grname
for gname in `cat $groupf | cut -f1 -d:`
do
if [ "$grname" = "$gname" ]
then
echo "Enter the username to be added"
read uname
for grname in `cat $groupf`
do
$gname="$gname:$uname"
exit 1
done
fi
done
}
echo "Group display"
dispgrp
I am stuck and need your valuable help.
#!/bin/sh
dispgrp()
{
groupf="/home/srikanth/scm/auths/group.dat"
tmpfile="/path/to/tmpfile"
# you may want to pipe this to more or less if the list may be long
cat "$groupf" | cut -f1 -d:
echo "Enter the group name:"
read grname
if grep "$grname" "$groupf" >/dev/null 2>&1
then
echo "Enter the username to be added"
read uname
if ! grep "^$grname:.*\<$uname\>" "$groupf" >/dev/null 2>&1
then
sed "/^$grname:/s/\$/,$uname/" "$groupf" > "$tmpfile" && mv "$tmpfile" "$groupf"
else
echo "User $uname already exists in group $grname"
return 1
fi
else
echo "Group not found"
return 1
fi
}
echo "Group display"
dispgrp
You don't need to use loops when the loops are done for you (e.g. cat, sed and grep).
Don't use for to iterate over the output of cat.
Don't use exit to return from a function. Use return.
A non-zero exit or return code signifies an error or failure. Use 0 for normal, successful return. This is the implicit action if you don't specify one.
Learn to use sed and grep.
Since your shebang says #!/bin/sh, the changes I made above are based on the Bourne shell and assume POSIX utilities (not GNU versions).
Something like (assume your shell is bash):
adduser() {
local grp="$1"
local user="$2"
local gfile="$3"
if ! grep -q "^$grp:" "$gfile"; then
echo "no such group: $grp"
return 1
fi
if grep -q "^$grp:.*\\<$user\\>" "$gfile"; then
echo "User $user already in group $grp"
else
sed -i "/^$grp:/s/\$/,$user/" "$gfile"
echo "User $user added to group $grp"
fi
}
read -p "Enter the group name: " grp
read -p "Enter the username to be added: " user
adduser "$grp" "$user" /home/srikanth/scm/auths/group.dat

Resources