Strange syntax error in if condition - Shell Script - bash

So I'm making a shell script in Ubuntu. It's purpose is simple. You give a command with arguments and you get a different operation each time. The problem is that when I run the the script it won't actually run because of a syntax error in one elif. The most suspicious thing is that I have a similar elif above wich works or at least doesn't pop a syntax error...
I'm leaving my code for you to see it and understand. Thanks in advance!
if [ "$1" = "-a" -a $# -lt 3 ]
then
echo "Add a new line in katalogos!"
read -p "Give me a name... " name
read -p "Give me a surname... " surname
read -p "Give me a city name... " cityName
read -p "Give me a phone number... " num
echo "$name $surname $cityName $num" > katalogos
elif [ "$1" = "-l" -a $# -lt 3 ]
then
echo "Content of katalogos will be sorted numerically and blank lines will be excluded!"
sort -b -n katalogos
elif [ "$1" = "-s" -a $# -lt 4 ]
if [[ $2 != *[!0-9]* ]]
then
echo "Content of katalogos will be sorted according to the second argument!"
sort +$3 katalogos
fi
elif [ "$1" = "-c" -a $# -lt 4 ] // syntax error
if [[ $2 = *[!0-9]* ]]
then
echo "Content of katalogos will be sorted according to the keyword!"
if [ $(grep -e "$2" katalogos | wc -l) -eq 0 ]
then
echo "String is not matched."
else
grep -e "$2" katalogos
fi
fi
elif [ "$1" = "-d" -a ( "$3" = "-b" -o "$3" = "-r" ) ]
if [[ $2 = *[!0-9]* ]]
then
echo "Katalogos's string matching lines will be deleted and blank lines will be in their place, assuming that the third argument equals -b, else just the lines will be deleted!"
if [ $(grep -e $2 katalogos | wc -l) -eq 0 ]
then
echo "String is not matched."
else
if [ "$3" = "-b" ]
then
sed -i "$3" katalogos | sed -i '$ a '
echo "A blank line inserted in place of the deleted one."
else
sed -i "$3" katalogos
echo "Line deleted."
fi
fi
fi
elif [ "$1" = "-n" ]
echo "katalogos's number of blank lines will be shown with the ability to delete them!"
grep -cvP '\S' katalogos
read -p "Do you want to delete them? Type 1 for yes or 0 for no... " ans
if [ $ans -eq 1 ]
then
grep -cvP '\S' file | sed -i
echo "Lines deleted."
fi
else
echo "Help centre!"
echo "-Type ./telcat -a to insert a new line to katalogos."
echo "-Type ./telcat -l to see the contents of katalogos sorted numerically (excluding blank lines)."
echo "-Type ./telcat -s plus a number to see the contents of katalogos sorted by the data that the number points to."
echo "-Type ./telcat -c plus a keyword to see only the lines that match with the word given."
echo "-Type ./telcat -d plus a keyword and -b or -r to delete the lines that contain the word given. Specifically if the third argument is -b it will automatically add a blank line to the deleted one and if it is -r it will not."
echo "-Type ./telcat -n to see the number of blank lines of katalogos."
echo "End of help centre!"
fi

Related

Bash script - Using spaces as string

I'm trying to build a really simple TODO list with a bash script. It should allow user to add and remove a task and also see the entire list.
I've done it with the following script. But I've issues allowing a given task to take a whitespace as a string. For instance, if I'm adding a task with the command: ./programme_stack.sh add 1 start projet n1, it will only add a task with "start".
I've read a couple of things online, I know, I should double quote the variables but after trying this, it doesn't work. I must be missing something on the road.
Here is my script:
#!/bin/bash
TACHES=$HOME/.todo_list
# functions
function remove() {
res_remove=$(sed -n "$1p" $TACHES)
sed -i "$1d" $TACHES
}
function list() {
nl $TACHES
}
function add() {
if [ ""$(($(wc -l $TACHES | cut -d " " -f 1) + 1))"" == "$1" ]
then
echo "- $2" >> $TACHES
else
sed -i "$1i - $2" $TACHES
fi
echo "Task \"$2\" has been add to the index $1"
}
function isNumber() {
re='^[0-9]+$'
if ! [[ $# =~ $re ]] ; then
res_isNumber=true
else
res_isNumber=false
fi
}
# application
case $1 in
list)
list
;;
done)
shift
isNumber $#
if ! [[ "$res_isNumber" = false ]] ; then
echo "done must be followed by an index number"
else
nb_taches=$(wc -l $TACHES | cut -d " " -f 1)
if [ "$1" -ge 1 ] && [ "$1" -le $nb_taches ]; then
remove $1
echo "Well done! Task $i ($res_remove) is completed"
else
echo "this task doesn't exists"
fi
fi
;;
add)
shift
isNumber $1
if ! [[ "$res_isNumber" = false ]] ; then
echo "add must be followed by an index number"
else
index_max=$(($(wc -l $TACHES | cut -d " " -f 1) + 1))
if [ "$1" -ge 1 ] && [ "$1" -le $index_max ]; then
add $1 $2
else
echo "Idex must be between 1 and $index_max"
fi
fi
;;
*)
echo "./programme_stack.sh (list|add|done) [args]"
;;
esac
Can you guys see what I'm missing?
Many thanks!!
To get the script to support embedded spaces, 2 changes are needed
1) Accept embedded space - either
1A) Pass in the task name in quote script add nnn "say hello", OR
1B) Concatenate all the input parameters into single string.
2) Quote the task name to prevent it from being broken into individual words
In the code, implement 1B and 2
add)
...
if [ "$1" -ge 1 ] && [ "$1" -le $index_max ]; then
num=$1
shift
# Combine all remaining arguments
todo="$#"
add "$num" "$todo"
...

