"Linux Gazette...making Linux just a little more fun!"

Tom's 2 cent Tips

By Tom Bryant

The notes listed below document the author's "show stoppers" which were made as he learned UNIX, after working on DOS, VMS, and other systems. There is a small chance that you will make exactly the same error, and this document will help you to solve it. What is far more likely is that you will skim through it, and hopefully learn one very important thing:

Almost all errors are trivial, and result from your understanding a Unix function *almost* completely. Gross errors in understanding are very rare, although when you hit a show stopper, you usually get the initial impression that you are hopelessly in left field and will never understand this cryptic operating system. Not True!! *Do not give up*!

I suppose that you have already guessed it, but the FAQs, man pages and emacs info documentation comes with Linux, and *really* help out. You have to really dig: there are bookshelves of documentation in the above sources. You will probably learn more about certain topics than you wanted to, but after a while all this knowledge begins to look good on your resume. While I have tried to avoid overlap in this document, some does exist. It should give you pause that there is so little overlap! A modern operating system is a complex, many faceted beast. Take it a facet at a time and you'll get around it!

UNIX is largely free of the system crashes that plague DOS and the MAC. This is because a user account is almost incapable of accessing the system resources required to crash the system. When you're running in DOS or MAC mode, you (or more often, programs that you run) can crash the system. I once complexly wiped my hard disk while running Borland's C++ compiler. I have never had this happen with Linux. I've never, in five years, crashed the system so badly that I had to reset. Granted, some functions (X windows and the modem) required a reset, but I could always get to a root virtual terminal to shut down gracefully. I've merrily crashed VMS, other Unicies, and of course, anything Microsoft has ever produced, from DOS 3.3 to NT. Linux is robust.

I suppose no introduction is complete without a testimonial. Here goes: I mentioned above that I had worked on a number of systems before embracing Unix. Unix (especially Linux) is the best system I have *ever* worked on. Give it a chance: it takes a while to collect a "critical mass" of Unix commands to make the system really fly, but once you do, you'll become one of those insufferable Unix propeller heads who claim (truthfully) that nothing else even comes close!

Please Note: The author has made every effort to insure the correctness of the information which follows. However, there are NO warranties, expressed or implied, for this information. In other words, if something goes wrong, it's your problem.


Alphabetical list of utilities with one or more tips with them:


