Tuesday, December 11, 2007

Mail a Text File from the Shell

Would you like an easy way to send files to someone outside of your local network, or quickly test a mail server?

Well the old "mail" command is still available, and it comes in handy for sending config files, or using within a shell script.

This is all you need:

$ mail -s test me@company.com < /etc/hosts

That's the shortcut. If you want more details, you can read the rest of the post...

Using the mail command

1) Use Mail Interactively to read mail
a) type Mail
b) enter the number of the message to read, press enter
c) press space to page down, n for next message
d) ? for help
e) q to quit

2) Use mail Interactively to send mail
a) type mail command followed by email address
b) Enter subject, press enter
c) Enter text of message
d) press Ctrl-d on a line by itself when finished
e) Enter CC: if desired, or press Ctrl-d again

$ mail user@company.com
Subject: test
Here is my test message
<Ctrl-d>
Cc: <Ctrl-d>$

For a little less work, add the subject to the command prompt. Remember to use quotes if the subject it more than one word.

$ mail -s "mounting" user@company.com
Scott,

Can you help me troubleshoot my mount problem?

<Ctrl-d>
Cc: <Ctrl-d>$


3) Use mail to Send a Text File

Imagine that you just asked your friend to help you troubleshoot your partitions, and then he asked you to send a copy of your fstab file. Your first thought may be, "how can I copy and paste this into my mail program?", or "how can I get this file to my Windows machine where my mail program is running?" If you have synergy running, it's easy enough -- http://systemnotesorg.blogspot.com/2007/05/how-to-copy-and-paste-in-linux-and.html Then you think, I can use firefox, and attach the file to an email, or paste the text into the message, but that takes a few steps. And then what if X isn't running?

Sure you could copy the file to an nfs mounted partition, or scp the file to another machine that has X or Windows running. But I think one of the easiest ways to email a text file is to use a single mail command from a shell prompt:

$ mail -s "My fstab File" myfriend@mycompany.com < /etc/fstab

Remember to only send text files, unless you want to uuencode a binary file, but that is usually not the most practical way to move binaries. That sounds like a topic for another day...

Summary of the mail program

In summary, here are three common ways to use Mail / mail:

1) Interactively read: Mail
2) Interactively send: mail user@company.com <ctrl-d> to end
3) Automated send: mail -s "My fstab File" myfriend@mycompany.com < /etc/fstab

Note that option 3 may come in handy for testing a recently installed mail server, such as may be required on an RHCE exam. It is also good for mailing log output, which is what logwatch does.

A Little Background

You may be familiar with the Mail command (upper-case "M"), which is convenient for reading mail on the system when pine or mutt hasn't been installed. The the little mail command (lower-case "m") is very convenient for sending mail from the command prompt with a simple one-liner command.

On modern linux systems Mail and mail may be the same program but historically they were different programs. On your system you can check for the mail program:

[ /]$ which mail
/bin/mail
[ /]$ which Mail
/usr/bin/Mail
[ /]$ ll /usr/bin/Mail
lrwxrwxrwx 1 root root 14 Sep 20 2006 /usr/bin/Mail -> ../../bin/mail
[ /]$

Note that /usr/bin/Mail is a symlink to /bin/mail on this rhel4 system.

Saturday, December 1, 2007

Vim Tips -- Search and Replace


Did you ever wonder if there was an easy way to send an example file for someone to look at, but still keep your private information safe?

Sure you can use a sed, or perl one-liner, as I discussed: using-bash-and-sed-to-modify-text-file.html, but why not use search and replace in vi / vim before publishing your info? That way you can see exactly what you are changing. Remember, while vim is installed by default on most Linux distros, is also available for Windows, and it is free -- http://www.vim.org

Suppose you have part of a log file, or nmap output that contains real hostnames, a real domain name, and real IP addresses. You want someone to help you troubleshoot something, but you don't what to give the real info for everyone to see.

Start with the info in a text file, such as this:

