Introduction

Archive-name: unix-faq/faq/part3
Version: $Id: part3,v 2.9 1996/06/11 13:07:56 tmatimar Exp $

These seven articles contain the answers to some Frequently Asked
Questions often seen in comp.unix.questions and comp.unix.shell.
Please don't ask these questions again, they've been answered plenty
of times already - and please don't flame someone just because they may
not have read this particular posting.  Thank you.

This collection of documents is Copyright (c) 1994, Ted Timar, except
Part 6, which is Copyright (c) 1994, Pierre Lewis and Ted Timar.
All rights reserved.  Permission to distribute the collection is
hereby granted providing that distribution is electronic, no money
is involved, reasonable attempts are made to use the latest version
and all credits and this copyright notice are maintained.
Other requests for distribution will be considered.  All reasonable
requests will be granted.

All information here has been contributed with good intentions, but
none of it is guaranteed either by the contributors or myself to be
accurate.  The users of this information take all responsibility for
any damage that may occur.

Many FAQs, including this one, are available on the archive site
rtfm.mit.edu in the directory pub/usenet/news.answers.
The name under which a FAQ is archived appears in the "Archive-Name:"
line at the top of the article.  This FAQ is archived as
"unix-faq/faq/part[1-7]".

These articles are divided approximately as follows:

      1.*) General questions.
      2.*) Relatively basic questions, likely to be asked by beginners.
      3.*) Intermediate questions.
      4.*) Advanced questions, likely to be asked by people who thought
           they already knew all of the answers.
      5.*) Questions pertaining to the various shells, and the differences.
      6.*) An overview of Unix variants.
      7.*) An comparison of configuration management systems (RCS, SCCS).

This article includes answers to:

      3.1)  How do I find the creation time of a file?
      3.2)  How do I use "rsh" without having the rsh hang around
              until the remote command has completed?
      3.3)  How do I truncate a file?
      3.4)  Why doesn't find's "{}" symbol do what I want?
      3.5)  How do I set the permissions on a symbolic link?
      3.6)  How do I "undelete" a file?
      3.7)  How can a process detect if it's running in the background?
      3.8)  Why doesn't redirecting a loop work as intended?  (Bourne shell)
      3.9)  How do I run 'passwd', 'ftp', 'telnet', 'tip' and other interactive
              programs from a shell script or in the background?
      3.10) How do I find the process ID of a program with a particular
            name from inside a shell script or C program?
      3.11) How do I check the exit status of a remote command
            executed via "rsh" ?
      3.12) Is it possible to pass shell variable settings into an awk program?
      3.13) How do I get rid of zombie processes that persevere?
      3.14) How do I get lines from a pipe as they are written instead of
            only in larger blocks?
      3.15) How do I get the date into a filename?
      3.16) Why do some scripts start with #! ... ?

If you're looking for the answer to, say, question 3.5, and want to skip
everything else, you can search ahead for the regular expression "^3.5)".

While these are all legitimate questions, they seem to crop up in
comp.unix.questions or comp.unix.shell on an annual basis, usually
followed by plenty of replies (only some of which are correct) and then
a period of griping about how the same questions keep coming up.  You
may also like to read the monthly article "Answers to Frequently Asked
Questions" in the newsgroup "news.announce.newusers", which will tell
you what "UNIX" stands for.

With the variety of Unix systems in the world, it's hard to guarantee
that these answers will work everywhere.  Read your local manual pages
before trying anything suggested here.  If you have suggestions or
corrections for any of these answers, please send them to to
[email protected].

How do I find the creation time of a file?

Date: Thu Mar 18 17:16:55 EST 1993

3.1)  How do I find the creation time of a file?

      You can't - it isn't stored anywhere.  Files have a last-modified
      time (shown by "ls -l"), a last-accessed time (shown by "ls -lu")
      and an inode change time (shown by "ls -lc"). The latter is often
      referred to as the "creation time" - even in some man pages -
      but that's wrong; it's also set by such operations as mv, ln,
      chmod, chown and chgrp.

      The man page for "stat(2)" discusses this.

How do I use "rsh" without having the rsh hang around ... ?

Date: Thu Mar 18 17:16:55 EST 1993