You can only run this from root, but it is an essential command. Almost all of your time spent in Linux should be spent in a user account of your own creation, as this account is prevented from executing disastrous commands such as rm -rf /* (This cleans your disk in a disastrously complete fashion.)

MORAL: Always run in a user account, not root unless you are doing some system administration which will only work in root.


Ar creates a library file, which, after you get a utility program you'll be using in other programs, allows you to easily access the object files of that program. The files are created by the following command:

        ar -r libArchiveName.a objectfile.o
ArchiveName is, by convention, named libArchiveName.a. However, you refer to in your make file only by ArchiveName. e.g
       LIBFILES = ... -lArchiveName ...

Backing up.

Having your system crash and not loosing valuable data is what separates the computer pros from the also rans. Here's how I do it.

All of the software on my system is safely contained on the CD that I used to load it in. There's no reason to back up this stuff. CDs are more stable than any tape or disk backup I know of.

I only back up files that I have fiddled with. My programs, configuration files, documents, etc. I have a script file, sall (save all) which goes to each directory that I have stuff in, tars it, gzips it and then saves it to floppy.

This stuff all fits on one 1.44 Mb disk. It represents about 730 pages of single spaced typewritten output. That's a lot of typing. Most books aren't that big. If you have more stuff than this, I'd suspect that 80% of it is stuff you haven't touched in a year. You can back it up to a disk, put the disk in a safe place and pretty much forget it. Of course, if you gzip the result, you can usually more than triple the amount of stuff you back up.

I have 15+ disks which I keep my backups on, and back up at the end of any day that I have done a substantial amount of input. This gives me access to a month or more of past versions.

You'll find your own way in this. The important thing is to *do* it. Then you can feel almost smug when you hard disk finally dies.

bash (and ksh and sh)

To set an environmental variable with bash use the following syntax:

For example, to tell bash that your are a vt100 type terminal, enter:
         TERM=vt100; export TERM
NOTE: There are no spaces around the equal sign. This is true for all bash commands, not just setting environmental variables. Remember this and be saved frustration down the road.


  1. As Unix is currently configured, there is no easy way to get a single keystroke without using curses or changing the terminal characteristics. Your users will just have to get used to hitting Enter after an input. If they can't, uses curses or the GCC C manual (actually a superb reference for C libraries well worth the $50+ bucks!)
  2. After you have output to an opened text file, it is a good idea to send a fflush(stdout) command. This will flush the buffer and keep you from overflowing it if you get over 2048 characters in it, which is easy to do.
  3. To use math.h functions in a c program:
    Link in the math libraries by typing:
            gcc source.cxx -lm
    Math libraries MUST be linked in last!!! Old UNIX linker convention!
  4. When declaring pointers, every variable must be proceeded by an asterisk!
           int *v1,
    Results in compiler complaints of bad unary operator arguments! Correct:
           int *v1,
  5. To capture the output of a compile which is causing problems, and outputting reams of digital vomit, use this construct from a sh based shell like bash:
           gcc -c flaw.cxx &> TextCapture 
           gcc -c flaw.cxx 2> TextCapture
    The errors will be captured in TextCapture.
  6. A static variable is initialized only once in a function. All other calls to that function result in the existing value of the variable being used.
  7. Shifts in most Unix C compliers are actually integer divides and multiplies by 2. Never mind what the ANSI spec says:
    This code gives problems:
           long n = 0x80000000;
           for (i = 0; i < 24; i++) printf("\n n is now %lx.", n >> i);
    n must be declared as an unsigned long for this to produce reasonable results. The fact that n starts negative fowls the compiler up. This also indicates that right and left shifts are implemented as divide and multiply by 2 with the GNU C++ compiler.
  8. This is perhaps my favorite subtle error. If I only had a dollar for every hour I've spent searching for this self imposed klutziness...
        for (i = 0; i < upper_limit; i++);
            this = cant_happen;
            the_loop = wont_execute;
    See the semi-colon at the end of the for statement line? The program pauses for an empty loop of upper_limit cycles, and then proceeds to do just the first installment of the loop and nothing else. It's quite easy to to, too, even after you've been bitten a few times by it.
  9. You can core dump a program sooner than quicker by writing too much stuff into a string, so that it doesn't end with that all important 0. e.g.:
           double age; 
           char oops[52]; 
           int len; ...  
           sprintf(oops,"The age of the universe in nanoseconds is %f.\n",age); 
           len = strlen(oops); 
    The fragment will die at the strlen call, as the string is longer than 50 characters, even if it is 10 character in the sprintf statement. The age of the universe in nanoseconds is about 19 characters long.
  10. This code will pass a file pointer (tfile) to a function:
        #include <stdio.h>  /* fclose, fopen, printf.*/
        #include <stdlib.h> /* exit.                 */
        void main(int argc, char* argv[]) 
            /* Function Prototypes:*/
            int openfile(FILE** tfile);
            /* Variables:*/
            FILE *tfile;       /* Test file.*/
            /* Begin:*/
            printf("Starting program.  tfile = %p.\n", tfile);
            printf("Back in main.  tfile = %p\n", tfile);
        /* Open the file and return the pointer.*/
        int openfile(FILE **tfile)
            *tfile = fopen("junk","wb");
            printf("Tfile just opened.  tfile = %p, *tfile = %p.\n", 
                    tfile, *tfile);
            return 1;
  11. This code will allow you to reference an array of characters as an array of longs without using a union:
        int bozo(void)
            void use_long(long *array); // Function prototype.
            char *array;                // The character array.
            use_long((long*) array);
        void use_long(long *array);
            // Reference the array as longs here.
  12. Profiling:
    Compile with the flag -pg (for gprof compatible output). Link with the -pg flag AT THE END OF THE LINK LIST. e.g:
        gcc  -o run_time_image my_main.o my_other_stuff.o -pg
    Even then, don't expect miracles. This does not work on an mpeg2 package I found on the net.
  13. Reading in a globbed (wildcarded) file list:
    If you want you program to work on all files in a directory, you can enter "myprog *". Argc will count the number of files out there, and argv will be the char **list of their names.
  14. The code
            string1[i++] = string2[i]; 
    Will take the nth component of string2 and put it the into the nth component of string1, then increment i. This is how GCC and the SGI C compiler do it.

    The Motorola C compiler takes the (n + 1)th component of string2 and puts it into the nth component of string1. I gets incremented *before* the assignment. Safer is to just use this code:

            string1[i] = string2[i]; 
  15. The code
        if (index++ == NR_PTS) index = 0; // Fails to implement a circular buffer.
    Does not implement the desired buffer. Let's say that NR_PTS is 100, and index is 99. The comparison is made when index is 99. Index is not reset, by then it is set to 100 by the ++. Next time, of course, the index will be reset to 0, but by then, it's overwritten whatever was in buffer[100], which is the 101st entry in buffer, a 100 int (or whatever) array.

    use this instead:

        if (++index == NR_PTS) index = 0; // Implements the circular buffer.
  16. Occasionally, if you allocate a *huge* array (I needed one 10.7 MB to make this one happen) the program will bomb as it tries to pass the array to another function. For example:
            char moby[20000000];  // A really big text file.
    There simply wasn't enough room on the C/C++ stack to hold the array, and the program bombed as soon as it tried to access it. The compiler, of course, didn't complain.

    The fix is to malloc (or new, in C++) the array:

            char *moby;  // A really big text file. 
        moby = (char*) malloc(20000000);
        if (moby == NULL)
            printf("Hey! I'm not big enough to hold moby!\n");
  17. When it works on one machine and not on others, or in the debugger and not out of it:
    You might well be not initializing your variables properly. Sometimes one machine will just happen to initialize a variable to a non lethal value, and the others won't. See inexplicable errors item 3 as well.
  18. Inexplicable errors:
    Occasionally, you will face very odd errors. A function works perfectly *most* of the time. The rest of the time, it has very strange errors. Variables seem to change value in a capricious way, and your frustration index soars off of the charts. If you're a beginner, you decide that programming is beyond you, and give up. Don't despair, quite yet. There are a few tricks, not written in any book that I have seen, that I have developed over the years to help you over these rough spots. In general, try these in the order given.
    1. Rewrite your code until it is remorselessly neat.
    2. Step through it line by line with a good debugger (I recommend GNU's GDB for C code. It's a bit flaky (1998) for C++).
    3. If these steps did not fix the problem, try recompiling everything with a command like:
                          rm *.o     // Important!
                          make       // Or whatever compilation command you use.
    4. If you have inconsistent program results, so that the program works most of the time, and inexplicably fails occasionally, carefully check to see that all of your variables have been initialized properly.
    5. Sometimes the memory can be corrupted by new routines not fitting into word boundaries on your machine. Granted, this is *not* supposed to happen, but it does anyway, even in the best compilers and linkers. Try declaring arrays to be 1 index larger than they should be and see if that fixes it. (This is getting to be a rare C error (1998)).
      1. There are times when the program does not misbehave in the debugger, and if you put a few diagnostic printfs in it to further check it out, it still does not die. These temporary measures may need to be made permanent as the extra variables you created to let you see what is going on have pushed the memory usage boundaries of the function to a more robust spot (i.e. to a word boundary).
    6. Once you think you've fixed the problem, *TEST EVERYTHING* before you release anything.
    7. Keep at it. With practice, you can dumb yourself down to the intelligence level of a compiler. Good Luck!