Host realhost-1.realdomain.com (192.168.0.54) appears to be up.
Host realhost-2.realdomain.com (192.168.0.55) appears to be up.
Host realhost-3.realdomain.com (192.168.0.56) appears to be up.
Host realhost-4.realdomain.com (192.168.0.57) appears to be up.
Host realhost-5.realdomain.com (192.168.0.150) appears to be up.
Host realhost-6.realdomain.com (192.168.0.151) appears to be down.

With a few quick search commands, the output can be converted to this:

Host xyz-1.example.com (10.10.10.34) appears to be up.
Host xyz-2.example.com (10.10.10.35) appears to be up.
Host xyz-3.example.com (10.10.10.36) appears to be up.
Host xyz-4.example.com (10.10.10.37) appears to be up.
Host xyz-5.example.com (10.10.10.130) appears to be up.
Host xyz-6.example.com (10.10.10.131) appears to be down.


O.K., here are the commands.

The fastest way to replace "realhost" with "xyz" on every line in the file is to use the %s command:

To enter command mode, press <esc>
:%s/realhost/xyz/g

What this means is
% = every line in the file
s = search
/realhost/ = pattern to search for
/xyz/ = text to replace with
g = global

Suppose you want to do this on almost all lines, but would like to confirm each replacement. That's easy, just use a "c" for confirm.

:%s/realhost/xyz/gc

c = confirm

O.K., but what if you only want to replace a few lines in the file? Simply use the line numbers separated by a comma instead of the "%" symbol:

:7,12s/192.168.0/10.10.10/g

Use ".", or nothing to search and replace only on the current line:

:s/5/3/gc

The items between the slashes are called regular expressions, and work as expected. When searching for special characters, e.g. "$ / ^", etc. they must be escaped by using a backslash "\" (Use \\ to get a literal backslash).

That's it. You're done, so save the file, and quit
<esc>
:wq!

Here is a list that may come in handy:

Vi / Vim Substitution and Regular Expressions Cheat Sheet


Vi / Vim Substitution and Regular Expressions
----------------------------------------------
% = every line in the file
s = search
/ / = pattern to search for
/ / = text to replace with
g = global
c = confirm each substitution
p = Print the line after the change is made

Regular Expressions (regex)
---------------------------
. = Matches any single character (including spaces), except newline.
* = Matches zero or more of the preceding single character
^ = Match at the beginning of line, used at the beginning of a regex
$ = Match at the end of the line
\ = Escape special characters
[ ] = Match any one character, e.g. [a-z] = lowercase, [^a-z] = not lowercase
\( = Save pattern
\< = Match characters at the beginning of a word

POSIX character classes
------------------------
[:alpha:] = Alphabetic characters
[:digit:] = Numeric characters
[:alnum:] = Alphanumeric characters
[:space:] = Whitespace characters
[:blank:] = Space and tab characters
[:cntrl:] = Control characters
[:upper:] = Uppercase characters
[:lower:] = Lowercase characters
[:graph:] = Printable and visible nonspace characters
[:print:] = Printable characters including whitespace
[:punct:] = Punctuation characters
[:xdigit:] = Hexadecimal characters

Metacharacters Used in Replacement Strings
-------------------------------------------
\n = Replaces with text matched by the nth pattern -- \( \)
\ = Escape special character
& = Replaced with entire text matched by the search pattern
~ = String found replaced by text of previous search
\u, \l = Change next character to upper or lowercase

Ping Multiple Hosts Using Bash Nmap and Fping

I explained how to get a list of hosts using nmap -- using-nmap-to-generate-host-lists.html, but here is another look at the subject.

The question is, how do I ping multiple hosts to find out which ones are down? Sure this could be considered a topic of system monitoring, but maybe you just want to reboot a bunch of machines, and make sure they all come back online. This quick check will tell you whether there is a problem or not.

Here are three methods for pinging a list of hosts:

1.) for host in `cat all.txt `;do ping -c 1 $host; done
2.) nmap -sP -R -iL all.txt
3.) sudo fping -u < xyz/all.txt

First, we assume that you have a text file named all.txt that contains a list of hostnames, one per line. Obviously, the examples here contain fake hostnames, domain names and IPs, as described in another article about vi: vim-tips-search-and-replace.html.

