Sun Solaris   Solaris Tips

Finding text strings in binary files

Ever wondered what's inside some of those binary files on your system (binary executables or binary data)? Several times I've gotten error messages from some command in the Solaris system, but I couldn't tell where the error was coming from because it was buried in some binary executable file.

The Solaris "strings" command lets you look at the ASCII text buried inside of executable files, and can often help you troubleshoot problems. For
instance, one time I was seeing error messages like this when a user was trying to log in:

    Could not set ULIMIT

I finally traced the problem down to the /bin/login command by running the "strings" command like this:

    root> strings /bin/login | more

The strings command lists ASCII character sequences in binary files, and help me determine that the "Could not set ULIMIT" error was coming from
this file. Once I determined that the error message I was seeing was coming from this file, solving the problem became a simple matter.

A vi macro to display line numbers

While the vi editor has been known to be a little rough around the edges, it still has some pretty nice features. One of those features is the ability to define cool macros. Here we'll show you how to create two macros--one to display line numbers of the file you're editing, and one to hide the line numbers.

First, create a file named .exrc in your home directory (or edit the current file if it already exists). This is the configuration file that vi reads when it is started. Put the following two lines into this file:

    :map #1 :set number^M
    :map #2 :set nonumber^M

(A very important note: create the ^M characters in this file by typing the key sequence [CTRL-V][CTRL-M]. This key sequence embeds an actual ^M
character (the carriage return) into the file.)

Now, save this file and re-start vi. From vi's command-mode, you'll now be able to display line numbers beside your file contents by hitting the [F1]
function key, and clear line numbers by hitting the [F2] key. If you like these macros, create your own powerful macros by following this same
technique!

Use traceroute to find network problems

With the Internet and TCP/IP networks all around us, it's important to know a little bit about TCP/IP network troubleshooting. The Solaris "traceroute"
command lets you trace the route your packets are taking to get from your current workstation to a remote workstation you're trying to reach.

For instance, suppose you're on the Internet and you're not getting an HTTP response from a remote server named www.zdtips.com. You try to "ping" the
remote site like this:

    ping www.zdtips.com

but get no answer. Is the remote server down, or is there a broken link between you and the remote site? Issue the following command to see where
the problem lies:

    traceroute www.zdtips.com

The traceroute command works its way through the network, and tells you the path it's taking to get to the destination site as it goes along. Watch for
the point traceroute fails to learn more about the network segment that has failed. Of course you can also try this on working connections to learn more about how your Internet packets get from one site to another.

Using telnet's command mode

When you use the Solaris telnet command to log in from one site to another, don't forget that you can enter telnet's command mode at any time during
your session, usually by entering the following key sequence:

    [CTRL] ] (the control key and right-bracket key at the same time)

When this is successful, you'll see the following prompt:

    telnet>

Then, from the "telnet>" prompt, enter "?" to learn more about the available telnet commands. A few commands that will give you more help at the prompt are shown below:

    display        displays many current Telnet settings
    send ?        displays commands you can "send" to the remote site
    set ?             displays variables that can be set

One of my favorites (mostly when fooling around) is:

    send ayt

which means "send this message: are you there?".

Keep commands running after you leave with nohup

It's 3 p.m., and you want to start a long job running. Unfortunately, you can't be sure that the job will finish by 5 p.m. when you need to leave, and the company is very strict about making sure you log off when you leave. However, if you log off the system, the job will be stopped. What can you do?

On Solaris systems you can use the "nohup" (no hang-up) command to keep jobs running long after you log off the system. Using nohup tells the system not to "hang-up" on your job after you've logged off the system.

Here's how to run the job, and keep it running after you log off:

root> nohup my-long-job &

This creates a file named "nohup.out" in the current directory that contains the standard output of the command ("my-long-job") you're running.
Everyone is happy because the job keeps running, you get to leave at 5 p.m., and you're properly logged off the system.

Use rsh to run commands on somebody else's computer

Here's a hypothetical situation for you: On the "Sesame Street Network", you're logged into a Solaris workstation named "elmo", and you need to look
at the man pages for the tar command. Unfortunately the man pages aren't installed on this workstation.

If you're on a network of Solaris computers like this, and you know that the man pages are installed on another workstation named "bigbird", the solution is simple. Simply run the man command on bigbird, like this:

elmo> rsh bigbird "man ksh" | more

This runs the "man ksh" command on bigbird, and pipes the output of the command into the "more" command on elmo.

Here's a few other "rsh" commands you can run on remote workstations:

rsh bigbird "who"
rsh ernie "ps -ef"
rsh grover "ls -al /home"

Use CDPATH to traverse filesystems faster

If you're like many Solaris users and administrators, you spend a lot of time moving back and forth between directories in similar locations. For
instance, you might often work in your home directory (such as "/home/al"), the /usr/local directories, web page directories, or other user's home
directories in /home.

If you're often moving back-and-forth between the same directories, and you use the Bourne shell (sh) or Korn shell (ksh) as your login shell, you can
use the CDPATH shell variable to save yourself a lot of typing, and quickly move between directories.

Here's a quick demo. First move to the root directory:

    cd /

Next, if it's not set already, set your CDPATH shell variable as follows:

CDPATH=/usr/spool

Then, type this cd command:

    cd cron

What happens? Type this and see what happened:

    pwd

The result should be "/usr/spool/cron".

When you typed "cd cron", the shell looked in your local directory for a sub-directory named "cron". When it didn't find one, it searched the CDPATH
variable, and looked for a "cron" sub-directory. When it found a sub-directory named cron in the /usr/spool directory, it moved you there.

You can set your CDPATH variable just like your normal PATH variable:

    CDPATH=/home/al:/usr/local:/usr/spool:/home

Group commands together with parentheses

Have you ever needed to run a series of commands, and pipe the output of all of those commands into yet another command?

For instance, what if you wanted to run the "sar", "date", "who", and "ps -ef" commands, and wanted to pipe the output of all three of those commands
into the "more" command? If you tried this:

    sar -u 1 5; date; who; ps -ef | more

you'll quickly find that it won't work. Only the output of the "ps -ef" command gets piped through the "more" command, and the rest of the output
scrolls off the screen.

Instead, group the commands together with a pair of parentheses (and throw in a few echo statements for readability) to get the output of all these
commands to pipe into the more command:

(sar -u 1 5; echo; who; echo; ps -ef; echo; date; echo) | more

Use CDPATH to traverse file systems faster

If you're like many Solaris users and administrators, you spend a lot of time moving back and forth between directories in similar locations. For
instance, you might often work in your home directory (such as "/home/al"), the /usr/local directories, web page directories, or other user's home
directories in /home.

If you're often moving back-and-forth between the same directories, and you use the Bourne shell (sh) or Korn shell (ksh) as your login shell, you can
use the CDPATH shell variable to save yourself a lot of typing, and quickly move between directories.

Here's a quick demo. First move to the root directory:

    cd /

Next, if it's not set already, set your CDPATH shell variable as follows:

CDPATH=/usr/spool

Then, type this cd command:

    cd cron

What happens? Type this and see what happened:

    pwd

The result should be "/usr/spool/cron".

When you typed "cd cron", the shell looked in your local directory for a sub-directory named "cron". When it didn't find one, it searched the CDPATH
variable, and looked for a "cron" sub-directory. When it found a sub-directory named cron in the /usr/spool directory, it moved you there.

You can set your CDPATH variable just like your normal PATH variable:

    CDPATH=/home/al:/usr/local:/usr/spool:/home

A shell function to get one character at a time from the end user

Many times people writing shell programs want to prompt an end-user to "Hit Any Key to Continue" during the middle of a shell program. However, they
often settle for lesser functionality, because they think it's not easy to get one keystroke at a time from the end user.

The "GetKeystroke" function shown below shows exactly how to get one character at a time from your end-users in your Bourne shell/Korn shell
programs:

-----------------------------------------------------------------
#!/bin/sh

GetKeystroke () {
trap "" 2 3
oldSttySettings=`stty -g`
stty -echo raw
echo "`dd count=1 2> /dev/null`"
stty $oldSttySettings
trap 2 3
}


#----------------------------------------------#
# Here's the main program. Hit "x" to exit. #
#----------------------------------------------#

keyStroke=""
while [ "$keyStroke" != "x" ]
do
clear
echo "HIT ANY KEY TO CONTINUE: \c"
keyStroke=`GetKeystroke`
done

-----------------------------------------------------------------

To test this program, try the following:

1. Save the code between the lines in a file named /tmp/keytest
2. Make the program executable: chmod +x /tmp/keytest
3. Run the program: /tmp/keytest

You'll see that you can hit one character at a time, without the need to hit the [Enter] key. When you're finished testing, type "x" to leave the
program.

Use the "at" command to run jobs some other time

Many times it's necessary to schedule programs to run at a later time. For instance, if your computer system is very busy during the day, you may need
to run jobs late at night when nobody is logged on the system.