The command chmod XXX .* can have unforeseen after effects. It affects the directory you're in as well as the files in that directory! You might find that only root can access the files, and then only after chmoding them back to where they were supposed to be.

cpio (not often used, most Unix users use tar instead)

To use cpio, for backups, specify:

    find (path name) [-name] | cpio -oc > (destination file and directory.)
To extract a cpio file:
    cpio -i [-F (full file specification)] [-r rename files]


To change the erase key from ctrl h to <-, one has to add the line:

        stty erase \x7F
To the .cshrc file. Unfortunately, the C shell interpreter does not understand the sequence \x7F, and I needed to write a short C program to insert the byte with value 127 (7F hex) at the end of the file.

To set the terminal from 'console' or 'Linux' to vt100 (needed by elm (a mail handling utility)) on an SGI, put this line in your .cshrc file in you SGI home directory:

        eval `tset -s -Q vt100`
If your etc/ttytype file is set up to recognize vt100s, this should work.

Note this does not work on all machines. These methods were useless on a motorola, for example


    find (path name) -name (file name) -print
wildcards at the beginning of a find names must be preceded by a \. e.g.:
        find / -name "\*bozo*" -print 
finds all files with the letters bozo in them. Some systems require the quotes as well.


Make sure the line setting is binary. Zipped files will not transfer correctly if this is not set.