3.2)  How do I use "rsh" without having the rsh hang around until the
      remote command has completed?

      (See note in question 2.7 about what "rsh" we're talking about.)

      The obvious answers fail:
            rsh machine command &
      or      rsh machine 'command &'

      For instance, try doing   rsh machine 'sleep 60 &' and you'll see
      that the 'rsh' won't exit right away.  It will wait 60 seconds
      until the remote 'sleep' command finishes, even though that
      command was started in the background on the remote machine.  So
      how do you get the 'rsh' to exit immediately after the 'sleep' is
      started?

      The solution - if you use csh on the remote machine:

            rsh machine -n 'command >&/dev/null </dev/null &'

      If you use sh on the remote machine:

            rsh machine -n 'command >/dev/null 2>&1 </dev/null &'

      Why?  "-n" attaches rsh's stdin to /dev/null so you could run the
      complete rsh command in the background on the LOCAL machine.
      Thus "-n" is equivalent to another specific "< /dev/null".
      Furthermore, the input/output redirections on the REMOTE machine
      (inside the single quotes) ensure that rsh thinks the session can
      be terminated (there's no data flow any more.)

      Note: The file that you redirect to/from on the remote machine
      doesn't have to be /dev/null; any ordinary file will do.

      In many cases, various parts of these complicated commands
      aren't necessary.

How do I truncate a file?

Date: Mon, 27 Mar 1995 18:09:10 -0500

3.3)  How do I truncate a file?

      The BSD function ftruncate() sets the length of a file.
      (But not all versions behave identically.)  Other Unix variants
      all seem to support some version of truncation as well.

      For systems which support the ftruncate function, there are
      three known behaviours: 

      BSD 4.2 - Ultrix, SGI, LynxOS
              - truncation doesn't grow file
              - truncation doesn't move file pointer


      BSD 4.3 - SunOS, Solaris, OSF/1, HP/UX, Amiga
              - truncation can grow file
              - truncation doesn't move file pointer

      Cray    - UniCOS 7, UniCOS 8
              - truncation doesn't grow file
              - truncation changes file pointer

      Other systems come in four varieties:

      F_CHSIZE - Only SCO
               - some systems define F_CHSIZE but don't support it
               - behaves like BSD 4.3

      F_FREESP - Only Interative Unix
               - some systems (eg. Interactive Unix) define F_FREESP but
                   don't support it
               - behaves like BSD 4.3

      chsize() - QNX and SCO
               - some systems (eg. Interactive Unix) have chsize() but
                   don't support it
               - behaves like BSD 4.3

      nothing  - no known systems
               - there will be systems that don't support truncate at all


      Moderator's Note: I grabbed the functions below a few years back.
                        I can no longer identify the original author.
                        S. Spencer Sun <[email protected]> has also
                        contributed a version for F_FREESP.

      functions for each non-native ftruncate follow

      /* ftruncate emulations that work on some System V's.
         This file is in the public domain. */

      #include 
      #include 

      #ifdef F_CHSIZE
      int
      ftruncate (fd, length)
           int fd;
           off_t length;
      {
        return fcntl (fd, F_CHSIZE, length);
      }
      #else
      #ifdef F_FREESP
      /* The following function was written by
         [email protected] (William Kucharski) */

      #include 
      #include 
      #include 

      int
      ftruncate (fd, length)
           int fd;
           off_t length;
      {
        struct flock fl;
        struct stat filebuf;

        if (fstat (fd, &filebuf) < 0)
          return -1;

        if (filebuf.st_size < length)
          {
            /* Extend file length. */
            if (lseek (fd, (length - 1), SEEK_SET) < 0)
              return -1;

            /* Write a "0" byte. */
            if (write (fd, "", 1) != 1)
              return -1;
          }
        else
          {
            /* Truncate length. */
            fl.l_whence = 0;
            fl.l_len = 0;
            fl.l_start = length;
            fl.l_type = F_WRLCK;      /* Write lock on file space. */

            /* This relies on the UNDOCUMENTED F_FREESP argument to
               fcntl, which truncates the file so that it ends at the
               position indicated by fl.l_start.
               Will minor miracles never cease? */
            if (fcntl (fd, F_FREESP, &fl) < 0)
              return -1;
          }

        return 0;
      }
      #else
      int
      ftruncate (fd, length)
           int fd;
           off_t length;
      {
        return chsize (fd, length);
      }
      #endif
      #endif

Why doesn't find's "{}" symbol do what I want?

Date: Thu Mar 18 17:16:55 EST 1993

3.4)  Why doesn't find's "{}" symbol do what I want?

      "find" has a -exec option that will execute a particular command
      on all the selected files. Find will replace any "{}" it sees
      with the name of the file currently under consideration.

      So, some day you might try to use "find" to run a command on
      every file, one directory at a time.  You might try this:

        find /path -type d -exec command {}/\* \;

      hoping that find will execute, in turn

        command directory1/*
        command directory2/*
        ...

      Unfortunately, find only expands the "{}" token when it appears
      by itself.  Find will leave anything else like "{}/*" alone, so
      instead of doing what you want, it will do

        command {}/*
        command {}/*
        ...

      once for each directory.  This might be a bug, it might be a
      feature, but we're stuck with the current behaviour.

      So how do you get around this?  One way would be to write a
      trivial little shell script, let's say "./doit", that consists of

        command "$1"/*

      You could then use

        find /path -type d -exec ./doit {} \;

      Or if you want to avoid the "./doit" shell script, you can use

        find /path -type d -exec sh -c 'command $0/*' {} \;

      (This works because within the 'command' of "sh -c 'command' A B C ...",
       $0 expands to A, $1 to B, and so on.)

      or you can use the construct-a-command-with-sed trick

        find /path -type d -print | sed 's:.*:command &/*:' | sh

      If all you're trying to do is cut down on the number of times
      that "command" is executed, you should see if your system has the
      "xargs" command.  Xargs reads arguments one line at a time from
      the standard input and assembles as many of them as will fit into
      one command line.  You could use

        find /path -print | xargs command

      which would result in one or more executions of

        command file1 file2 file3 file4 dir1/file1 dir1/file2

      Unfortunately this is not a perfectly robust or secure solution.
      Xargs expects its input lines to be terminated with newlines, so
      it will be confused by files with odd characters such as newlines
      in their names.

How do I set the permissions on a symbolic link?

Date: Thu Mar 18 17:16:55 EST 1993

3.5)  How do I set the permissions on a symbolic link?

      Permissions on a symbolic link don't really mean anything.  The
      only permissions that count are the permissions on the file that
      the link points to.

How do I "undelete" a file?

Date: Thu Mar 18 17:16:55 EST 1993

3.6)  How do I "undelete" a file?

      Someday, you are going to accidentally type something like
      "rm * .foo", and find you just deleted "*" instead of "*.foo".
      Consider it a rite of passage.

      Of course, any decent systems administrator should be doing
      regular backups.  Check with your sysadmin to see if a recent
      backup copy of your file is available.  But if it isn't, read
      on.

      For all intents and purposes, when you delete a file with "rm" it
      is gone.  Once you "rm" a file, the system totally forgets which
      blocks scattered around the disk were part of your file.  Even
      worse, the blocks from the file you just deleted are going to be
      the first ones taken and scribbled upon when the system needs
      more disk space.  However, never say never.  It is theoretically
      possible *if* you shut down the system immediately after the "rm"
      to recover portions of the data.  However, you had better have a
      very wizardly type person at hand with hours or days to spare to
      get it all back.

      Your first reaction when you "rm" a file by mistake is why not
      make a shell alias or procedure which changes "rm" to move files
      into a trash bin rather than delete them?  That way you can
      recover them if you make a mistake, and periodically clean out
      your trash bin.  Two points:  first, this is generally accepted
      as a *bad* idea.  You will become dependent upon this behaviour
      of "rm", and you will find yourself someday on a normal system
      where "rm" is really "rm", and you will get yourself in trouble.
      Second, you will eventually find that the hassle of dealing with
      the disk space and time involved in maintaining the trash bin, it
      might be easier just to be a bit more careful with "rm".  For
      starters, you should look up the "-i" option to "rm" in your
      manual.

      If you are still undaunted, then here is a possible simple
      answer.  You can create yourself a "can" command which moves
      files into a trashcan directory. In csh(1) you can place the
      following commands in the ".login" file in your home directory:

        alias can       'mv \!* ~/.trashcan'       # junk file(s) to trashcan
        alias mtcan     'rm -f ~/.trashcan/*'      # irretrievably empty trash
        if ( ! -d ~/.trashcan ) mkdir ~/.trashcan  # ensure trashcan exists

      You might also want to put a:

        rm -f ~/.trashcan/*

      in the ".logout" file in your home directory to automatically
      empty the trash when you log out.  (sh and ksh versions are left
      as an exercise for the reader.)

      MIT's Project Athena has produced a comprehensive
      delete/undelete/expunge/purge package, which can serve as a
      complete replacement for rm which allows file recovery.  This
      package was posted to comp.sources.misc (volume 17, issue
      023-026)

How can a process detect if it's running in the background?

Date: Thu Mar 18 17:16:55 EST 1993

3.7)  How can a process detect if it's running in the background?

      First of all: do you want to know if you're running in the
      background, or if you're running interactively? If you're
      deciding whether or not you should print prompts and the like,
      that's probably a better criterion. Check if standard input
      is a terminal:

            sh: if [ -t 0 ]; then ... fi
            C: if(isatty(0)) { ... }

      In general, you can't tell if you're running in the background.
      The fundamental problem is that different shells and different
      versions of UNIX have different notions of what "foreground" and
      "background" mean - and on the most common type of system with a
      better-defined notion of what they mean, programs can be moved
      arbitrarily between foreground and background!

      UNIX systems without job control typically put a process into the
      background by ignoring SIGINT and SIGQUIT and redirecting the
      standard input to "/dev/null"; this is done by the shell.

      Shells that support job control, on UNIX systems that support job
      control, put a process into the background by giving it a process
      group ID different from the process group to which the terminal
      belongs.  They move it back into the foreground by setting the
      terminal's process group ID to that of the process.  Shells that
      do *not* support job control, on UNIX systems that support job
      control, typically do what shells do on systems that don't
      support job control.

Why doesn't redirecting a loop work as intended? (Bourne shell)

Date: Thu Mar 18 17:16:55 EST 1993

3.8)  Why doesn't redirecting a loop work as intended?  (Bourne shell)

      Take the following example:

        foo=bar

        while read line
        do
                # do something with $line
                foo=bletch
        done < /etc/passwd

        echo "foo is now: $foo"

      Despite the assignment ``foo=bletch'' this will print
      ``foo is now: bar'' in many implementations of the Bourne shell.
      Why?  Because of the following, often undocumented, feature of
      historic Bourne shells: redirecting a control structure (such as
      a loop, or an ``if'' statement) causes a subshell to be created,
      in which the structure is executed; variables set in that
      subshell (like the ``foo=bletch'' assignment) don't affect the
      current shell, of course.

      The POSIX 1003.2 Shell and Tools Interface standardization
      committee forbids the behaviour described above, i.e. in P1003.2
      conformant Bourne shells the example will print ``foo is now:
      bletch''.

      In historic (and P1003.2 conformant) implementations you can use
      the following `trick' to get around the redirection problem:

        foo=bar

        # make file descriptor 9 a duplicate of file descriptor 0 (stdin);
        # then connect stdin to /etc/passwd; the original stdin is now
        # `remembered' in file descriptor 9; see dup(2) and sh(1)
        exec 9<&0 < /etc/passwd

        while read line
        do
                # do something with $line
                foo=bletch
        done

        # make stdin a duplicate of file descriptor 9, i.e. reconnect
        # it to the original stdin; then close file descriptor 9
        exec 0<&9 9<&-

        echo "foo is now: $foo"

      This should always print ``foo is now: bletch''.
      Right, take the next example:

        foo=bar

        echo bletch | read foo

        echo "foo is now: $foo"

      This will print ``foo is now: bar'' in many implementations,
      ``foo is now: bletch'' in some others.  Why?  Generally each part
      of a pipeline is run in a different subshell; in some
      implementations though, the last command in the pipeline is made
      an exception: if it is a builtin command like ``read'', the
      current shell will execute it, else another subshell is created.

      POSIX 1003.2 allows both behaviours so portable scripts cannot
      depend on any of them.

What's wrong with having '.' in your $PATH ?

Date: Thu Mar 18 17:16:55 EST 1993

2.13) What's wrong with having '.' in your $PATH ?

      A bit of background: the PATH environment variable is a list of
      directories separated by colons.  When you type a command name
      without giving an explicit path (e.g. you type "ls", rather than
      "/bin/ls") your shell searches each directory in the PATH list in
      order, looking for an executable file by that name, and the shell
      will run the first matching program it finds.

      One of the directories in the PATH list can be the current
      directory "." .  It is also permissible to use an empty directory
      name in the PATH list to indicate the current directory.  Both of
      these are equivalent

      for csh users:

        setenv PATH :/usr/ucb:/bin:/usr/bin
        setenv PATH :/usr/ucb:/bin:/usr/bin

      for sh or ksh users

        PATH=:/usr/ucb:/bin:/usr/bin export PATH
        PATH=:/usr/ucb:/bin:/usr/bin export PATH

      Having "." somewhere in the PATH is convenient - you can type
      "a.out" instead of "./a.out" to run programs in the current
      directory.  But there's a catch.

      Consider what happens in the case  where "." is the first entry
      in the PATH.  Suppose your current directory is a publically-
      writable one, such as "/tmp".  If there just happens to be a
      program named "/tmp/ls" left there by some other user, and you
      type "ls" (intending, of course, to run the normal "/bin/ls"
      program), your shell will instead run "./ls", the other user's
      program.  Needless to say, the results of running an unknown
      program like this might surprise you.

      It's slightly better to have "." at the end of the PATH:

        setenv PATH /usr/ucb:/bin:/usr/bin:.

      Now if you're in /tmp and you type "ls", the shell will
      search /usr/ucb, /bin and /usr/bin for a program named
      "ls" before it gets around to looking in ".", and there
      is less risk of inadvertently running some other user's
      "ls" program.  This isn't 100% secure though - if you're
      a clumsy typist and some day type "sl -l" instead of "ls -l",
      you run the risk of running "./sl", if there is one.
      Some "clever" programmer could anticipate common typing
      mistakes and leave programs by those names scattered
      throughout public directories.  Beware.

      Many seasoned Unix users get by just fine without having
      "." in the PATH at all:

        setenv PATH /usr/ucb:/bin:/usr/bin

      If you do this, you'll need to type "./program" instead
      of "program" to run programs in the current directory, but
      the increase in security is probably worth it.

How do I ring the terminal bell during a shell script?

>From: [email protected] (Uwe Waldmann)
Date: Fri, 30 Apr 93 16:33:00 +0200

2.14) How do I ring the terminal bell during a shell script?

      The answer depends on your Unix version (or rather on the kind of
      "echo" program that is available on your machine).

      A BSD-like "echo" uses the "-n" option for suppressing the final
      newline and does not understand the octal \nnn notation.  Thus
      the command is

        echo -n '^G'

      where ^G means a _literal_ BEL-character (you can produce this in
      emacs using "Ctrl-Q Ctrl-G" and in vi using "Ctrl-V Ctrl-G").

      A SysV-like "echo" understands the \nnn notation and uses \c to
      suppress the final newline, so the answer is:

        echo '\007\c'

Why can't I use "talk" to talk with my friend on machine X?

>From: [email protected] (Ted Timar)
Date: Thu Mar 18 17:16:55 EST 1993

2.15) Why can't I use "talk" to talk with my friend on machine X?

      Unix has three common "talk" programs, none of which can talk with
      any of the others.  The "old" talk accounts for the first two types.
      This version (often called otalk) did not take "endian" order into
      account when talking to other machines.  As a consequence, the Vax
      version of otalk cannot talk with the Sun version of otalk.
      These versions of talk use port 517.

      Around 1987, most vendors (except Sun, who took 6 years longer than
      any of their competitors) standardized on a new talk (often called
      ntalk) which knows about network byte order.  This talk works between
      all machines that have it.  This version of talk uses port 518.

      There are now a few talk programs that speak both ntalk and one
      version of otalk.  The most common of these is called "ytalk".

Why does calendar produce the wrong output?

>From: [email protected] (Ted Timar)
Date: Thu Sep  8 09:45:46 EDT 1994

2.16) Why does calendar produce the wrong output?

      Frequently, people find that the output for the Unix calendar
      program, 'cal' produces output that they do not expect.

      The calendar for September 1752 is very odd:

               September 1752
             S  M Tu  W Th  F  S
                   1  2 14 15 16
            17 18 19 20 21 22 23
            24 25 26 27 28 29 30

      This is the month in which the US (the entire British Empire actually)
      switched from the Julian to the Gregorian calendar.

      The other common problem people have with the calendar program is
      that they pass it arguments like 'cal 9 94'.  This gives the calendar
      for September of AD 94, NOT 1994.

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

End of unix/faq Digest part 2 of 7
**********************************