--- all.txt ---
xyz-1
xyz-2
xyz-3
xyz-4
xyz-5
xyz-6
xyz-7
xyz-8
--- end all.txt ---

1.) Use a for loop in the shell

$ for host in `cat all.txt `;do ping -c 1 $host; done
PING xyz-1.example.com (10.10.10.34) 56(84) bytes of data.
64 bytes from xyz-1.example.com (10.10.10.34): icmp_seq=0 ttl=62 time=0.472 ms

--- xyz-1.example.com ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.472/0.472/0.472/0.000 ms, pipe 2
PING xyz-2.example.com (10.10.10.35) 56(84) bytes of data.
64 bytes from xyz-2.example.com (10.10.10.35): icmp_s eq=0 ttl=62 time=0.459 ms

--- xyz-2.example.com ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.459/0.459/0.459/0.000 ms, pipe 2
PING xyz-3.example.com (10.10.10.36) 56(84) bytes of data .
64 bytes from xyz-3.example.com (10.10.10.36): icmp_seq=0 ttl=62 time=0.390 ms

--- xyz-3.example.com ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.390/0.390/0.390/0.000 ms, pipe 2
PING xyz-4.example.com (10.10.10.37) 56(84) bytes of data.
64 bytes from xyz-4.example.com (10.10.10.37): icmp_s eq=0 ttl=62 time=0.382 ms

--- xyz-4.example.com ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.382/0.382/0.382/0.000 ms, pipe 2
PING xyz-5.example.com (10.10.10.130) 56(84) bytes of data .
64 bytes from xyz-5.example.com (10.10.10.130): icmp_seq=0 ttl=63 time=0.195 ms

--- xyz-5.example.com ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.195/0.195/0.195/0.000 ms, pipe 2
PING xyz-6.example.com (10.10.10.131) 56(84) bytes of data .

--- xyz-6.example.com ping statistics ---
1 packets transmitted, 0 received, 100% packet loss, time 0ms

You can see that even for the six hosts we are checking for, there is quite a bit of extra info to look through to find out if a host is up or not.


$ for host in `cat xyz/all.txt `;do ping -c 1 $host; done | grep -v "\=" | grep -v PING
--- xyz-1.example.com ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms

--- xyz-2.example.com ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms

--- xyz-3.example.com ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms

Not as bad, but we can do better. The above example still takes three lines to show the results (hostname, %packet loss, and a blank line). Using a different tool give much better results, so why did I waste your time looking at this method? Well, it still may be easier to use ping, since it will most likely be installed by default, and you can do tricks with the input file that may not work with the other methods. For example, if your input file contains lines that start with a "#" you can use grep -v to skip those lines, but other tools may expect cleaner input.

2.) Use nmap

So let's try the same thing with nmap.

$ nmap -sP -R -iL all.txt
Reading target specifications from FILE: all.txt


Starting nmap V. 3.00 ( www.insecure.org/nmap/ )
Host xyz-1.example.com (10.10.10.34) appears to be up.
Host xyz-2.example.com (10.10.10.35) appears to be up.
Host xyz-3.example.com (10.10.10.36) appears to be up.
Host xyz-4.example.com (10.10.10.37) appears to be up.
Host xyz-5.example.com (10.10.10.130) appears to be up.
Host xyz-6.example.com (10.10.10.131) appears to be down.

Much cleaner, isn't it?

For even less info to sift through, you can use grep to look for hosts that are up or down.

$ nmap -v -sP -R -iL all.txt | grep down
Host xyz-6.example.com (10.10.10.131) appears to be down.

Then, of course it can be cleaned up further with a simple awk print statement.

$ nmap -v -sP -R -iL all.txt | grep down | awk '{print $2}'
xyz-6.example.com

3.) Use fping

Another option is fping, but it has the disadvantage that it is probably not installed by default, and it has to be run as root (or sudo), unless additional configuration is performed. It does give very nice output, and is very fast.

Show me all hosts from my list that are down:
$ sudo fping -u < all.txt
xyz-6