gdb (and dbx)

To examine a core file:

        gdb <executable file name> <core file name> 
The core file name is usually "core".

To examine an array:

	p *arrayName@number_of_bytes_you_want_to_see


The new way of handling compiling and linking programs is with a utility called imake. It makes programs easier to move around from machine to machine. To use it:

  1. Read the imake, make and xmkmf man pages. Also any README files that came with the program you're trying to build. If you're going to be getting into some serious program development, the emacs info page on make is a must read.
  2. Back up everything! Source, Makefile, imakefile, etc.
  3. imake
  4. If imake doesn't work, try xmkmf
  5. If it *still* doesn't work, go back to step 0.

kermit (quick check of file integrity)

This is true for any other data transfer protocol as well, such as ftp.

Check the size of the files that you have transfered to the target system. If they are not the same size, something went wrong.


To disable the annoying wait after an unsuccessful login, edit the file /etc/login.defs. Change the parameter FAIL_DELAY from whatever it's set to to 0. You'll have to be root to do this.

link (ln)
to create a symbolic link:
        ln -s <existing file or directory> <Linked file or directory$gt;
	ln -s OldFile NewLink 
This creates a symbolic link name NewLink that points to OldFile. When you access NewLink, Unix actually accesses OldFile.

Links are mainly used to save disk space, allowing yu to have the same file in several different places without actually having maintain multiple copies of the file.


Less can't display ANSI color escape sequences like most of the other Unix text output utilities can. It instead tries to print them with ugly results. On other unicies, the work around is to use the pg function, but it's not avalible on Linux (yet).


To regain text that has scrolled off of the screen, try shift-PageUp and/or shift-PageDown. This will work on a given console until you shift to another one. When you shift back, however, you will find that the scrolled text has been lost.

To change from virtual console to virtual console, use left alt (only the left alt key works on my machine) FX where FX is one of the function keys F1 thru F6. F7 will be your X window, and isn't set up for you to log into it, but see the note below.

When you are in X, you can move back to the text consoles by simultaneously typing the control and alt keys, and then the function key of the virtual console you want to be in.

To add more virtual consoles edit your /etc/ inittab file and add a line to the getty configuration section. I added this line to inittab and it allowed me to add another virtual console:

c7:456:respawn:/sbin/agetty 38400 tty7
This let me log into F7, but not as root. To log in as root, I added this line to the /etc/securetty file:
I did not add further consoles, as 7 is the canonical number for the maximum number of things you want to juggle at one time, and each virtual console takes up precious RAM even if it is dormant.

When your screen gives weird output for lower case letters, try this:

      echo "^V^[c" 