Having difficulty writing a script to sort words

I am dealing with sorting words in Bash according to a given argument. I am given either argument -r, -a , -v or -h and according to it there are options to sort the words, as you can see at my "help".
Somehow, if I pass the argument -r it creates an error. I really don't understand what I am doing wrong, as if[["$arg"=="-a"]] works, but I have to use case somehow.
Here is my code:
#!/bin/bash
# Natalie Zubkova , zubkonat
# zubkonat#cvut.fel.cz , LS
#help
help="This script will calculate occurances of words in a given file, and it will sort them according to the given argument in following order> \n
without parametre = increasing order according to a number of occurance\n
-r = decreasing order according to a number of occurance\n
-a = in alphabetical increasing order\n
-a -r = in alphabetical decreasing order\n
There are also special cases of the given parametre, when the script is not sorting but:\n
-h = for obtaining help \n
-v = for obtaining a number of this task "
# this function will divide a given chain into a words, so we can start calculating the occurances, we also convert all the capital letters to the small ones by - tr
a=0;
r=0;
EXT=0;
if [ "$1" == "-h" ]; then
echo $help
exit 0
fi
if [ "$2" == "-h" ]; then
echo $help
exit 0
fi
if [ "$1" == "-v" ]; then
echo "5"
exit 0
fi
if [ "$2" == "-v" ]; then
echo "5"
exit 0
fi
function swap {
while read x y; do
echo "$y" "$x";
done
}
function clearAll {
sed -e 's/[^a-z]/\n/gI' | tr '[A-Z]' '[a-z]' | sort | uniq -c |awk '{i++; if(i!=1) print $2" "$1}' #swap
}
for arg do
case "$arg" in
"-a")
a=1
;;
"-r")
r=1
;;
"-v")
echo "5" #number of task is 5
exit 0
;;
"-h")
echo $help
exit 0
;;
"-?")
echo "invalid parametre, please display a help using argument h"
exit 0
;;
esac
done
#Sort according to parametres -a and -r
function sortWords {
if [[ a -eq 1 ]]; then
if [[ r -eq 0 ]]; then
clearAll | sort -nk1
fi
fi
if [[ a -eq 1 ]]; then
if [[ r -eq 1 ]]; then
clearAll | sort -nk1 -r
fi
fi
if [[ r -eq 1 ]]; then
if [[ a -eq 0 ]]; then
clearAll | sort -nk2 -r
fi
fi
if [[ a -eq 0 ]]; then
if [[ r -eq 0 ]]; then
clearAll | sort -nk2
fi
fi
}
#code is from Stackoverflow.com
function cat-all {
while IFS= read -r file
do
if [[ ! -z "$file" ]]; then
cat "$file"
fi
done
}
#histogram
hist=""
for arg do
if [[ ! -e "$arg" ]]; then
EXT=1;
echo "A FILE DOESNT EXIST" >&2
continue;
elif [[ ! -f "$arg" ]]; then
EXT=1;
echo "A FILE DOESNT EXIST" >&2
continue;
elif [[ ! -r "$arg" ]]; then
EXT=1;
echo "A FILE DOESNT EXIST" >&2
continue;
fi
done
for arg do
hist="$hist""$arg""\n"
done
echo -e "$hist" | cat-all | sortWords
exit $EXT;
Here is what our upload system which does some test to see if our program works says:
Test #6
> b5.sh -r ./easy.txt
ERROR: script output is wrong:
--- expected output
+++ script stdout
## --- line 1 (167 lines) ; +++ no lines ##
-the 89
-steam 46
-a 39
-of 37
-to 35
...
script written 484 lines, while 484 lines are expected
script error output:
A FILE DOESNT EXIST
cat: invalid option -- 'r'
Try `cat --help' for more information.
script exit value: 1
ERROR: Interrupted due to failed test
If anyone could help me I would really appreciate it.
You forgot to move the parameter index position with shift:
"-r")
r=1
shift
;;
shift above moves to the next command line arg: ./easy.txt in your case.
Without it, read -r file will read -r instead of the file name.

bash- reading file from stdin and arguments

So I have googled this and thought I found the answers, but it still doesnt work for me.
The program computes the average and median of rows and columns in a file of numbers...
Using the file name works:
./stats -columns test_file
Using cat does not work
cat test_file | ./stats -columns
I am not sure why it doesnt work
#file name was given
if [[ $# -eq 2 ]]
then
fileName=$2
#file name was not given
elif [[ $# -eq 1 ]]
then
#file name comes from the user
fileName=/dev/stdin
#incorrect number of arguments
else
echo "Usage: stats {-rows|-cols} [file]" 1>&2
exit 1
fi
A very simple program that accepts piped input:
#!/bin/sh
stdin(){
while IFS= read -r i
do printf "%s" "$i"
done
}
stdin
Test is as follows:
echo "This is piped output" | stdin
To put that into a script / utility similar to the one in the question you might do this:
#!/bin/sh
stdin(){
while IFS= read -r i
do printf "%s" "$i"
done
}
rowbool=0
colbool=0
for i in $#
do case "$i" in
-rows) echo "rows set"
rowbool=1
shift
;;
-cols) echo "cols set"
colbool=1
shift
;;
esac
done
if [[ $# -gt 0 ]]
then
fileName=$1
fi
if [[ $# -eq 0 ]]
then fileName=$(stdin)
fi
echo "$fileName"

unary operator expected with more than 1 argument

for var in "$#"
do
if test -z $var
then
echo "missing operand"
elif [ -d $var ]
then
echo "This is a directory"
elif [ ! -f $var ]
then
echo "The file does not exist"
else
basename=$(basename $var)
dirname=$(readlink -f $var)
inodeno=$(ls -i $var| cut -d" " -f1)
read -p "remove regular file $#" input
if [ $input = "n" ]
then exit 1
fi
mv $var "$var"_"$inodeno"
echo "$basename"_"$inodeno":"$dirname" >> $HOME/.restore.info
mv "$var"_"$inodeno" $HOME/deleted
fi
done
**Hello, the above code is trying to mimic the rm command in unix. Its purpose is to remove the file .
Eg if I type in bash safe_rm file1 , it works however if type in
bash safe_rm file1 file 2 , it prompts me to remove file 1 twice and gives me a unary operater expected for line 27(if [ $input = "n" ]).
Why does it not work for two files, ideally I would like it to prompt me to remove file1 and file 2.
Thanks
read -p "remove regular file $#" input
should probably be
read -p "remove regular file $var" input
That's the basic.
And this is how I'd prefer to do it:
for T in "$#"; do
if [[ -z $T ]]; then
echo "Target is null."
elif [[ ! -e $T ]]; then
echo "Target does not exist: $T"
elif [[ -d $T ]]; then
echo "Target can't be a directory: $T"
else
BASE=${T##*/}
DIRNAME=$(exec dirname "$T") ## Could be simpler but not sure how you want to use it.
INODE_NUM=$(exec stat -c '%i' "$T")
read -p "Remove regular file $T? "
if [[ $REPLY == [yY] ]]; then
# Just copied. Not sure about its logic.
mv "$T" "${T}_${INODE_NUM}"
echo "${BASE}_${INODE_NUM}:${DIRNAME}" >> "$HOME/.restore.info"
mv "${T}_${INODE_NUM}" "$HOME/deleted"
fi
fi
done

bash script to automate searching for a string in code including sub-folders

I have been using a combination of tail, head and grep to print out code that is relavant to me. It helps me find functions and get straight to the point. I do this about 30 to 40 times a day so I thought i'd ask for some assistance on this since I am not good with sh scripting.
The command format I'd eventually like is something like giveme "searchstring" -nn << where nn is the number to give to head. err.. moving on
Below is what I am currently doing
grep -rl searchitem ./
cat ./filename | grep -n searchitem
tail -n +line ./filename | head -numberoflinestodisplay
Example:
jezzy#forum:/var/www/sgs# grep -rl insert_into_selectbox ./
./bin/ext/js/functions_edit.js
./src/ext/js/functions_edit.js
jezzy#forum:/var/www/sgs# cat ./bin/ext/js/functions_edit.js | grep -n insert_into_selectbox
195: for (key in data) insert_into_selectbox(selectbox, data[key], key, 0);
232: insert_into_selectbox(selectbox, data[key], key,1);
273: for (key in data) if (key!="_overload_") insert_into_selectbox(selectbox, data[key], key, 0);
289:function insert_into_selectbox(selectbox,text,value,selected) {
323: insert_into_selectbox(id,right.value,right.value,1);
334: insert_into_selectbox(id,options_right[i].text,options_right[i].value,1);
Then I choose one of the greps that works for me. I do not mind if the bash script does it for all occurrences of grep. It'll teach me to be more specific with my searches.
jezzy#forum:/var/www/sgs# tail -n +289 ./bin/ext/js/functions_edit.js | head -30
function insert_into_selectbox(selectbox,text,value,selected) {
if (text=="" || value=="") return;
var obj = getObj(selectbox);
var index = _selectbox_find(obj, value);
if (index == -1) {
index = obj.options.length;
obj.options[index] = new Option(text,value);
}
if (!obj.multiple && selected) obj.options[index].selected = true;
}
I think I could figure it out if I knew how to get all instances of the items found.
Merci and Thanks
Try this, it should be do what you look for, if I understand it correctly:
# Usage: giveme searchstring [numlines]
find . -type f -exec awk -v n=${2:-10} '
/'"$1"'/ {
printf("==== %s ====\n",FILENAME);
for(i=0;i<n;i++)
{
printf("%4d: %s\n",FNR,$0);
getline
}
}' {} +
You can try this script:
#!/bin/bash
shopt -s extglob
SEARCHDIR_DEFAULT="./"
read -ep "Enter search directory [$SEARCHDIR_DEFAULT]: " SEARCHDIR
[[ $SEARCHDIR =~ ^[[:space:]]*$ ]] && SEARCHDIR=$SEARCHDIR_DEFAULT
for (( ;; )); do
read -p "Enter search item: " SEARCHITEM
read -p "Enter number of lines to display: " NUMLINES
readarray -t MATCHEDFILES < <(exec grep -rl "$SEARCHITEM" "$SEARCHDIR")
echo "Please select a file to search into. Press C to cancel, or X to exit."
select FILE in "${MATCHEDFILES[#]}"; do
if [[ -n $FILE ]]; then
readarray -t MATCHEDLINES < <(exec grep -n "$SEARCHITEM" "$FILE")
echo "Please select starting line."
select LINE in "${MATCHEDLINES[#]}"; do
if [[ -n $LINE ]]; then
LINENUM=${LINE%%:*}
tail -n "+$LINENUM" "$FILE" | head -n "$NUMLINES"
elif [[ $REPLY == [cC] ]]; then
break
elif [[ $REPLY == [xX] ]]; then
break 3
fi
done
elif [[ $REPLY == [cC] ]]; then
break
elif [[ $REPLY == [xX] ]]; then
break 2
fi
done
done
New version:
#!/bin/bash
shopt -s extglob
SEARCHDIR_DEFAULT="./"
NUMLINES_DEFAULT=10
for (( ;; )); do
until
read -ep "Enter search directory [$SEARCHDIR_DEFAULT]: " SEARCHDIR
[[ $SEARCHDIR =~ ^[[:space:]]*$ ]] && SEARCHDIR=$SEARCHDIR_DEFAULT
[[ -d $SEARCHDIR ]]
do
echo "Please enter a valid directory."
done
SEARCHDIR_DEFAULT=$SEARCHDIR
until
read -p "Enter search item: " SEARCHITEM
[[ ! $SEARCHITEM =~ ^[[:blank:]]*$ ]]
do
echo "Please enter a valid search item."
done
until
read -p "Enter number of lines to display [$NUMLINES_DEFAULT]: " NUMLINES
[[ $NUMLINES =~ ^[[:space:]]*$ ]] && NUMLINES=$NUMLINES_DEFAULT
[[ $NUMLINES =~ ^[[:digit:]]+$ && NUMLINES -gt 0 ]]
do
echo "Please enter a valid number of lines."
done
NUMLINES_DEFAULT=$NUMLINES
readarray -t MATCHEDFILES < <(exec grep -rle "$SEARCHITEM" "$SEARCHDIR")
P1="Please select a file to search into. [ENTER = show list again, C = cancel, X = exit]: "
P2="Please select starting line. [ENTER = show list again, C = cancel, X = exit]: "
PS3=$P1
select FILE in "${MATCHEDFILES[#]}"; do
if [[ -n $FILE ]]; then
readarray -t MATCHEDLINES < <(exec grep -ne "$SEARCHITEM" "$FILE")
if [[ ${#MATCHEDLINES[#]} -eq 0 || ! ${MATCHEDLINES[0]} =~ ^[[:digit:]]+: ]]; then
echo "No matching lines were extracted. Perhaps file is a binary."
elif [[ ${#MATCHEDLINES[#]} -eq 1 ]]; then
LINENUM=${MATCHEDLINES[0]%%:*}
echo "----------------------------------------"
tail -n "+$LINENUM" "$FILE" | head -n "$NUMLINES"
echo "----------------------------------------"
else
PS3=$P2
select LINE in "${MATCHEDLINES[#]}"; do
if [[ -n $LINE ]]; then
LINENUM=${LINE%%:*}
echo "----------------------------------------"
tail -n "+$LINENUM" "$FILE" | head -n "$NUMLINES"
echo "----------------------------------------"
elif [[ $REPLY == [cC] ]]; then
break
elif [[ $REPLY == [xX] ]]; then
break 3
fi
done
PS3=$P1
fi
elif [[ $REPLY == [cC] ]]; then
break
elif [[ $REPLY == [xX] ]]; then
break 2
fi
done
done

Resources