I keep getting an error on line 22: [!: command not found
My script Asks for a name, phone number, and date of birth and then amends these details to a comma separated value file called “birthday.csv”.
It then Sorts “birthday.csv” by date of birth. The newly sorted file is then displayed and calculates their age.
Can someone take a look at my script and see why this is popping up.
#!/bin/bash
a=0
while [ $a -lt 2 ];
do
echo Please enter a first name
read firstName
echo Please enter last name
read lastName
echo Please enter phone number
read phoneNumber
echo Please enter date of birth - format dd/mm/yyyy
read dob
echo "$firstName,$lastName,$phoneNumber,$dob" >> userBirthdays.csv
echo If you would like to add another person press 1 or enter 2 to proceed
read a
done
INPUT=./userBirthdays.csv
OLDIFS=$IFS
IFS=","
[! -f INPUT] & while read Name Surname Telephone DOB
do
birthMonth=${DOB:0:2}
birthDay=#10${DOB:3:2}
birthYear=${DOB:6:4}
currentDate=`date +%d/%m/%Y`
currentMonth=${currenDate:0:2}
currentDay=#10${currentDate:3:2}
currentYear=${currentDate:6:4}
if [[ "$currentMonth" -lt "$birthMonth" ]] || [[ "$currentMonth" -eq "$birthMonth" && "$((#10$currentDay))" -lt "$((#10$birthDay))" ]]
then
let Age=currentYear-birthYear-1
else
let Age=currentYear-birthYear
fi
echo "Name : $Name"
echo "Surname : $Surname"
echo "Telephone : $Telephone"
echo "DOB : $DOB"
echo "Age : $Age"
echo "##########################################"
done < $INPUT
IFS=$OLDIFS
echo $DATE
exit 0;
Thank you in advance
you need a space between between the [ and ! chars, i.e.
[ ! -f $INPUT ] && while read ....
#^-^--------^-^---^------------
Note that you almost certainly want two '&' chars, as in my correction.
Thanks to #GordonDavisson for another '$' ;-)
IHTH
Separate [ and ! with a space, so [ will be a command, and ! will be its first argument, as you meant them to be.
(Not sure there are no other problems).
Related
I'm trying to create a bash script that reads a CSV with two columns:
first column = name
second column = URL
and try to download a PDF file from the URL on the second column with a random name with letters and numbers .pdf and change the name using the first column.
The PDF name could be duplicate so if is duplicate I want to add numbers like:
Example %20 $5000.pdf
Example %20 $5000.1.pdf
Example %20 $5000.2.pdf
Because if I try to download wget and curl will not auto-increment with the output option.
I tried a lot of things but my limitations are taking too much time.
I created a counter that add the line number to the end, but if I got a larger PDF there will be unnecessary auto-increment numbers. (code below)
There should be a better method, but my lack of knowledge is taking too much time. So any help with that will be really appreciated, I'm a beginner on bash scripts.
Thanks for any help in advance!
CSV example:
Example %20 $5000,HTTP://example.com/djdiede.pdf
Example %20 $5000,HTTP://example.com/djdi42322ede.pdf
Example %30 $1000,HTTP://example.com/djd4234iede.pdf
Example %50 $1000,HTTP://example.com/dj43566diede.pdf
Code so far:
#!/bin/bash -e
COUNTER=1
while IFS=, read -r field1 field2
do
COUNTER=$[$COUNTER +1]
if [ "$field1" == "" ]
then
echo "Line $COUNTER field1 is empty or no value set"
elif [ "$field2" == "" ]
then
echo "Line $COUNTER field2 is empty or no value set"
else
pdf_file=$(echo $field1 | tr '/' ' ')
echo "================================================"
echo "Downloading $COUNTER $pdf_file..."
echo "================================================"
pdf_file_test="$pdf_file.pdf"
if [ -e "$pdf_file_test" ]; then
echo -e "\033[32m ^^^ File already exists!!! Adding line number at the end of the file: $pdf_file.$COUNTER.pdf \033[0m" >&2
wget -q -nc -O "$pdf_file."$COUNTER.pdf $field2
else
wget -q -nc -O "$pdf_file".pdf $field2
fi
fi
done < test.csv
This should help. I tried to stay close to your own coding style:
#!/bin/bash -e
LINECOUNTER=0
while IFS=, read -r field1 field2
do
LINECOUNTER=$[$LINECOUNTER +1]
if [ "$field1" == "" ]
then
echo "Line $LINECOUNTER: field1 is empty or no value set"
elif [ "$field2" == "" ]
then
echo "Line $LINECOUNTER: field2 is empty or no value set"
else
pdf_file=$(echo "$field1" | tr '/' ' ')
echo "================================================"
echo "Downloading $LINECOUNTER: $pdf_file..."
echo "================================================"
pdf_file_saveas="$pdf_file.pdf"
FILECOUNTER=0
while [ -e "$pdf_file_saveas" ]
do
FILECOUNTER=$[$FILECOUNTER +1]
pdf_file_saveas="$pdf_file.$FILECOUNTER.pdf"
done
if [ $FILECOUNTER -gt 0 ]
then
echo -e "\033[32m ^^^ File already exists!!! Adding number at the end of the file: $pdf_file_saveas \033[0m" >&2
fi
wget -q -nc -O "$pdf_file_saveas" "$field2"
fi
done < test.csv
Here's what I did:
use two counters: one for lines, one for files
when a file already exists, use file counter + loop to find the next 'empty slot' (i.e. file named <filename>.<counter-value>.pdf that does not exist)
fixed wrong line numbers (line counter needs to start at 0 instead of 1)
added double quotes where necessary/advisable
If you want to improve your script further, here are some suggestions:
instead of the big if ... elif ... else contruct, you can use if + continue, e.g. if [ "$field1" == "" ]; then continue; fi or even [ "$field1" == "" ] && continue
instead of terminating on error (#!/bin/bash -e), you could add error detection and handling after the wget call, e.g. if [ $? -ne 0 ]; then echo "failed to download ..."; fi
Hi I want to make a script that inserts contacts to a .csv file. It asks the user to type First Name, Last Name, and Phone number and then the output will be saved in a file named contacts.csv.
For example : John,Dawson,2102983187
Tried this but want to separate output with commas as it's on the example and it has to be in a loop.
#!/bin/bash
IFS=','
touch contacts.csv
echo "What's your contact's first name?"
read fname
echo "$fname" | grep '^[[:upper:]]\+[a-z]\{0,\}' >> contacts.csv
echo "What's your contact's last name?"
read lname
echo "$lname" | grep '^[[:upper:]]\+[a-z]\{0,\}' >> contacts.csv
echo "Whats your contact's phone number"
read phone
echo "$phone" | grep '[0-9]\{10\}$' >> contacts.csv
#!/bin/ksh
read fname?"What's your contact's first name? "
read lname?"What's your contact's last name? "
read phone?"What's your contact's phone number? "
[[ $fname =~ ^[[:upper:]][a-z]+$ ]] || { echo "$fname should be uppercase first char followed by lowercase chars" ; exit 1; }
[[ $lname =~ ^[[:upper:]][a-z]+$ ]] || { echo "$fname should be uppercase first char followed by lowercase chars" ; exit 2; }
[[ $phone =~ ^[[:digit:]]+$ ]] || { echo "$fname should be digits only" ; exit 3; }
echo "$fname,$lname,$phone" >> contacts.csv
Changing IFS isn't necessary. IFS=Input separator, and from your checks, I assume, that you don't want , in your input.
Read can provide a prompt, no need to use echo.
I would check all inputs first, and then write to output.
If you put the block between
while true; do
...
done
An empty input will stop the processing
I am new to bash scripting. I have a script that i am working on for a school project and its not working as expected. I keep receiving a error on my if then statements.
#!/bin/bash
echo –e “Would you like to add a new employee’s information? y/n \n”
read EMPLOYEE
if [ $EMPLOYEE = “y” –o $EMPLOYEE = “Y” ]
then
echo –e “Please enter employee’s first name \c”
read FIRST
echo –e “Please enter employee’s last name \c”
read LAST
echo –e “Please enter empolyee’s ID \c”
read ID
echo –e “$FIRST\t$LAST\t$ID” >> database
fi
echo –e “Would you like to search for an employee? y/n \n”
read SEARCH
if [ $SEARCH = “y” –o $SEARCH = “Y” ]
then
echo –e “Enter the first name, last name or employee ID to search for. \c”
read WORD
grep “$WORD” database
fi
With even a bash version 3 the following should work as you intended and be somehow shellitical correct ;-)
#!/bin/bash
printf "Would you like to add a new employee’s information? y/n \n"
read -r EMPLOYEE
if [ "$(tr '[:upper:]' '[:lower:]' <<<"$EMPLOYEE")" = "y" ]
then
printf "Please enter employee’s first name : \c"
read -r FIRST
printf "Please enter employee’s last name : \c"
read -r LAST
printf "Please enter empolyee’s ID : \c"
read -r ID
printf "%s\t%s\t%s" "$FIRST" "$LAST" "$ID" >> database
fi
printf "Would you like to search for an employee? y/n \n"
read -r SEARCH
if [ "$(tr '[:upper:]' '[:lower:]' <<<"$SEARCH")" = "y" ]
then
printf "Enter the first name, last name or employee ID to search for. : \c"
read -r WORD
grep "$WORD" database
fi
Notes on refactoring/repair:
Do not rely on echo -e in most cases printf works better and looks more like coding,
use read -rto mot mangle backslashes on input,
quote variables from input (with real ASCII quote characters), as the shell parser otherwise does funny things,
do not test against y and Y but simply lowercase the string received to only compare one variant.
Use some indent concept to track, and
use a shell linter as suggested by a commenter already (that one works nicely currently).
The above code does have no errors indicated in the above mentioned linter.
Small task for as exercise for the reader (thanks andlrc :)
Use lowercase variable names and I amend meaningful names
Enjoy the learning of the shell coding!
I'm fairly new to Unix shell or bash programming.
I'm doing a project on an inventory program. I'm wondering if it is possible to check for numbers and letters within a function.
For example, I were to sell a hamburger, within the price I would only put numbers, if I were to input letters in it, how can i check that I have put letters instead of numbers in it?
Sorry if my English is bad, English is not my first language.
echo -n "Food :"
read food
echo -n "Price :"
read price
You could do it in multiple ways.
Using test with if condition:
if [ $var -eq $var 2> /dev/null ]
then
...
fi
OR
if [[ $var == +([0-9]) ]]
then
## its a number
fi
Using egrep with regex command like:
if [[ echo $var | egrep -q '^[0-9]+$' ]]
then
####...its a number...###
fi
It would be like:
read price
expression='^[0-9]+$'
if ! [[ $price =~ $expression ]] ; then
echo "Error: Please enter a number" >&2; exit 1
fi
Here, ^[0-9]+$ shows "starting(^) with a number[0-9] and continuing the same till the end($)
Hope that helps.
Allowing prices to have decimal points, this checks for valid prices:
re='^[0-9]+\.?[0-9]*$'
[[ $price =~ $re ]] && echo "Is Valid"
A more complete working example, which provides and error message for bad numbers, is:
read -p "Enter the food name: " food
re='^[0-9]+\.?[0-9]*$'
while true
do
read -p "Enter the price: " price
[[ $price =~ $re ]] && break
echo " You must enter a valid number"
echo " Please try again"
done
echo ""
echo "The price of $food is $price"
The following shows the above in action:
$ bash script.sh
Enter the food name: Hamburger
Enter the price: a lot
You must enter a valid number
Please try again
Enter the price: 1.9y
You must enter a valid number
Please try again
Enter the price: 0.99
The price of Hamburger is 0.99
How it works
The regular expression is ^[0-9]+\.?[0-9]*$. Let's look at it one piece at a time:
^ matches the beginning. This assures that no non-number characters precede the number
[0-9]+ matches one or more numbers
\.? matches the decimal point, if there is one.
[0-9]* matches numbers, if any, following the decimal point.
$ matches at the end of the string, assuring that there are no non-number characters following the number.
I am getting the error: line 34: #10#1001: syntax error: operand expected (error token is "#10#1001")
I have just changed
[! -f INPUT ] &
to
[ ! -f INPUT ] &&
and now i am getting the error
line 34: #10#1001: syntax error: operand expected (error token is "#10#1001")
when i run the script.
The script inputs users names, phones numbers and dob and then sorts the infomation and calculates their ages.
What could be causing this error as i cannot work out what operand it is demanding
#!/bin/bash
a=0
while [ $a -lt 2 ];
do
echo Please enter a first name
read firstName
echo Please enter last name
read lastName
echo Please enter phone number
read phoneNumber
echo Please enter date of birth - format dd/mm/yyyy
read dob
echo "$firstName,$lastName,$phoneNumber,$dob" >> userBirthdays.csv
echo If you would like to add another person press 1 or enter 2 to proceed
read a
done
INPUT=./userBirthdays.csv
OLDIFS=$IFS
IFS=","
[ ! -f INPUT ] && while read while read Name Surname Telephone DOB
do
birthMonth=${DOB:0:2}
birthDay=#10${DOB:3:2}
birthYear=${DOB:6:4}
currentDate=`date +%d/%m/%Y`
currentMonth=${currenDate:0:2}
currentDay=#10${currentDate:3:2}
currentYear=${currentDate:6:4}
if [[ "$currentMonth" -lt "$birthMonth" ]] || [[ "$currentMonth" -eq "$birthMonth" && "$((#10$currentDay))" -lt "$((#10$birthDay))" ]]
then
let Age=currentYear-birthYear-1
else
let Age=currentYear-birthYear
fi
echo "Name : $Name"
echo "Surname : $Surname"
echo "Telephone : $Telephone"
echo "DOB : $DOB"
echo "Age : $Age"
echo "##########################################"
done < $INPUT
IFS=$OLDIFS
echo $DATE
exit 0;
You have two mistakes on this line:
[ ! -f INPUT ] && while read while read Name Surname Telephone DOB
It should be:
[ -f ${INPUT} ] && while read Name Surname Telephone DOB
I recommend debugging your script with:
bash -x /path/to/birthdays.bash
This will print command traces before executing each command.
-lt comparison (and friends) are for integers, not strings. And you turn currendDay into a non-integer string by prepending #10 to it (you do it twice, by the way).
Your intentions here are not very clear. Do you want to compare strings? Then use < instead of -lt. Do you want currentDay to be a number? Then don't prepend #10 to its value (in both places you do it now).