Solaris makes this very easy with the "at" command. You can use the "at" command to run a job at almost any time--later today, early tomorrow...whenever.

Suppose you want to run the program "my_2_hour_program" at ten o'clock tonight. Simply tell the at command to run the job at 10 p.m. (2200):

    /home/al> at 2200
    at> my_2_hour_program > /tmp/2hour.out
    at> <CTRL><D>
warning: commands will be executed using /bin/ksh
job 890193600.a at Tue Mar 17 22:00:00 1998

Or suppose you'd like to run a find command at five o'clock tomorrow morning:

/home/al> at 0500 tomorrow
at> find /home > /tmp/find.out
at> <CTRL><D>
warning: commands will be executed using /bin/ksh
job 890215200.a at Wed Mar 18 05:00:00 1998

When you're at the "at" prompt, just type the command you want to run. Try a few tests with the at command until you become comfortable with the way
it works.

Head, tail, and sed

Want to see the first 5 lines of the /etc/passwd file? Pretty easy, just use the "head" command:

    head -5 /etc/passwd

Want to see the last 20 lines of the /etc/passwd file? Again, pretty easy, just use the "tail" command:

    tail -20 /etc/passwd

But what if you only want to see lines 10-15 of a given file? Neither the "head" nor the "tail" commands alone will do. Instead, use the "sed"
command to print the range of lines you want to see:

    sed -n '10,15p' /etc/passwd

This command works great, even if you want to see a few lines somewhere in the middle of a 100,000 line file.

Rearranging columns with awk

Have you ever had a column-oriented text file, similar to a spreadsheet, but the columns weren't in the order you wanted? For instance, suppose you
had the following information in a file named "checkbook.orig":

    COST         DATE            BALANCE
    10.00         040198           1000.00
    20.00         040298             980.00
    30.00         040298             950.00

The information is good, but you'd prefer to have the DATE column first, followed by the COST information in the second column, and the BALANCE
column third.

Using awk, you can easily rearrange the columns. The following command reads the data from the file named "checkbook.orig", and writes the data to
a file named "checkbook.new":

    awk '{print $2, $1, $3}' checkbook.orig > checkbook.new

This brief awk command reads each line of the original file, and for each line it reads, it writes an output line to the "new" file. As it writes
each record to the new file, it rearranges the order of the columns, so that the columns now appear in the desired order!

If you prefer a little more control of the printed output, awk also has a "printf" function that's very similar to printf in the "C" programming
language. Here's the same example, with a tab character in-between each column of the output:

    awk '{printf ("%s\t%s\t%s\n", $2, $1, $3) }' checkbook.orig > checkbook.new

The awk command is a powerful programming utility that takes care of things like opening files and reading each line automatically, so all you have to
do is tell awk how to process each line as it goes by.

Add spice to interactive shell programs with tput

When you're writing interactive shell programs, you often want to add a little more spice to your user interface - those special nuances that make
your application more appealing. The Solaris "tput" command can be used to enhance your user interface.

Here's a couple of quick tput commands that can spice up your user interface:

The tput command can be used to make text appear bold on terminals that support a bold appearance. Making text appear bold works very well for
titles, or at times when you want a word or phrase to stand out from the rest of the text. Try these three commands at your command line to make
the "[Enter]" portion of the following echo statement output appear bold:

bold=`tput smso`
norm=`tput rmso`
echo "Hit the ${bold}[Enter]${norm} key to continue: \c"

You can also position the cursor on-screen with the tput command. Type this command at the command line to see what happens:

    tput cup 10 40

I once wrote a crude interactive screen editor using tput cup to properly position the cursor when the user hit the various arrow keys.

Create a directory and move into it at the same time

Question: How often do you create a new directory and then move into that directory in your next command?  Answer: Almost always.

I realized this trend in my own work habits, so I created a simple shell function to do the hard work for me.

     md () {
          mkdir -p $1  &&  cd $1
     }

This is a Bourne shell function named "md" that works for Bourne and Korn shell users. It can be easily adapted for C shell users.

Taking advantage of the -p option of the mkdir command, the function easily creates multi-level subdirectories, and moves you into the lowest level of the directory structure.  You can use the command to create one subdirectory like this:

     /home/al> md docs
     /home/al/docs> _

or you can create an entire directory tree and move right into the new directory like this:

     /home/al> md docs/memos/internal/solaris8
     /home/al/docs/memos/internal/solaris8>

Easily convert man pages to text documents

Have you ever wanted to convert a man page into a plain text document?
I do this occasionally when I want to share information via an email or other document format.

