4

I am trying to make a simple email script so that other users can email me from the terminal.

The script is being made partly as a learning experience, and party because the other users are not very linux savvy, so I want them to easily be able to email from terminal, without having to learn the programs, and syntax.

I have ubuntu 16.04.1 Server

So far this is what I am working with:

clear
echo -e "Please Enter Subject of email:\n "
read subject

echo -e "\n\nFrom: $USER\nTo: Adminstrator\nSubject: $subject\n\nEnter body of email: \n "
read body
echo -e "\nYour message is as follows:\n"
echo -e "\nFrom: $USER\nTo: Administrator\nSubject: $subject\n\n"
echo -e "$body \n\n"

read -p "Send to Administrator (y/n)?" choice
case "$choice" in
  y|Y ) echo "$body" | mail -s "$subject" email@gmail.com;;
  n|N ) echo "Roger Dodger. Email canceled";;
    * ) echo "invalid";;
esac

Things it can do:

  • request input for subject and body

  • send input to their respective variables.

  • call those variables, and send the email.

Things I want it to be able to do:

  • Allow pressing enter to go to a new line

  • Allow backspace

I have been everywhere twice, and I assume I am missing something, or lack some fundamental knowledge about how I can incorporate these features. I have tried using read with delimiters, cat with delimiters while loops (whatever that is) I have setup variable, aliases. I have read man pages http://tldp.org/LDP/Bash-Beginners-Guide/html/sect_08_02.html http://ss64.com/bash/read.html I have tried piping it and substituting with $() I have tried sending the input to a file and calling that file.

Iam running out of ideas.

I could not get the terminal provide a a section to type in at all when using cat, but I could get it to provide the prompt with read.

I am thinking I want similar to

cat >> SEND && echo | ${BODY) 

and then where the script calls for it, just provide the ${BODY} variable.

In a terminal by itself, I successfully ran:

cat << SEND && echo | ${BODY} && echo "${BODY}"

which allowed me to type multiple lines, until I typed SEND, and the pasted the ${BODY} variable whos output was correct.

Any ideas are apreciated.

  • 1
    Are you sure you want it to work this way? As a general rule, avoid having your users enter text manually after launching the program. That means i) it's hard to edit and has all the issues you mention in your question; ii) if a user makes a mistake, they need to start again from the beginning; iii) even using heredocs (which are what you're looking for), the user needs to manually type the end marker. Would you be open to alternatives like i) using command line arguments to pass the subject and a file for the email? ii) using GUI tools (if installed) to enter the text instead of the terminal? – terdon Sep 27 '16 at 7:48
  • Alternatively consider calling either a terminal text editor such as Nemo or a GUI text editor such as Gedit. When the user exits the editor, then give them the option to cancel, reedit, or send. – L. D. James Sep 27 '16 at 8:22
  • @L.D.James yeah, but I doubt the OP has a GUI installed since it's a server. – terdon Sep 27 '16 at 8:54
  • @terdon Yea, I figured as much. That's why I mentioned Nemo first. Of course a simple sud apt install gedit would provide the GUI to the users logging in. Just some input for immediate full functionality for arrow keys and editing. While I understand the OP made a reference to learning experience. Many applications are a compilation of the Linux tools on the system. But of course, for terminal experience, I could see a lot of merit in including Nemo in the mix. Thanks for the nudge of the OP's intentions! – L. D. James Sep 27 '16 at 10:03
  • @L.D.James fair enough, I added another script to my answer which uses the default $EDITOR. – terdon Sep 27 '16 at 10:11
2

I don't think what you're asking for is possible. When using read, you only have access to very minimal text editing abilities, it simply isn't designed for what you want it to do. Even if you can find a way to allow backspace and newlines, it is still a very cumbersome way of asking your users to provide input. It is very rarely a good idea to expect your users to type text directly into the running program. I suggest an alternative:

  1. Have your script read the subject from the command line.
  2. Have it read the body from an input file.

That way, your user doesn't need to type everything out manually, your script can be automated to send multiple emails, greatly increasing its usefulness and your users don't need to start everything again from scratch if they make a typo. Finally, they can format the email body as they want. So, for example, you can do something like this:

#!/bin/bash
## Parse command line options. 
while getopts ":s:f:" opt; do
  case $opt in
            s)
                subject="$OPTARG"
                ;;
            f)
                file="$OPTARG"
                ## Exit if the file isn't a file or isn't readble               
                if [[ ! -r "$file" && ! -f "$file" ]]; then
                    echo "File doesn't exist or isn't readble"
                        exit
                fi
                ;;
    \?)
      echo "Invalid option: -$OPTARG" >&2;;
  esac
done
## Read the email body
body=$(cat "$file")

## Inform the user. The "\033[1m" starts bold formatting and the
## "\033[0m" ends it. Remove them if you don't want bold. 
echo -e "\033[1mYour message is as follows:\033[0m"
cat<<EoF

From: $USER
To: Administrator
Subject: $subject

$body

------------------
EoF

read -p "Send to Administrator (y/n)?" choice
case "$choice" in
  y|Y ) echo "$body" | mail -s "$subject" email@gmail.com;;
  n|N ) echo "Roger Dodger. Email canceled";;
    * ) echo "invalid";;