(that's E C H O space control-V escape C return) to fix it.


To send a message with a subject:

    mail -s "This is the subject" recipient@computer.full.ip.address < message
To forward your mail:

Create a file in your root directory called .forward. This file should contain the address of the machine that you want to send the mail to:

e.g. My .forward file reads:



The make utility *requires* that commands (as listed under a target:dependency line) begin with a tab (ASCII 09).

If your emacs tab stops are set to under 8, emacs will insert spaces (ASCII 32), and not a tab. This will stop make dead in its tracks. You'll have to reset your tabs to edit a makefile.

If you break up the lines in your make file (a good idea: readability is king!) don't put anything after your backslash (line continuation symbol) or make will throw up on it:

       LIBS = Lmylib Lyourlib ... \
                     No spaces or tabs or anything after here!!!


When you install linux, your serial ports will not be configured. You have to enable the call to /etc/rc.d/rc.serial in /etc/rc/rc.S:

    # Run serial port setup script:
    # (CAREFUL! This can make some systems hang if the rc.serial script isn't
    # set up correctly.  If this happens, you may have to edit the file from a
    # boot disk)
    # You need to enable this line (remove the # comment symbol):

    . /etc/rc.d/rc.serial 

    #  for your modem to work.


Installation (for Author's PPP link -- a *very* brief reminder list):

    Load the networking module in the slakware file.
    Create or copy the /etc/hosts file.
    Create or copy the /etc/resolve.conf.
    Edit /etc/rc.d/rc.serial.
Run time problems:

Try the ifconfig and netstat commands to find out what your current network configuration is.

Use the ping command to check you connections.

Make sure that your linux kernel has drivers both for your network card and ethernet. Networking will not work without them.


Many text files are quasi-readable, and filled with control characters. If the file turns out to be an nroff man page, you can read it with the command:

        groff -Tascii -man file.name | less 
Often a variation of this command is necessary. See the man pages for groff and grog. Grog tries to look at the file for you and suggest a command. This is one that repays a lot of fiddling. Back up the original file, and groff away. Usually you'll get it. Remember also that postscript files (usually denoted by a .ps suffix) are read with the ghostscript command from X.


Here's a wierd one. The qsort function has a hard time calling it's comparison function from a C file compiled with gcc. It works fine if gcc thinks it's compiling a C++ file. Here's a pixel value sort I did, heavily edited:

        int pixCmp(pixel*, pixel*); // Return -1, 0, or 1. For the qsort call.
        qsort(data, BigNumber, sizeof(pixel), pixCmp);
        int pixCmp(pixel* a,
                   pixel* b)
            if (a->clr > b->clr) return 1;
            else if (a->clr < b->clr) return -1;
            else return 0;
If the file is named pixels.c, it produces the following gcc error:
            pixels.c: In function `readData':
            pixels.c:164: warning: passing arg 4 of `qsort' from incompatible 
                                   pointer type
If the file is named pixels.C, it produces no errors:


Installing PPP to work with Linux can be done, but it is not trivial.

I'll describe the steps that worked for me, so that you might get a variation on them to work for you.

Don't expect it to work perfectly the first time. You'll have to futz with it, unless you are very lucky.

PPP must first be installed in your kernel. To check if it is there:

        dmesg | grep -i ppp
You should get something that looks like this:
        PPP: version 0.2.7 (4 channels) NEW_TTY_DRIVERS OPTIMIZE_FLAGS
        PPP line discipline registered.
If you don't, you'll have to recompile your kernel, or get a copy of a kernel that has ppp on it from the net. Instructions for doing this are found in the file /usr/doc/ppp/README.linux.gz. This is where it is in my Slackware release, yours will probably be similar. You need to read this file now. Before you go any further. Otherwise, what follows will read like gibberish.

Read the Readme? Good. Here's how my pppd/chat command looks:

    /usr/sbin/pppd connect '/usr/sbin/chat "" ATDT7035551212 CONNECT "" ogin:\
    tbryant word: secret_password' /dev/modem 38400 -detach crtscts modem    \
    defaultroute noipdefault
Fill in your appropriate telephone number, user ID and password.

Run the script from your root directory,unless you have given pppd suid privileges (recommended).

When I am running the script, I do so from an X windows term, so I can start netscape (or whatever X application I want) easily.

Once I've established the connection, then I can run netscape, ftp, or telent to other internet connected machines.

My ISP (Internet Service Provider) assigns me a different IP address each time I log on. This IP address can be found with ifconfig, or from the /var/log/messages file.

The last few lines have what you need:

        Aug 28 20:01:23 3C273 pppd[168]: local IP address
To log off, the PPP-HOWTO.gz document has the following logoff script:
    # If the ppp0 pid file is present then the program is running.  Stop it.
    if [ -r /var/run/$DEVICE.pid ]; then
            kill -INT `cat /var/run/$DEVICE.pid`
    # If the kill did not work then there is no process running for this
    # pid.  It may also mean that the lock file will be left.  You may wish
    # to delete the lock file at the same time.
          if [ ! "$?" = "0" ]; then
                  rm -f /var/run/$DEVICE.pid
                  echo "ERROR: Removed stale pid file"
                  exit 1
    # Success.  Let pppd clean up its own junk.
          echo "PPP link to $DEVICE terminated."
          exit 0
    # The PPP process is not running for ppp0
    echo "ERROR: PPP link is not active on $DEVICE"
    exit 1
Additional hints not in the README.linux file:

All exchanges between you and you host computer will be logged in the /var/log/messages file. Deducing what's going wrong is much easier if you just look at the end of this file:

	    tail /var/log/messages 
Keep trying, don't be afraid to futz around. If you're well backed up (you *ARE*, aren't you?) you won't hurt any of you hardware, or permanently damage any software (even this is very unlikely). Good Luck!


When a file absolutely refuses to go away, try surrounding its name with quotes. This might kill it. I needed to remove a file called #filename#. Here's how I fared.

      rm #filename#    Refused to work.
      rm "#filename#"  Worked.
      rm '#filename#'  Worked.
      rm \#filename#   Worked.
The top command worked on the older versions of Linux and SGI's IRIX. This is probably a Posix compatibility problem that caused the more recent versions of Linux to stop working.


The setup script will not run unless you are in /usr/lib/setup, and running as root. Be careful. Back up everything before you start playing around with this. Don't be afraid to play, however, as you can always improve on the defaults Linux comes with.


To set up a swap file, (needed for installation)

  1. Start you new Linux box with the boot and root disks.
  2. make a partition (The rule of thumb is 1 - 2 times the size of the RAM on your machine.) using fdisk. Be sure and set the data type to Linux swap.
  3. Format the partition: mkswap -c </dev/partitionName>
  4. Enable swapping in /etc/rc.d/rc.S: /sbin/swapon -a


To make a tar file:

       tar -cf tarfilename filename (or directory.  Directory is recursive)
This creates the file.
       tar -rf tarfilename filename (or directory.  Directory is recursive)
This appends to an existing file.

To extract a tar file:
Get into the directory where you want to have the files.

       tar -xf (Complete filespec of the tar file to be extracted.)


*NEVER* *NEVER* *NEVER* name an executable test. This is a very easy, logical thing to do. When you try and run it, the shell will invoke it's test utility, find nothing there, and exit silently, leaving you very puzzled.


To set the system clock (CMOS) from Linux:

Set the system time from the CMOS clock, adjusting the time to correct for systematic error, and writ- ting it back into the CMOS clock.

This option uses the file /etc/adjtime to determine how the clock changes. It contains three numbers: The first number is the correction in seconds per day (for example, if your clock runs 5 seconds fast each day, the first number should read -5.0).

The second number tells when clock was last used, in seconds since 1/1/1970.

The third number is the remaining part of a second that was left over after the last adjustment.

The following instructions are from the source code:

  1. create a file /etc/adjtime containing as the first and only line: '0.0 0 0.0'
  2. run clock -au or clock -a, depending on whether your CMOS is in Universal or Local Time. This updates the second number.
  3. set your system time using the date command. mmddhhmm[yy][.ss]
  4. update your CMOS time using clock -wu (for UT) or clock -w.
  5. replace the first number in /etc/adjtime by your correction.
  6. put the command clock -au or clock -a in your /etc/rc.local, or let cron(8) start it regularly.


When a user is added, you have to make sure that the user owns, or at least has read, write, and execute privileges on his/her home directory. If you neglect this step, the new user will be unable to function properly, and perhaps will not be able to log on!

The /usr/bin directory must have its privileges set to 755 in order for users to be able to execute the UNIX commands contained therein.

Virtual Terminals:

To change from terminal to terminal:

                                Left Alt + fn
(n is the terminal number, from 1 - 6 and f is a function key.)

To return to virtual terminal text mode from X:

                           Left Alt + Control + fn
n is again the number of the terminal you want.

To see text that has scrolled off of the screen:

                        Shift + Page up or Page down.
Moves you up and down by half a screen each time.

To see task information:

 + Scroll Lock
To see memory information:
                             Shift + Scroll Lock


There are few short X tips. You need to read much of the documentation that is out there, and but the O'Rielly series in X and Motif if you intend to do serious developemt. It's an extrodinary, platform independent, system that solves some very difficult problems with accessing system resources in a uniform way. It's also very complex, with all sorts of redundant functions and kludges. Good Luck.

If you're going to just start getting into building user interfaces, I suggest that you bypass X entirely, and concentrate on Java. Of course, Java for Unix platforms is based on X, but you shouldn't have to worry about that.

Copyright © 1998, Tom Bryant
Published in Issue 29 of Linux Gazette, June 1998