I used to think this was difficult, but then I discovered a simple way to do it. Here's the wrong way to write the man page for the ls command into a text file named ls.bad:

     man ls > ls.bad

This keeps all of the formatting characters in your document, which is generally not what you want.  Here's a better way that eliminates those formatting characters:

     man ls | col -b > man.txt

The col command with the -b option removes the undesirable backspace characters from the text stream, so the only thing left in your document is the text you want, in the format you want.

How to page more than one command at a time

Have you ever wanted to group a bunch of commands into a paging program like "page" or "more", but didn't know how?

As a system administrator, I always worry about certain things, like who's doing what, what processes are running, what the network traffic looks like, etc.  One day I decided to create a simple alias that would combine all the commands I wanted into one big chunk of information.  Then I realized that it wouldn't all fit into one screen. 
Fortunately I knew how to group all of the commands together, so the "more" command could handle them as one set of input.

First, here's the wrong way to try to page a sequence of four commands:

     date; netstat -i; whodo; ps -ef | more

The only command that gets paged properly here is the "ps -ef" command
--the rest of them scroll off the screen before you can read them.

Here's the correct way to page four commands so they're all controlled
by "more":

     (date; netstat -i; whodo; ps -ef) | more

Once you find the commands you want to group together, you can combine them into an alias or shell program.  I recommend a shell program for this, because a few "echo" statements sure make it easier to see where one command ends and the next command begins!

Initializing log files

Like all Unix file systems, open log files can cause a real problem when they get too large and need to be deleted. The problem is, if you delete an open file, the link is removed, but all of the inodes are lost. Even worse, if the program continues to log to the file, the link never re-appears, and additional inodes are lost and are unrecoverable. I suggest two solutions to the above problems.

If you have lost inodes, a simple reboot (make sure FSCK is run on startup) will recover lost inodes and missing filespace.

To empty (or zero out) an open log file, simply issue the following command:

date > logfile

This will 'empty' the file and insert as the first line the output from the date command. If you want a completely empty file, don't enter date,

just > logfile.

This works great on apache and other web server logs, without ever stopping the service.

Don't forget the options that make ls work better for you

Generally speaking, most users type ls or ls -al to see their directory listings. But don't forget that there are a few other cool options that make it easier to read your directory listings.

The -aCF options are my next-favorite listing combination.  The following command:

     ls -aCF

lists (a) all files in columns with special characters appended to the end of each name to show whether the file is a normal file, directory, executable file, or link.

The -m option lets you list files in a comma-separated list, which can be useful if you're going to be exporting the list to a Perl program or shell script.

To sort the listing by file size, try

     ls -al | sort -4n

To reverse the filesize listing, use

     ls -al | sort -4nr

instead.

Changing a file's owner and group at the same time

Did you know that in Solaris 2.x you can change both the user and group ownership on a file at the same time by running /usr/ucb/chown?

For example, suppose you have a file called /export/home/grahamc/myfile that is owned by user 'grahamc' and group 'users'. To change the ownerships, you could type the following as root:

    /usr/ucb/chown grahamc:users /export/home/grahamc/myfile

rather than typing:

    /usr/bin/chown grahamc /export/home/grahamc/myfile
    /usr/bin/chgrp users   /export/home/grahamc/myfile

You can also recursively change ownerships with the -R option.

For more information, try:   man -s 1b chown

Using the which command

I think a good command in addition to the 'type' is the 'which' command. You can really see where the executable come from. This is very helpful to find out if there is an alias set to the command and if so - which one.

Example:   # type ls
           ls is a tracked alias for /usr/bin/ls
           # which ls
           ls:     aliased to ls -aF



Legal Disclaimer
THE INFORMATION IN THIS PUBLICATION IS PROVIDED "AS IS". WE EXPRESSLY DISCLAIMS ALL REPRESENTATIONS AND WARRANTIES OF ANY KIND REGARDING THE CONTENTS OR USE OF THE INFORMATION INCLUDING, BUT NOT LIMITED TO, EXPRESS AND IMPLIED WARRANTIES OF ACCURACY, COMPLETENESS, MERCHANTABILITY, FITNESS FOR A PARTICULAR USE, OR NON-INFRINGEMENT. IN NO EVENT SHALL WE BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS, LOST BUSINESS OR LOST DATA, RESULTING FROM THE USE OR RELIANCE UPON THE INFORMATION, EVEN IF WE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF IMPLIED WARRANTIES, SO THE ABOVE EXCLUSION MAY NOT APPLY TO YOU.