esac

Then, you give it the subject and an input file. For example, if you have a file called message with the following contents:

$ cat message 
Dear John,

I hope this email finds you well. I am writing to inquire about the
price of the bridge you are selling. I would love to have a bridge
like that in my garden since I think it would make my cows look
larger.

Looking forward to hearing from you,

best,

Jack. 

You would run the script like this:

$ foo.sh -s "about that bridge..." -f message 
Your message is as follows:

From: terdon
To: Administrator
Subject: about that bridge...

Dear John,

I hope this email finds you well. I am writing to inquire about the
price of the bridge you are selling. I would love to have a bridge
like that in my garden since I think it would make my cows look
larger.

Looking forward to hearing from you,

best,

Jack. 

------------------
Send to Administrator (y/n)?

Alternatively, if you insist on having the users write the message on the fly, you could use the default editor available on your system:

#!/bin/bash

clear
echo -e "Please Enter Subject of email:\n "
read subject

## create a temp file
tmpfile=$(mktemp)

cat<<EoF

Press Enter to open an editor where you can write the body of the email.
When finished, save and exit.
EoF
read

## Open the editor
"$EDITOR" "$tmpfile"

## Read the body and delete the tmp file
body=$(cat "$tmpfile")
rm "$tmpfile"
## Inform the user. The "\033[1m" starts bold formatting and the
## "\033[0m" ends it. Remove them if you don't want bold. 
echo -e "\033[1mYour message is as follows:\033[0m"
cat<<EoF

From: $USER
To: Administrator
Subject: $subject

$body
EoF

read -p "Send to Administrator (y/n)?" choice
case "$choice" in
  y|Y ) echo "$body" | mail -s "$subject" email@gmail.com;;
  n|N ) echo "Roger Dodger. Email canceled";;
    * ) echo "invalid";;
esac

Finally, if you insist on making your life and that of your users more difficult than it needs to be, you can use cat > $tmpfile to write the body to the tmpfile, tell your users to hit Ctrl+D when they've finished writing:

#!/bin/bash

clear
echo -e "Please Enter Subject of email:\n "
read subject

## create a temp file
tmpfile=$(mktemp)
## Enter the body
printf '\nPlease enter the body of the email below. Hit Ctrl+D when done.\n\n'
cat > $tmpfile
body=$(cat "$tmpfile")
rm "$tmpfile"
## Inform the user. The "\033[1m" starts bold formatting and the
## "\033[0m" ends it. Remove them if you don't want bold. 
echo -e "\033[1mYour message is as follows:\033[0m"
cat<<EoF

From: $USER
To: Administrator
Subject: $subject

$body
EoF

read -p "Send to Administrator (y/n)?" choice
case "$choice" in
  y|Y ) echo "$body" | mail -s "$subject" email@gmail.com;;
  n|N ) echo "Roger Dodger. Email canceled";;
    * ) echo "invalid";;
esac
  • When I get back to my machine I will respond in more debth to all of this. I really appreciate all the feedback and effort. I was trying to avoid using a text file, or a text editor, even on the spot, as that requires knowledge of using said editor. GUI is not ideal, but I dont shun for thinking out of the box. – Stevener78 Sep 28 '16 at 12:39
  • For my knowledge, I know if I use cat << EOF in the terminal it will allow me to type, backspace, new line etc, and it can be sent to a file, or script, so I think if I could find a way to incorporate the cat << EOF into my script, in a way that that provides the place to type, like read does, then it would be the best option. However, as of right now, anytime I try and throw cat << EOF into the script (in place of read) no matter what, I seem to get the response " Missing EOF" not gotten a chance to look in debth into the proposed scripts, but I sure they will work flawlessly as alternate – Stevener78 Sep 28 '16 at 12:48
  • 1
    @Stevener78 yes, that's because you're running cat <<EoF in the script, so the shell will read the script and look for the EoF in the script. In any case though, please put some serious thought into the idea of not asking your users to type things out manually. Say you get this to work somehow, and the user hits Ctrl+C by mistake after having entered 100 lines of email or whatever. What then? Have you ever seen a program that asks you to enter text (more than a few characters) after it has started running? This is really bad design. My 2nd solution lets you write perfectly well. – terdon Sep 28 '16 at 12:53
  • I do understand where you are coming from. However, in reality, this little script is only gonna be used as like a "Hey, man. can you grant me access to x folder?" As a learning experience, I wanted to see how much I could get this script to do. Give me a better idea of how certain commands are ran, and how they can be manipulated etc. This server is really going to only have my roommates in it. Anything lengthy, and they will likely tell me in person. However, I'm sure they would see a custom script and think like me. "Wonder what all this can do" Where it's weaknesses are." – Stevener78 Sep 28 '16 at 13:01
  • 1
    @Stevener78 no, when the script reads "cat <<EoF", it understands "keep reading until the EoF is found". Since there is no EoF in the script, it will break. I respectfully suggest that your first learning exercise should be to learn that this isn't the way to go :) Have you understood how my 2nd script works? That's basically what you want, just easier. Anyway, see updated answer. The last script should work pretty much as you want. – terdon Sep 28 '16 at 14:16

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.