Chinaunix

标题: comp.unix.shell FAQ(转载http://home.comcast.net/~j.p.h/) [打印本页]

作者: r2007    时间: 2005-05-25 21:39
标题: comp.unix.shell FAQ(转载http://home.comcast.net/~j.p.h/)
From: j.p.h@comcast.net (Joe Halpin)
Newsgroups: comp.unix.shell
Subject: comp.unix.shell FAQ - Answers to Frequently Asked Questions
Summary: This posting answers questions which come up with some frequency on comp.unix.shell. It should be read by anyone with a question about shell programming before posting questions to the newsgroup.
Followup-To: comp.unix.shell

Archive-name: unix-faq/shell/sh
Posting-Frequency: monthly
Version: $Id: cus-faq.html,v 1.11 2005/09/01 17:39:36 jhalpin Exp $
Maintainer: Joe Halpin

This FAQ list contains the answers to some Frequently Asked Questions often seen in comp.unix.shell. It spells "unix" in lower case letters to avoid arguments about whether or not Linux, FreeBSD, etc are unix. That's not the point of this FAQ, and I'm ignoring the issue.

This document as a whole is Copyright (c) 2003 Joe Halpin. It may be copied freely. Exceptions are noted in individual answers.

Suggestions, complaints, et al, should be sent to the maintainer at j.p.h@comcast.net or posted to comp.unix.shell

There are two levels of questions about shells.

One is the use of the shell itself as an interface to the operating system. For example, "how do I run a program in the background, and go on with other things?". Or "how do I setup environmental variables when I log in?".

The other level is how to write shell scripts. This often involves having the shell execute unix utilities to perform part of the work the shell script needs to accomplish, and requires knowledge of these utilities, which isn't nominally in the scope of shell programming. However, unless the question involves something other than standard unix utilities, it should be included in this FAQ.

Standard unix utilities are defined by either POSIX or the Single Unix Specification. These are now joined and are normally abbreviated as "POSIX/SUS". This specification can be found at

http://www.opengroup.org/onlinepubs/007904975/toc.htm

The man pages found on that web page define standard behavior for any given utility (or the shell itself). However, you should also check the man page on your system for any utility or shell you need to use. There isn't always a perfect correspondence between the standard and a particular implementation (in fact, I'm not sure there's any case in which they perfectly correspond).

There is also an Austin Group FAQ, which describes the standardization effort in more detail ls at

http://www.opengroup.org/austin/faq.html

Other good web sites that provide information about shells and shell programming (including OS utilities) include:

http://www.shelldorado.com/
http://cfaj.freeshell.org/shell/
http://www.faqs.org/faqs/by-newsgroup/comp/comp.unix.shell.html

This FAQ is available at
http://home.comcast.net/~j.p.h/
http://www.newsville.com/cgi-bin ... tly_Asked_Questions

---------------------------
The predictable legal stuff

---------------------------
The answers given in this FAQ list are provided with the best intentions, but they may not be accurate for any particular
shell/os. They may be completely wrong for any shell/os. If you don't test the answers, that's a bug in your procedures.

There are no guarantees for the answers or recommendations given in this document. In fact, I don't even claim to have tested any or all of them myself. Many of the answers here have been contributed by one or more regular participants in the newsgroup, who I believe to be competent (certainly more competent than I am), but THERE ARE NO GUARANTEES.

Did I really need to make that all uppercase? Hopefully not, but there are a lot of lawyers around with too much time on their hands, so I want to make it clear that THERE ARE NO GUARANTEES about the accuracy of answers in this FAQ list. This is, hopefully, an aid to people trying to learn shell programming, but it is NOT a supported product. You have to figure out for yourself whether or not the answers here work for what you're trying to do.

Under no circumstances will the maintainer of this FAQ list, or any contributors to it, be held liable for any mistakes in this
document. If the answers work for you, well and good. If not, please tell me and I'll modify them appropriately so that this will be more useful.

If you don't agree to that, don't read any farther than this. Reading beyond this point indicates your agreement.

If you do test the answers and find a problem, please send email to the maintainer (see above), so it can be corrected, or (preferably) post a question to the newsgroup so it can be discussed and corrected if there's a problem.

A number of people have contributed to this FAQ, knowingly or unknowingly. Some of the answers were taken from previous postings in the group, and other people contributed questions and answers directly to the maintainer, which you are welcome to do as well.

Among the contributors is Heiner Steven, who also provided the momentum to get this FAQ list started. He maintains a web site about shell programming that has a lot of good stuff in it.

http://www.shelldorado.com/

======================================================================

CONTENTS:

0   COPYING

0a. Glossary
    Google
    POSIX/SUS ("the standard")
    UUOC
    dotfile
    portable
    race condition
    shebang
    shells
    top-posting


0b. Notes about using echo
1.  How can I send e-mails with attached files?
2.  How can I generate random numbers in shell scripts?
3.  How can I automatically transfer files using FTP with error checking?

4.  How can I remove whitespace characters within file names?
5.  How can I automate a telnet session?
6.  How do I do date arithmetic?

7.  Why did someone tell me to RTFM?
8.  How do I create a lock file?
9. How can I convert DOS text files to unix, and vice versa?
10. How can a shell prompt be set up to change the title of xterm?

11. How do I get the exit code of cmd1 in cmd1|cmd2
12. Why do I get "script.sh: not found"
13. Why doesn't echo do what I want?

14. How do I loop through files with spaces in their name?
15. how do I change my login shell?
16. When should I use a shell instead of perl/python/ruby/tcl...
17. Why shouldn't I use csh?

18. How do I reverse a file?
19. How do I remove last n lines?
20. how do I get file size, or file modification time?

[21. How do I get a process id given a process name? Or, how do I find out if a process is still running, given a process ID?
22. How do I get a script to update my current environment?
23. how do I rename *.foo to *.bar?

24. How do I use shell variables in awk scripts
25. How do I input the user with a timeout?

[26. How do I get one character input from the user?
27. why isn't my .profile read?
28. why do I get "[5" not found in "[$1 -eq 2]"?
29. How do I exactly display the content of $var (with a \n appended).
30. How do I split a pathname into the directory and file?

31. How do I make an alias take an argument?
32. How do I deal with a file whose name begins with a weird character
33. Why do I lose the value of global variables that are set in a loop
34. How do I batch an FTP download/upload?

Appendix A: Some example scripts
Appendix B: References. These
correspond with numbers in square brackets (e.g. [1]) which may appear
in the text.

======================================================================

[ 本帖最后由 r2007 于 2005-12-3 17:41 编辑 ]
作者: r2007    时间: 2005-05-25 21:41
标题: comp.unix.shell FAQ(转载http://home.comcast.net/~j.p.h/)
ANSWERS

0  COPYING

   Some contributors may copyright their submissions and license them
   differently than this document.

   [1] Chris F.A. Johnson. Examples marked with COPYING[1] were
       contributed by Chris F.A. Johnson. He has copyrighted these
       examples, and licensed them under the GNU General Public
       License (GPL). Copying them directly into another script will
       cause that script to also come under the GPL. For details see

       http://www.fsf.org/licenses/licenses.html

0a.Glossary

   -------------------------------
   Google

      Google is one of the search engines on the Internet. It took
      over dejanews some years ago, and now is the standard reference
      when directing someone to a past thread one some topic. This is
      a very good place to start when researching a question about
      shell programming (and just about anything else).

        http://groups.google.com/advanced_group_search

   -------------------------------
   POSIX/SUS ("the standard")

      POSIX (Portable Operating System Interface) and SUS (Single Unix
      Specification) have been joined into one standard. This is what
      people usually mean when they refer to "the standard" in
      discussions about unix. When people in this group refer to the
      POSIX shell, they are talking about the shell prescribed by this
      specification. You can find this standard at

        http://www.opengroup.org/onlinepubs/007904975/toc.htm

   -------------------------------
   UUOC
   
      This is short for "Useless use of cat". It's used to point out
      that some example script has used cat when it could have used
      redirection instead. It's more efficient to redirect input than
      it is to spawn a process to run cat. For example

        $ cat file | tr -d 'xyz'

      runs two processes, one for cat and one for tr. This is less
      efficient than

        $ tr -d 'xyz' < file

      In general, "cat file | somecommand" can be more efficiently
      replaced by "somecommand < file"

      or (especially for multi-file input)

        $ somecommand file [file ...]

      but check the man page for "somecommand" to find out if it will
      accept this syntax.

      For more details about this, as well as other things like it, see
        http://rhols66.adsl.netsonic.fi/era/unix/award.html

   -------------------------------
   dotfile

      This refers to a file which starts with '.' (a dot). These files
      are not shown in directory listings without the -a (or -A in
      newer versions of ls - check the man page on your system) option
      to ls. Often they are configuration files, subdirectories used
      by applications to store configuration files, NFS swap files, et
      al.

   -------------------------------
   portable

      The word "portable" means different things to different people,
      in different situations, which is to say, there isn't one
      definition of "portable".

      At one extreme, a portable script is one which will work under
      any shell, on any operating system. At this end of the spectrum,
      there is no such thing as a portable shell script (some
      operating systems don't even have shells). If we confine the
      operating system to unix (which would make sense since this is
      comp.unix.shell), the only truly portable scripts are those
      which make no use of built-in shell facilities or syntax, but
      which only call external utilities. For example

        echo Hello World

      would probably qualify. However, that doesn't do anyone much
      good.

      Given that there are probably few (if any) scripts which have to
      meet such a standard, a more frequent use of the word "portable"
      indicates the degree to which a script will run under different
      shells and/or different operating environments.

      For example, if you're writing an installation script for an
      application, and the platforms on which that application runs
      are defined, then the problem is pretty well bounded. The choice
      of shell is one which is available on all required platforms,
      and the syntax to be used is the smallest subset of all the
      variants of that shell on the target platforms.

      The degree to which your shell script needs to be portable has
      to be determined by you, or the requirements you've been given
      for the script.

   -------------------------------
   race condition

      This is a situation in which two entities (processes, threads,
      etc) are trying to access a shared resource, or perform the same
      action, and the result depends on the order of execution of the
      two entities.

   -------------------------------
   shebang

      This is the first line of a shell script, which indicates to the
      operating system which interpreter (shell) it should invoke to
      interpret the script. It has the form

      #!/path/to/shell [ argument ]

      where /path/to/shell might be /bin/sh, /usr/local/bin/bash, etc.

      This line is only interpreted by the operating system. That is,
      if a shell script (test.sh) is executable and run from the
      command line by typing its name and giving the script as an
      argument, as in

      $ sh test.sh

      then sh interprets test.sh. For sh, the shebang line is simply a
      comment, and is ignored.

   -------------------------------
   shells

   A number of shells are discussed in this group, including
   sh
   csh
   pdksh
   ksh88
   ksh93
   tcsh
   zsh
   rc
   es
   bash
   ash
   dash

   These (and more) are names of shells which are referenced in the
   group. A comparison of some of these is available at

     http://www.faqs.org/faqs/unix-faq/shell/shell-differences/

   However, it does not make specific the differences between
   ksh88, ksh93 and pdksh, which are not entirely compatible.

   -------------------------------
   top-posting

   A. top posting
   Q. What's the most irritating way to respond on usenet?

   Please see the following:

      http://catb.org/~esr/jargon/html/T/top-post.html
      http://www.uwasa.fi/~ts/http/quote.html
      http://members.fortunecity.com/nnqweb/
      http://www.guckes.net/mail/editing.html
      
======================================================================

[ 本帖最后由 r2007 于 2005-12-3 16:19 编辑 ]
作者: r2007    时间: 2005-05-25 21:41
标题: comp.unix.shell FAQ(转载http://home.comcast.net/~j.p.h/)
0b. Notes about using echo

   This isn't really a FAQ, but discussions about using echo come up
   often enough that it seems reasonable to have something about it in
   the FAQ list.

   The echo command is not consistent in the handling of its arguments
   from implementation to implementation. Sometimes a string with
   backslash quoted characters will be interpreted in one way, and
   sometimes another.

   Also, if the string being echoed wasn't built into the script
   itself, then it could have shell metacharacters in it, which could
   confuse things. In cases where external input is used to build a
   string to be echoed the string typically should be quoted.

   For example

   s="a string with\na newline and\ta tab"

   Following are some results with various shells:

   ------
   bash:
   $ echo "$s"
   a string with\na newline and\ta tab
   
   $ echo -e "$s"
   a string with
   a newline and        a tab

   -------
   pdksh:
   $ echo "$s"
   a string with
   a newline and        a tab

   $ echo -e "$s"
   $ echo "$s"
   a string with
   a newline and        a tab

   --------
   ksh88:
   $ echo "$s"
   a string with
   a newline and        a tab

   $ echo -e "$s"
   -e a string with
   a newline and        a tab

   -------
   ksh93:
   $ echo "$s"
   a string with\na newline and\ta tab

   $ echo -e "$s"
   -e a string with\na newline and\ta tab

   Note that ksh93 makes the handling of arguments system dependent
   when they contain '\', and/or the first argument begins with '-'.

   http://www.cs.princeton.edu/~jlk/kornshell/doc/man93.html

   POSIX does not allow the -e option. It also makes the result of
   using -n or any string with '\' in it implementation-defined.
   However, on XSI-conforming systems, it disallows options, and
   defines the use of backslash-quoted characters.

   In general, the behavior of echo is system and/or shell dependent
   if its arguments contain a backslash, or its first argument is -n
   or -e.

   The biggest problem with echo is when using it to output strings
   that the script got externally (e.g. user input, or reading from a
   file). These strings may have '\' characters in them for
   example. In this case, results may not be what you expect.

   print is available in some shells, although printf(1) is perhaps
   more portable. Additionally, a here document will give predictable
   results in that it will not expand escape sequences.

   cat <<EOF
   $s
   EOF

   produces

   a string with\na newline and\ta tab

   So consider not using echo unless you are sure what will happen,
   given the shell you're using.   

======================================================================

[ 本帖最后由 r2007 于 2005-12-3 16:27 编辑 ]
作者: r2007    时间: 2005-05-25 21:42
标题: comp.unix.shell FAQ(转载http://home.comcast.net/~j.p.h/)
1. How can I send e-mails with attached files?

   a. Use uuencode
   
      This is the simplest way to do this. For example

      $ uuencode surfing.jpeg surfing.jpeg | mail someone@some.where

      To send regular text as well

      $ (cat mailtext; uuencode surfing.jpeg surfing.jpeg) |
        mail someone@some.where

   b. Use MIME

      $ metasend -b -t someone@some.where -s "Hear our son!" \
        -m audio/basic -f crying.au

      These examples are adapted from
      http://www.shelldorado.com/articles/mailattachments.html which
      goes into much more detail about this.

   c. Use pine (with a patch) or mutt

      

======================================================================

2. How can I generate random numbers in shell scripts?

   This depends on the shell, and the facilities available from the
   OS.

   a. Some shells have a variable called RANDOM, which evaluates to a
      different value every time you dereference it. If your shell has
      this variable,

        $ number=$RANDOM will produce a random number.

   b. Some systems have a /dev/urandom device, which generates a
      stream of bits. This can be accessed using the dd(1) utility. An
      example of this (from a more extensive discussion of different
      techniques at http://www.shelldorado.com/scripts/cmds/rand)

        n=`dd if=/dev/urandom bs=1 count=4 2>/dev/null | od -t u4 | \
        awk 'NR==1 {print $2}'`

        also:

        od -vAn -N4 -tu4 < /dev/urandom

   c. Use a utility such as awk(1), which has random number generation
      included. This approach is the most portable between shells and
      operating systems.

        awk 'BEGIN {srand();print rand()}'

      Note that this doesn't work with older versions of awk. This
      requires a version supporting the POSIX spec for srand(). For
      example, on Solaris this will not work with /usr/bin/awk, but
      will with nawk or /usr/xpg4/bin/awk.

      Also, if you call this line more than once within the same
      second, you'll get the same number you did the previous time.

======================================================================

3. How can I automatically transfer files using FTP with error
   checking?

    First, there are tools to do that: curl, wget, lftp, ncftp. But,
    they are generally not part of the base system (you need to
    install them).
   
    zsh (version 4 and above) provides a FTP facility, see "info -f
    zsh -n 'zsh/zftp Module'"
   
    #! /usr/bin/zsh
    zftp open host user passwd || exit
    zftp get /remote/file > /local/file; r=$?
    zftp close && exit r
   
    With your system "ftp" command, two ways:
   
    1- using "ftp -n". Without the -n option, ftp expects user
    interaction to enter the password, so you'd need to use
    "expect". With "-n", you provide the user and password as any
    other FTP command.
   
    #! /bin/sh
    ftp -n << EOF
    open ftp.domain.org
    user anonymous ${LOGNAME:-`who am i`}@
    binary
    get /remote/file /local/file
    bye
    EOF
   
    The error checking can't be made correctly (if "open" fails, the
    "user" command will be still sent even if it shouldn't).
   
    2- using ~/.netrc
   
    If you put:
   
    <<
    machine ftp.domain.org
    login mylogin
    password mypasswd
    macdef init
      binary
      get /remote/file /local/file
      bye
   
   
    >>
   
    (with the trailing empty line) in your ~/.netrc (ensure it's not
    world readable) and then run "ftp ftp.domain.org", ftp will find
    the matching "machine" entry in your ~/.netrc and use the
    parameters provided there to make the ftp transaction.
   
    Those work at least on Linux, FreeBSD, Solaris, HPUX

======================================================================

[ 本帖最后由 r2007 于 2005-12-3 16:30 编辑 ]
作者: r2007    时间: 2005-05-25 21:43
标题: comp.unix.shell FAQ(转载http://home.comcast.net/~j.p.h/)
4. How can I remove whitespace characters within file names?

   File names in unix can contain all kinds of whitespace characters,
   not just spaces. The following examples only work with spaces,
   adjust accordingly.

   a. Use the substitution capabilities of awk, sed, et al.

        f=`printf '%s\n' "$filename" | sed 's/ /_/g'`

        f=`printf '%s\n' "$filename" | awk '{gsub(" ","_");print $0}'`

        f=`printf '%s\n' "$filename" | tr ' ' _`

      Add characters to the tr command line as needed (see the man
      page for tr to find out the available escape sequences).

      Additionally (although not exactly a one-liner)

        f=`tr ' ' _ <<EOF
        $filename
        EOF
        `

      See section 0a "Notes about using echo" for why echo is not used
      here.

   b. Use the substitution capabilities of the shell if it has
      them. Check the man page for your shell (probably under a
      section named something like "Parameter expansion") to see. For
      example:

      f=${filename// /_}

      With zsh:

      autoload -U zmv
      zmv '* *' '$f:gs/ /_/'

      It should be noted that the zmv solution renames the files (call
      mv internally and adress several problems that may arise) while
      the other solutions only update a variable (and then, renaming
      the files may involve a quite complicated script to do it
      reliably).

======================================================================

5. How can I automate a telnet session?

   This is outside the realm of shell programing, per se. You need
   a more special purpose scripting language such as expect. See
   http://expect.nist.gov/

   Perl scripts can also do this with the Telnet module from CPAN.

======================================================================

6. How do I do date arithmetic?

   This depends on exactly what you have in mind.

   a. Finding yesterday's date

      The GNU version of date has some nice features in this
      respect. For example

        To find yesterday's date

          $ date --date yesterday

        To find tomorrow's date

          $ date --date tomorrow

         See the man page for GNU date for other options. It can also
         provide dates more than one day in the past/future.

       The FreeBSD version of date also provides extensions that can
       do things like this.

         $ date
         Wed Oct 22 13:48:29 CDT 2003
         $ date -v-1d
         Tue Oct 21 13:45:16 CDT 2003

       Playing with the TZ variable isn't a reliable method. If you
       need to do something like this, but don't have GNU or FreeBSD
       date available, see section g. "Arbitrary date arithmetic".

   b. Determining relative ages of files

      If you want to determine whether or not one file is older than
      another, you can (with bash, pdksh, ksh93) do

        $ [[ file1 -ot file2 ]] && echo file1 is older

      or you can use find to search a directory tree for files that
      are newer/older than some file:

        $ find . -name '*.c' -newer test.c

   c. Finding elapsed time

      If you want to find elapsed time, perhaps because you want to
      know when some operation has timed out, some shells (bash, ksh
      zsh [,??])  have a SECONDS variable which tell how many seconds
      have elapsed since the invocation of the shell, or since the
      last time it was set.

      ksh93 has a floating point SECONDS which is locale dependent.

      In zsh 4.1 and above one can be made floating point with: float
      SECONDS

      zsh 4.1 and above also has $EPOCHSECONDS for seconds since
      1970-1-1 0:0:0 UTC (see zsh/datetime module).

   d. Determining leap year

      A leap year in the Gregorian calendar is defined as a year which
      is evenly divisible by 4, however, if it's also evenly divisible
      by 100 then it's not a leap year unless it's also evenly
      divisible by 400.  It gets worse than that, actually, but this
      is as far as I go :-).

      In the Julain calendar which was used before in Europe, only the
      years divisible by 4 where leap years.

      The standard "cal" utility performed the switch between Julian
      and Gregorian calendar in september 1752 (see cal 9 1752) which
      corresponds to the date used in England. The Gregorian calendar
      (created by Pope Gregory III) was first used in 1582 in many
      other countries.

      One possibility for a ksh function to do this (after 1600 AD/CE)
      is

        isleap()
        {
          y=$1
          four=$(( $y % 4 ))
          hundred=$(( $y % 100 ))
          fourhundred=$(( $y % 400 ))
          if [ $four -eq 0 ];then
            if [ $hundred -eq 0 ];then
              if [ $fourhundred -eq 0 ];then
                echo leap year
              else
                echo not a leap year
              fi
            else
              echo leap year
           fi
         else
           echo not a leap year
         fi
        }

      Or, valid with any date with the same calendar switch day as
      POSIX cal's (POSIX syntax):

        is_leap_year() # args: year
        # NB: year before year 1 is year -1, not 0.
        {
          [ "$1" -lt 0 ] && set -- "$(($1 + 1))"
            [ "$(($1 % 4))" -eq 0 ] && {
                [ "$(($1 % 100))" -ne 0 ] || [ "$(($1 % 400))" -eq 0 ] \
                      || [ "$1" -le 1752 ]
            }
        }

      Or in any Bourne shell (see COPYING[1]):

        is_leap_year() { ## USAGE: is_leap_year [year]
          isl_year=${1:-`date +%Y`}
          case $isl_year in
            *0[48] |\
            *[2468][048] |\
            *[13579][26] |\
            *[13579][26]0|\
            *[2468][048]00 |\
            *[13579][26]00 ) _IS_LEAP_YEAR=1
               return 0 ;;
            *) _IS_LEAP_YEAR=0
               return 1 ;;
          esac
        }

      On FreeBSD, use the -f option to date(1) to pass in the
      (supposed) February 29 in the current year and then print it the
      day of the month again to see if there really is such a date
      (note that you need -j as well as -f, otherwise date(1) thinks
      you want to set the clock):

      if [ $(date -jf%Y%m%d $(date +%Y0229) +%d) = 29 ]; then
        echo Leap year!
      fi

   e. Determining the last day of a month.

      There are a number of possibilities for doing this which have
      been mentioned in the group. The following is a sampling:

      In any Bourne-type shell (in conjunction with is_leap_year() as
      given above, when month is February) (see COPYING[1]):

        days_in_month() { ## USAGE: days_in_month [month [year]]
          if [ -n "$1" ]
          then
            dim_m=$1
            dim_y=$2
          else
            eval `date "+dim_m=%m dim_y=%Y"`
          fi
          case $dim_m in
            9|09|4|04|6|06|11) _DAYS_IN_MONTH=30 ;;
            1|01|3|03|5|05|7|07|8|08|10|12) _DAYS_IN_MONTH=31 ;;
            2|02) is_leap_year ${dim_y:-`date +%Y`} &&
              _DAYS_IN_MONTH=29 || _DAYS_IN_MONTH=28 ;;
          esac
          [ ${SILENT_FUNCS:-0} -eq 1 ] || echo $_DAYS_IN_MONTH
        }


      With GNU date:

        year=2003
        month=9
        date -d "$year/$month/1 +1 month -1 day" +%d

      With FreeBSD date use the -v-1d option to date(1) to get the day
      before the first day of the next month:

        $ MONTH=12
        $ date -v-1d -jf%Y-%m-%d $(date +%Y-$(((MONTH+1)%12))-01) +%d
        31
        
      In the shell using cal (But beware of implementations of cal
      which print more than one month at a time):

        month=9 ; year=2003   # adjust
        ##
        for lday in `cal $month $year` ; do : ; done
        echo $lday

        ## or
        set -- `cal $month $year` ; eval lday=\${$#}
        echo $lday
        
      In ksh, bash and zsh:

        : $(cal)
        days_in_month=$_

      In zsh:

        days_in_month=${$(cal)[-1]}

   f. Determining the day of the week for a given date.

      This algorithm is known as Zeller's congruence. An explanation
      of it is available from the Dictionary of Algorithms and Data
      Structures web page at NIST:

        http://www.nist.gov/dads/

      Also, a fuller explanation is available at

        http://www.merlyn.demon.co.uk/zeller-c.htm#ZC

      An example in C, with a short explanation, is given at

        http://wwwcdf.pd.infn.it/MLO/Calendars/Notes.html#zeller

      A shell (ksh93) implementation of a homework assignment (given
      for illustration only - don't turn this in as yours - you might
      be sorry if it's wrong :-)

        dayofweek()
        {
          # Implementation of a homework assignment given at
          # http://carbon.cudenver.edu/~traup/fa02/lec/hw3.html
          #
          # call with day:   1 - 31
          #           month: March = 1, Jan & Feb are months 11 and
          #                  12 of the previous year.
          #           year:  The year of the century
          #           c:     The previous century
          #
          # For example, for July 4, 1989,
          #   m = 5,  d = 4,  y = 89, and c = 19,
          # while for January 25, 1989,
          #   m = 11, d = 25, y = 88, and c = 19.
          #
          # The output is the day of the week with Sunday = 0,
          # Monday = 1, etc.

          d=$1
          m=$2
          y=$3
          c=$4

          A=$(( ($m * 13 - 1) / 5 ))
          B=$(( $y / 4 ))
          C=$(( $c / 4 ))
          D=$(( $A + $B + $C + $d + $y - ($c * 2) ))
          echo $(( $D % 7 ))
        }

      On FreeBSD, use the -f option to date(1) to pass in the date of
      interest and +%A to print the day of the week:

        $ date -jf%Y-%m-%d 2000-01-01 +%A
        Saturday

      (Use +%u or +%w if you want the weekday as a number.  See the
      strftime(3) manpage for details.)

   g. Arbitrary date arithmetic

      To do arbitrary date calculations is more complicated. One
      possibility is to call an external utility, or a program in
      another scripting language, which has this built in. For
      example, perl has wrappers for the unix time functions built in,
      so it can provide some relief in this regard. C programs can
      also be easily written to do date arithmetic (see the examples
      section). One thing to keep in mind, however, is that unix time
      functions are, strictly speaking, limited to the range of time
      between January 1 1970 at midnight, and January 19, 2038 at
      3:14:07. C/Perl programs which calculate dates outside this
      range might work, or they might not, that would depend on the
      implementation.

      To do arbitrary date arithmetic in the shell itself is also
      possible. An article provided on the web by SysAdmin magazine
      describes one way to do this.

        http://www.samag.com/documents/s=8284/sam0307b/0307b.htm

      Another possibility is given in the examples section, from

        http://groups.google.com/groups? ... f%40ogion.it.jyu.fi

      See also
        http://groups.google.com/groups? ... las@spam.is.invalid

      On FreeBSD, the -f and -v options to date(1) cover most things
      you might want to do, with the caveat that only dates within the
      range mentioned above are defined. Dates outside that range are
      not guaranteed to work.

      Also, zsh 4.1 and above has the zsh/datetime module that
      provides the $EPOCHSECONDS and the strftime function.

   h. Getting the number of seconds since the epoch

    - GNU date has the %s format option which returns the epoch
      time.

    - More portably, use awk.

        awk 'BEGIN {srand(); printf("%d\n", srand())}'

      This works because srand() sets its seed value with the
      current epoch time if not given an argument. It also returns
      the previous seed value, so the second call gives the epoch
      time.

      Note that this doesn't work with older versions of awk. This
      requires a version supporting the POSIX spec for srand(). For
      example, on Solaris this will not work with /usr/bin/awk, but
      will with nawk or /usr/xpg4/bin/awk.

      Depending on scheduling, when the call is actually executed,
      etc, this might be off by a second.

    - Another way is to use perl if you have it.

        perl -le 'print time'

    - Also, zsh 4.1 and above has the zsh/datetime module that
      provides the $EPOCHSECONDS and the strftime function.

======================================================================

[ 本帖最后由 r2007 于 2005-12-3 17:44 编辑 ]
作者: r2007    时间: 2005-05-25 21:43
标题: comp.unix.shell FAQ(转载http://home.comcast.net/~j.p.h/)
7. Why did someone tell me to RTFM?

   Because you didn't :-)

   RTFM is part of Usenet lingo, and means "Read The F-ing Manual".
   Generally people say this when someone asks a question that is
   asked so often, and is answered plainly in some relevant man page,
   that they're tired of seeing it asked.

   http://catb.org/~esr/jargon/html/R/RTFM.html

   So RTFM, and the FAQs first before asking. Also, if you're new to
   the group, search Google Groups

   http://groups.google.com/advanced_group_search

   before asking questions. And please don't post your homework
   questions to the group unless you've tried to figure them out, and
   have some specific questions. People will generally be happy to
   help you with your homework if you post what you've got and ask
   specific questions.

======================================================================

8. How do I create a lock file?

   Very carefully :-)

   The scheduler can stop one process in the middle of a non-atomic
   operation, and run another one, which wants to perform the same
   operation. The second one, having a full timeslice, might finish
   the operation. When control returns to the first process, confusion
   will reign.

   The trick is to do something atomic, so that this won't
   happen. There are a couple ways to do this. One is to create a
   directory instead of a file, the other is to create a symbolic
   link. Both operations are defined to be atomic by POSIX/SUS, by
   virtue of the fact that they both require invocation of the
   corresponding system calls, which are atomic.

   Beware of trying to create ANY kind of lock file on an NFS
   partition. NFS pretty much eliminates anything like atomicity.  If
   you're going to create a lock file, make sure you're doing it on a
   local partition, such as /tmp.

   Netscape/Mozilla uses the symbolic link method for its lockfile (in
   spite of the fact that it creates it in the user's home directory,
   which may be NFS mounted). When it starts up it creates a file
   named for the IP address of the machine it's running on, and the
   pid of the creating process. Then it tries to create a symbolic
   link named "lock", which points to that file. If this symlink
   already exists, link(2) will return an error. In a script this
   would work something like

   touch /tmp/xxx
   ln -s /tmp/xxx /tmp/lockfile 2>/dev/null
   ret=$?
   rm /tmp/xxx
   if [ $ret -ne 0 ];then
     echo lockfile already exists
     exit 1
   else
     echo success
   fi

   If you have procmail installed, another possibility is the
   lockfile(1) command that comes with it.

======================================================================

9. How can I convert DOS text files to unix, and vice versa?

    Unix text files consist of lines delimited by an LF ("line-feed")
    character (ASCII 10). DOS uses the two characters CR LF ("carriage
    return", "line feed"; ASCII 13, 10) for the same purpose.

    To convert a DOS text into unix text format, the CR characters
    (control-M) at the end of a line have to be removed. To create a
    DOS text file, the CR character should be added.

    A couple ways to remove CR characters:

      sed 's/^M$//' dos.txt > unix.txt

      tr -d '\r' < dosfile > unixfile

    To add them:   

      sed 's/$/^M/' unix.txt > dos.txt

    Note that "^M" in this case is an embedded control character, (CR,
    ASCII 13). Many shells allow embedding control characters by
    entering ^V first (control-V), resulting in the sequence

        ^V^M

    for entering "^M".

    However, zsh, bash or ksh93 allow for:

      sed $'s/$/\r/'

    There is one special case to be considered: DOS text files
    sometimes contain an explicit end-of-file character ^Z (ASCII 26,
    or octal 32), which has no correspondent character for unix text
    files, where the end-of-file condition is determined
    implicitly. To remove that as well as the CR characters:

      tr -d '\r\032' < dosfile > unixfile

    Note that sed does not understand that notation, but awk does, and
    one simple way to do the opposite conversion is

      $ awk '{printf "%s\r\n" $0}END{printf "%c", 26}' unixfile > dosfile

    This assume a not-quite-ancient awk, in practice anything
    but Solaris /bin/awk (use nawk or /usr/xpg4/bin/awk in Solaris).

    Finally, your system may come with utilities named something like
    dos2unix and unix2dos, or d2u dos2unix fromdos non-standard
    utilities, and GNU recode: recode /CRLF

======================================================================

10. How can a shell prompt be set up to change the title of xterm?

   
    Gives escape sequences for xterm. For example, to change the name
    of the current window to "XXX" (in bash), do

      $ echo -en "\033]2;XXX\007"

      or, more portably:

      $ printf '%b' '\e]2;XXX\a'

    See also "Why doesn't echo do what I want?"

[ 本帖最后由 r2007 于 2005-12-3 16:59 编辑 ]
作者: r2007    时间: 2005-05-25 21:44
标题: comp.unix.shell FAQ(转载http://home.comcast.net/~j.p.h/)
11. How do I get the exit code of cmd1 in cmd1|cmd2

    First, note that cmd1 exit code could be non-zero and still don't
    mean an error. This happens for instance in

    cmd | head -1

    you might observe a 141 (or 269 with ksh93) exit status of cmd1,
    but it's because cmd was interrupted by a SIGPIPE signal when
    "head -1" terminated after having read one line.

    To know the exit status of the elements of a pipeline
    cmd1 | cmd2 | cmd3

    a. with zsh:

       The exit codes are provided in the pipestatus special array.
       cmd1 exit code is in $pipestatus[1], cmd3 exit code in
       $pipestatus[3], so that $? is always the same as
       $pipestatus[-1].

    b. with bash:

       The exit codes are provided in the PIPESTATUS special array.
       cmd1 exit code is in ${PIPESTATUS[0]}, cmd3 exit code in
       ${PIPESTATUS[2]}, so that $? is always the same as
       ${PIPESTATUS: -1}.

    c. with any other Bourne like shells

       You need to use a trick to pass the exit codes to the main
       shell.  You can do it using a pipe(2). Instead of running
       "cmd1", you run "cmd1; echo $?" and make sure $? makes it way
       to the shell.

       exec 3>&1
       eval `
         # now, inside the `...`, fd4 goes to the pipe
         # whose other end is read and passed to eval;
         # fd1 is the normal standard output preserved
         # the line before with exec 3>&1
         exec 4>&1 >&3 3>&-
         {
           cmd1 4>&-; echo "ec1=$?;" >&4
         } | {
           cmd2 4>&-; echo "ec2=$?;" >&4
         } | cmd3
         echo "ec3=$?;" >&4
       `

    d. with a POSIX shell

       You can use this function to make it easier:

       run() {
         j=1
         while eval "\${pipestatus_$j+:} false"; do
           unset pipestatus_$j
           j=$(($j+1))
         done
         j=1 com= k=1 l=
         for a; do
           if [ "x$a" = 'x|' ]; then
             com="$com { $l "'3>&-
                         echo "pipestatus_'$j'=$?" >&3
                       } 4>&- |'
             j=$(($j+1)) l=
           else
             l="$l \"\$$k\""
           fi
           k=$(($k+1))
         done
         com="$com $l"' 3>&- >&4 4>&-
                    echo "pipestatus_'$j'=$?"'
         exec 4>&1
         eval "$(exec 3>&1; eval "$com")"
         exec 4>&-
         j=1
         while eval "\${pipestatus_$j+:} false"; do
           eval "[ \$pipestatus_$j -eq 0 ]" || return 1
           j=$(($j+1))
         done
         return 0
       }
      
       use it as:
      
       run cmd1 \| cmd2 \| cmd3
       exit codes are in $pipestatus_1, $pipestatus_2, $pipestatus_3

======================================================================

12. Why do I get "script.sh: not found"

    a. While script starts with "#!/bin/sh" (^M issue)

       That's the kind of error that occurs when you transfer a file
       by FTP from a MS Windows machine. On those systems, the line
       separator is the CRLF sequence, while on unix the line
       separator is LF alone, CR being just another ordinary character
       (the problem is that it is an invisible one on your terminal
       (where it actually moves the cursor to the beginning of the
       line) or in most text editors or pagers).

       So, if a MSDOS line is "#!/bin/sh", when on a Unix system, it
       becomes "#!/bin/sh<CR>" (other names for <CR> are \r, \015, ^M,
       <Ctrl-M>).

       So, if you run the file as a script, the system will look in
       /bin for an interpreter named "sh<CR>", and report it doesn't
       exist.

       $ sed 'l;d;q' < script.sh
       #!/bin/sh\r$

       shows you the problem ($ marks the end of line, \r is the CR
       character).

    b. PATH issue

       Sometimes a shell is installed someplace other than /bin or
       /usr/bin. For example, a shell which was not part of the OS
       installation might be installed into /usr/local/bin. If the
       script was written on a machine which had ksh located in
       /usr/bin, but was run on a machine where ksh was located in
       /usr/local/bin, the shebang line would not resolve correctly.

       This is unlikely to occur when using sh. However, if the shell
       is bash, zsh, et al, it might be installed in different places
       on different machines.

       One way around this is to use the env command in the shebang
       line. So instead of

       #!/bin/sh

       use

       #!/usr/bin/env sh

       Of course, env might itself live in some other directory than
       /usr/bin, but it's not likely.

======================================================================

13. Why doesn't echo do what I want?

    See also section 0a "Notes about using echo"

    The echo command is not consistent from shell to shell. For
    example, some shells (bash, pdksh [,?]) use the following
    arguments

      -n suppress newline at the end of argument list
      -e interpret backslash-escaped characters
      -E disable interpretation of backslash-escaped characters, even
         on systems where interpretation is the default.

    However, pdksh also allows using \c to disable a newline at the
    end of the argument list.

    POSIX only allows \c to be used to suppress newlines, and doesn't
    accept any of the above arguments.

    ksh88 and ksh93 leave the interpretation of backslash-escaped
    characters up to the implementation.

    [descriptions of behavior of other shells welcome]

    In short, you have to know how echo works in any environment you
    choose to use it in, and its use can therefore be problemmatic. If
    available, print(1) or printf(1) would be better.

======================================================================

[ 本帖最后由 r2007 于 2005-12-3 17:01 编辑 ]
作者: r2007    时间: 2005-05-25 21:45
标题: comp.unix.shell FAQ(转载http://home.comcast.net/~j.p.h/)
14. How do I loop through files with spaces in their name?

    So, you're going to loop through a list of files? How is this list
    stored? If it's stored as text, there probably was already an
    assumption about the characters allowed in a filename. Every
    character except '\0' (NUL) is allowed in a file path on Unix.  So
    the only way to store a list of file names in a file is to
    separate them by a '\0' character (if you don't use a quoting
    mechanism as for xargs input).

    Unfortunately most shells (except zsh) and most standard unix text
    utilities (except GNU ones) can't cope with "\0"
    characters. Moreover, many tools, like "ls", "find", "grep -l"
    output a \n separated list of files. So, if you want to
    postprocess this output, the simpler is to assume that the
    filenames don't contain newline characters (but beware that once
    you make that assumption, you can't pretend anymore your code is
    reliable (and thus can't be exploited)).

    So, if you've got a newline separated list of files in a
    list.txt file, Here are two ways to process it:

    1-

    while IFS= read -r file <&3; do
      something with "$file" # be sure to quote "$file"
    done 3< list.txt
    (if your read doesn't have the "-r" option, either make another
    assumption that filenames don't contain backslashes, or use:

    exec 3<&0
    sed 's/\\/&&/g' < list.txt |
    while IFS= read file; do
      something with "$file" <&3 3<&-
    done
    )

    2-

    IFS="
    " # set the internal field separator to the newline character
      # instead of the default "<space><tab><NL>".
   
    set -f # disable filename generation (or make the assumption that
           # filenames don't contain *, [ or ? characters (maybe more
           # depending on your shell)).
   
    for file in $(cat < list.txt); do
      something with "$file" # it's less a problem if you forget to
                             # quote $file here.
    done
   
    Now, beware that there are things you can do before building
    this list.txt. There are other ways to store filenames. For
    instance, you have the positional parameters.
   
    with:
    set -- ./*.txt
   
    you have the list of txt files in the current directory, and no
    problem with weird characters. Looping through them is just a
    matter of:
   
    for file
    do something with "$file"
    done
   
    You can also escape the separator. For instance, with
   
    find . -exec sh -c 'printf %s\\n "$1" | sed -n '"':1
      \$!{N;b1
      }
      s/|/|p/g;s/\n/|n/g;p'" '{}' '{}' \;
      
    instead of
   
    find . -print
   
    you have the same list of files except that the \n in filenames
    are changed to "|n" and the "|" to "|p". So that you're sure
    there's one filename per line and you have to convert back "|n"
    to "\n" and "|p" to "|" before referring to the file.

======================================================================

15. how do I change my login shell?

    See  http://www.faqs.org/faqs/unix-faq/shell/shell-differences

    Unless you have a very good reason to do so, do not change root's
    default login shell. By "default login shell" is meant the shell
    recorded in /etc/passwd. Note that "I login as root but don't like
    the default shell" isn't a good reason.

    The default shell for root is one which will work in single user
    mode, when only the root partition is mounted. This is one of the
    contexts root works in, and the default shell must accommodate
    this. So if you change it to a dynamically linked shell which
    depends on libraries that are not in the root partition, you're
    asking for trouble.

    The safest way of changing root's shell is to login as root and
    then

      # SHELL=/preferred/shell; export SHELL
      # exec <your preferred shell with login flag>

      e.g.

      # SHELL=/usr/bin/ksh; export SHELL
      # exec $SHELL -l

    Another possibility is to add something to root's .profile or
    .login which checks to see if the preferred shell is runnable, and
    then execs it. This is more complicated and has more pitfalls than
    simply typing "exec <shell>" when you login though. For example,
    one of the libraries that the desired shell relies on might have
    been mangled, etc. One suggestion that has been made is

      if [ -x /usr/bin/ksh ]; then
        SHELL=/usr/bin/ksh; export SHELL
        ENV=/root/.kshrc; export ENV   
        /usr/bin/ksh -l && exit
      fi

    A safer way is to try to run a command with the preferred shell
    before you try to exec it. This will lessen the possibility that
    the shell or one of the libraries it depends on has been
    corrupted, or that one of the libraries it depends on is not in
    the available mounted partitions.

      if [ -x /usr/bin/ksh ]; then
        /usr/bin/ksh -c echo >/dev/null 2>&1
        if [ $? -eq 0 ];then
           SHELL=/usr/bin/ksh; export SHELL
           ENV=/root/.kshrc; export ENV
           /usr/bin/ksh -l && exit
        fi
      fi

    Another common approach is to create another user with UID 0. For
    example, FreeBSD systems commonly create an account named toor,
    which can be setup however you like. This bypasses the
    controversy.
   
======================================================================

16. When should I use a shell instead of perl/python/ruby/tcl...

    a. Portability

       In many cases it can't be assumed that perl/python/etc are
       installed on the target machine. Many customer sites do not
       allow installation of such things. In cases like this, writing
       a shell script is more likely to be successful. In the extreme,
       writing a pure Bourne shell script is most likely to succeed.

    b. Maintainability

       If the script is one which serves some important purpose, and
       will need to be maintained after you get promoted, it's more
       likely that a maintainer can be found for a shell script than
       for other scripting languages (especially less used ones such
       as ruby, rexx, etc).

    c. Policy

       Sometimes you're just told what to use :-)

======================================================================

17. Why shouldn't I use csh?

       http://www.grymoire.com/Unix/CshTop10.txt
       http://www.grymoire.com/Unix/Csh.html#uh-0
       http://www.faqs.org/faqs/unix-faq/shell/csh-whynot/
   
======================================================================

[ 本帖最后由 r2007 于 2005-12-3 17:02 编辑 ]
作者: r2007    时间: 2005-05-25 21:45
标题: comp.unix.shell FAQ(转载http://home.comcast.net/~j.p.h/)
18. How do I reverse a file?

    Non-standard commands to do so are GNU tac and "tail -r".  sed
    '1!G;h;$!d' is subject to sed limitation on the size of its hold
    space and is generally slow.
   
    The awk equivalent would be:
   
    awk '{l[n++]=$0}END{while(n--)print l[n]}'
    It stores the whole file in memory.
   
    The best approach in terms of efficiency portability and resource
    cosumption seems to be:
   
    cat -n | sort -rn | cut -f2-

    "cat -n" is not POSIX but appears to be fairly
    portable. Alternatives are "grep -n '^'", "awk '{print NR,$0}'".
    Also, nl can be used as

    nl -ba -d'
    '

    i.e. NL as the delimiter.

======================================================================

19. how do I remove the last n lines?


    First we need to tell the code how many lines we want to cut
    from the bottom of a file.

      X=10

    Then We can do this:

       head -n $(( $(wc -l < file ) - $X )) file >$$ \
         && cat $$ >file && rm $$

       The break down:  
       1) $(wc -l < file)
          Find out how many lines are in the file. Need to use
          redirection so wc won't print the file name.
       2) $(( $lines_in_file - $X ))
          Take the output from step one and do some math to find out
          how many lines we want to have when all is said and done.
       3) head -$lines_when_said_and_done file
           extracts all but the unwanted lines from the file,
           and >$$ puts those lines into a temp file that has
           the name of the pid of the current shell.
       4) && cat $$ > file
          if everything has worked so far then cat the temp file into
          the original file.  This is better than mv or cp because it
          insures that the permissions of the temp file do not
          override with the perms of the original file.
       5) && rm $$
          Remove the temp file.

    AWK solutions:

       awk 'NR<=(count-12)' count="`awk 'END{print NR}' file`" file

       awk 'NR>n{print a[NR%n]} {a[NR%n]=$0}' n=12 file

       awk 'BEGIN{n=12} NR>n{print a[NR%n]} {a[NR%n]=$0}' file

      Whenever a line is read, the line that came 12 lines ago is
      printed, and then overwritten with the newly read line, using an
      rolling array indexed 0..11.

      See also question 26. for information about setting awk
      variables on the command line.

    $SHELL/sed/mv solutions:

      L=`wc -l <file`
      DL=`expr $L - 11`
      sed "$DL,\$d" file

      L=`wc -l <file`
      DL=`expr $L - 12`
      sed "${DL}q" file

      sed "`expr \`wc -l <file\` - 12`q" file

      sed -n -e :a -e '1,12{N;ba' -e '}' -e 'P;N;D' file

      The last solution is basically same algorithm as the rolling
      array awk solutions, and shares with them the advantage that
      the file is only read once - they will even work in a
      pipe. There may be limitations in sed's pattern space which
      would make this unusable however.

    PERL solution:

      perl -ne' print shift @x if @x == 12; push @x, $_ ' file

    Using GNU dd:

      ls -l file.txt | {
        IFS=" "
        read z z z z sz z
        last=`tail -10 file.txt | wc -c`
        dd bs=1 seek=`expr $sz - $last` if=/dev/null of=file.txt
      }

======================================================================

20. how do I get file size, or file modification time?

    If your system has stat(1), use it. On Linux, for example:

      filesize=$(stat -c %s -- filename)

      or use cut, awk, etc on the output.

    Probably the most portable solution is to use wc

     filesize=`wc -c < "$file"`

    ls may be able to tell you what you want to know.  From the man
    page for ls we learn about "ls -l" the file mode, the number of
    links to the file, the owner name, the group name, the size of the
    file (in bytes), the timestamp, and the filename.  For the file
    size in human readable formate use the "-h" option.

    For example:

    $ ls -l timeTravel.html
    -rw-rw-r-- 1 user user 20624 Jun 19  2002 timeTravel1.html

    so to get the file size:

    $ set -- `ls -l timeTravel1.html`
    $ echo $5
    20624

    Note that ls doesn't always give the date in the same
    format. Check the man page for ls on your system if that
    matters. If you're interested in the file modification time.

    Another possibility is to use GNU ls, which has a -T option giving
    complete time information for the file, including month, day,
    hour, minute, second and year.

    See also GNU find (-printf), GNU stat, GNU date (-r) and zsh stat
    (+mtime).

    On FreeBSD 4, you can use the -lT option to ls(1) to get the full
    modification time and the -f option to date(1) to parse it, for
    example:

    $ FILE=/etc/motd
    $ date -jf'%b %d %T %Y' +%Y-%m-%dT%T \
      $(ls -lT $FILE|tr -s ' ' \\t|cut -f6-9)
    2003-09-09T16:04:06

    Adjust syntax as needed if your shell is FreeBSD sh

======================================================================

[ 本帖最后由 r2007 于 2005-12-3 17:04 编辑 ]
作者: r2007    时间: 2005-05-25 21:46
标题: comp.unix.shell FAQ(转载http://home.comcast.net/~j.p.h/)
  1. 21. How do I get a process id given a process name? Or, how do I find
  2.     out if a process is still running, given a process ID?

  3.     There isn't a reliable way to to this portably in the shell. Some
  4.     systems reuse process ids much like file descriptors. That is,
  5.     they use the lowest numbered pid which is not currently in use
  6.     when starting a new process. That means that the pid you're
  7.     looking for is there, but might not refer to the process you think
  8.     it does.

  9.     The usual approach is to parse the output of ps, but that involves
  10.     a race condition, since the pid you find that way may not refer to
  11.     the same process when you actually do something with that
  12.     pid. There's no good way around that in a shell script though, so
  13.     be advised that you might be stepping into a trap.

  14.     One suggestion is to use pgrep if on Solaris, and 'ps h -o pid -C
  15.     $STRING' if not, and your ps supports that syntax, but neither of
  16.     those are perfect or ubiquitous.

  17.     The normal solution when writing C programs is to create a pid
  18.     file, and then lock it with fcntl(2). Then, if another program
  19.     wants to know if that program is really running, it can attempt to
  20.     gain a lock on the file. If the lock attempt fails, then it knows
  21.     the file is still running.

  22.     We don't have options in the shell like that, unless we can supply
  23.     a C program which can try the lock for the script. Even so, the
  24.     race condition described above still exists.

  25. ======================================================================

  26. 22. How do I get a script to update my current environment?

  27.     Processes in unix cannot update the environment of the process
  28.     that spawned them. Consequently you cannot run another process
  29.     normally and expect it to do that, since it will be a child of the
  30.     running process. There are a couple ways it can be done though.

  31.     a. source the script

  32.        This means that you use whatever syntax your shell has to read
  33.        the desired script into the current environment.

  34.        In Bourne derived shells (sh/ksh/bash/POSIX/etc) the syntax
  35.        would be

  36.          $ . script

  37.        In csh type shells this would be

  38.          $ source script

  39.      b. use eval

  40.         The eval command constructs a command by evaluating and then
  41.         executing a set of arguments. If those arguments evaluate to a
  42.         shell variable assignment, the current environment will be
  43.         updated. For example

  44.           --- exportFoo
  45.           #!/bin/ksh
  46.           echo export FOO=bar

  47.         If you run this like

  48.           eval "`exportFoo`"

  49.         the value of FOO will be set to 'bar' in the calling
  50.         shell. Note that the quotes are recommended as they will
  51.         preserve any whitespace that may be present in the variables
  52.         being set.

  53.         However, be aware that eval'ing a script written in another
  54.         shell could turn out to be the wrong thing to do. For example,
  55.         eval'ing this from a ksh script

  56.         #!/bin/csh
  57.         echo setenv FOO bar

  58.         Would not do what you expect. It would produce an error,
  59.         because ksh doesn't have a setenv command.

  60. ======================================================================

  61. 23. How do I rename *.foo to *.bar?

  62.     Naive examples in ksh/bash (which may or may not work many times)

  63.       $ ls *.foo | while read f;do mv "$f" "${f%.*}".bar;done

  64.     More generically

  65.       $ ls *.foo | while read f;do mv "$f" `basename "$f" .foo`.bar;done

  66.     However, these examples contain a potentially unnecessary use of
  67.     ls (ie, if the number of files is small enough to not overflow the
  68.     command line buffer), and will fail if any file names contain a
  69.     newline, or if there are leading or trailing spaces. An
  70.     alternative is:

  71.       for file in *.foo
  72.       do
  73.         mv -- "$file" "`basename -- \"$file\" .foo`.bar"
  74.       done

  75.     Also, tests for existence of files should also be incorporated,
  76.     e.g.:

  77.       for file in ./*.foo
  78.       do
  79.         newfile=`basename "$file" .foo`.bar
  80.         [ -f "$file" ] || continue
  81.         [ -f "$newfile" -o -d "$newfile" ] && continue
  82.         mv "$file" "$newfile"
  83.       done

  84.     In some linux distributions you may be able to use the rename
  85.     command

  86.       $ rename .foo .bar *

  87.     If not (Debian, for one, comes with a perl version of rename that
  88.     won't work with that command line) try

  89.       $ rename 's/.foo/.bar/' *.foo

  90.     More options, and much more discussion about this, is available
  91.     from [url]http://www.faqs.org/faqs/unix-faq/faq/part2/section-6.html[/url]

  92.     Note that for file specifications which don't match existing
  93.     files, the shell usually responds with something like "ls: *.foo:
  94.     No such file or directory", which will mess up your processing of
  95.     file names. One possibility is

  96.     #! /bin/sh
  97.     set x [*].foo ./*.foo
  98.     case "$2$3" in
  99.       "[*].foo./*.foo") ;;
  100.       *)
  101.         shift 2
  102.         for file
  103.         do
  104.           repl=`basename "$file" .foo`.bar
  105.           mv "$file" "$repl"
  106.         done;;
  107.      esac

  108.      Except that contrary to (zsh) mmv or zmv it doesn't check for
  109.      file overwriting and fails for filenames with NLs before the "."
  110.      and doesn't handle dotfiles.

  111. ======================================================================
复制代码

[ 本帖最后由 r2007 于 2006-8-11 14:30 编辑 ]
作者: r2007    时间: 2005-05-25 21:47
标题: comp.unix.shell FAQ(转载http://home.comcast.net/~j.p.h/)
  1. 24. How do I use shell variables in awk scripts

  2.    Short answer = either of these, where "svar" is a shell variable
  3.    and "avar" is an awk variable:

  4.         awk -v avar="$svar" '... avar ...' file
  5.         awk 'BEGIN{avar=ARGV[1];ARGV[1]=""}... avar ...' "$svar" file

  6.    depending on your requirements for handling backslashes and
  7.    handling ARGV[] if it contains a null string (see below for details).

  8.    Long answer = There are several ways of passing the values of
  9.    shell variables to awk scripts depending on which version of awk
  10.    (and to a much lesser extent which OS) you're using. For this
  11.    discussion, we'll consider the following 4 awk versions:

  12.    oawk (old awk, /usr/bin/awk and /usr/bin/oawk on Solaris)
  13.    nawk (new awk, /usr/bin/nawk on Solaris)
  14.    sawk (non-standard name for /usr/xpg4/bin/awk on Solaris)
  15.    gawk (GNU awk, downloaded from [url]http://www.gnu.org/software/gawk)[/url]

  16.    If you wanted to find all lines in a given file that match text
  17.    stored in a shell variable "svar" then you could use one of the
  18.    following:

  19.    a) awk -v avar="$svar" '$0 == avar' file
  20.    b) awk -vavar="$svar" '$0 == avar' file
  21.    c) awk '$0 == avar' avar="$svar" file
  22.    d) awk 'BEGIN{avar=ARGV[1];ARGV[1]=""}$0 == avar' "$svar" file
  23.    e) awk 'BEGIN{avar=ARGV[1];ARGC--}$0 == avar' "$svar" file
  24.    f) svar="$svar" awk 'BEGIN{avar=ENVIRON["svar"]}$0 == avar' file
  25.    g) awk '$0 == '"$svar"'' file

  26.    The following list shows which version is supported by which
  27.    awk on Solaris (which should also apply to most other OSs):

  28.         oawk = c, g
  29.         nawk = a, c, d, f, g
  30.         sawk = a, c, d, f, g
  31.         gawk = a, b, c, d, f, g

  32.    Notes:

  33.    1) Old awk only works with forms "c" and "g", both of which have
  34.       problems.

  35.    2) GNU awk is the only one that works with form "b" (no space
  36.       between "-v" and "var="). Since gawk also supports form "a",
  37.       as do all the other new awks, you should avoid form "b" for
  38.       portability between newer awks.

  39.    3) In form "c", ARGV[1] is still getting populated, but
  40.       because it contains an equals sign (=), awk changes it's normal
  41.       behavior of assuming that arguments are file names and now instead
  42.       assumes this is a variable assignment so you don't need to clear
  43.       ARGV[1] as in form "d".

  44.    4) In light of "3)" above, this raises the interesting question of
  45.       how to pass awk a file name that contains an equals sign - the
  46.       answer is to do one of the following:

  47.        i) Specify a path, e.g. for a file named "abc=def" in the
  48.           current directory, you'd use:

  49.                 awk '...' ./abc=def

  50.           Note that that won't work with older versions of gawk or with
  51.           sawk.

  52.       ii) Redirect the input from a file so it's opend by the shell
  53.           rather than awk having to parse the file name as an argument
  54.           and then open it:

  55.                 awk '...' < abc=def

  56.           Note that you will not have access to the file name in the
  57.           FILENAME variable in this case.

  58.    5) An alternative to setting ARGV[1]="" in form "d" is to delete
  59.       that array entry, e.g.:

  60.         awk 'BEGIN{avar=ARGV[1];delete ARGV[1]}$0 == avar' "$svar" file

  61.       This is slightly misleading, however since although ARGV[1]
  62.       does get deleted in the BEGIN section and remains deleted
  63.       for any files that preceed the deleted variable assignment,
  64.       the ARGV[] entry is recreated by awk when it gets to that
  65.       argument during file processing, so in the case above when
  66.       parsing "file", ARGV[1] would actually exist with a null
  67.       string value just like if you'd done ARGV[1]="". Given that

  68.       it's misleading and introduces inconsistency of ARGV[]
  69.       settings between files based on command-line order, it is
  70.       not recommended.

  71.    6) An alternative to setting svar="$svar" on the command line
  72.       prior to invoking awk in form "f" is to export svar first,
  73.       e.g.:

  74.         export svar
  75.         awk 'BEGIN{avar=ENVIRON["svar"]}$0 == avar' file

  76.       Since this forces you to export variables that you wouldn't
  77.       normally export and so risk interfering with the environment
  78.       of other commands invoked from your shell, it is not recommended.

  79.    7) When you use form "d", you end up with a null string in
  80.       ARGV[1], so if at the end of your program you want to print
  81.       out all the file names then instead of doing:

  82.         END{for (i in ARGV) print ARGV[i]}

  83.       you need to check for a null string before printing. or
  84.       store FILENAMEs in a different array during processing.
  85.       Note that the above loop as written would also print the
  86.       script name stored in ARGV[0].

  87.    8) When you use form "a", "b", or "c", the awk variable
  88.       assignment gets processed during awks lexical analaysis
  89.       stage (i.e. when the internal awk program gets built) and
  90.       any backslashes present in the shell variable may get
  91.       expanded so, for example, if svar contains "hi\there"
  92.       then avar could contain "hi<tab>there" with a literal tab
  93.       character. This behavior depends on the awk version as
  94.       follows:

  95.       oawk: does not print a warning and sets avar="hi\there"
  96.       sawk: does not print a warning and sets avar="hi<tab>here"
  97.       nawk: does not print a warning and sets avar="hi<tab>here"
  98.       gawk: does not print a warning and sets avar="hi<tab>here"

  99.       If the backslash preceeds a character that has no
  100.       special meaning to awk then the backslash may be discarded
  101.       with or without a warning, e.g. if svar contained "hi\john"
  102.       then the backslash preceeds "j" and "\j" has no special
  103.       meaning so the various new awks each would behave differently
  104.       as follows:

  105.       oawk: does not print a warning and sets avar="hi\john"
  106.       sawk: does not print a warning and sets avar="hi\john"
  107.       nawk: does not print a warning and sets avar="hijohn"
  108.       gawk: prints a warning and sets avar="hijohn"

  109.    9) None of the awk versions discussed here work with form "e" but
  110.       it is included above as there are older (i.e. pre-POSIX) versions
  111.       of awk that will treat form "d" as if it's intended to access a
  112.       file named "" so you instead need to use form "e". If you find
  113.       yourself with that or any other version of "old awk", you need
  114.       to get a new awk to avoid future headaches and they will not be
  115.       discussed further here.

  116.    So, the forms accepted by all 3 newer awks under discussion (nawk,
  117.    sawk, and gawk) are a, c, d, f, and g. The main differences between
  118.    each of these forms is as follows:

  119.       |-------|-------|----------|-----------|-----------|--------|
  120.       | BEGIN | files | requires |  accepts  |  expands  |  null  |
  121.       | avail |  set  |  access  | backslash | backslash | ARGV[] |
  122.       |-------|-------|----------|-----------|-----------|--------|
  123.    a) |   y   |  all  |     n    |     n     |     y     |   n    |
  124.    c) |   n   |  sub  |     n    |     n     |     y     |   n    |
  125.    d) |   y   |  all  |     n    |     n     |     n     |   y    |
  126.    f) |   y   |  all  |     y    |     n     |     n     |   n    |
  127.    g) |   y   |  all  |     n    |     y     |    n/a    |   n    |
  128.       |-------|-------|----------|-----------|-----------|--------|

  129.    where the columns mean:

  130.    BEGIN avail = y: variable IS available in the BEGIN section
  131.    BEGIN avail = n: variable is NOT available in the BEGIN section

  132.    files set = all: variable is set for ALL files regardless of
  133.                 command-line order.
  134.    files set = sub: variable is ONLY set for those files subsequent
  135.                 to the definition of the variable on the command line

  136.    requires access = y: variable DOES need to be exported or set on
  137.                 the command line
  138.    requires access = n: shell variable does NOT need to be exported
  139.                 or set on the command line

  140.    accepts backslash = y: variable CAN contain a backslash without
  141.                 causing awk to fail with a syntax error
  142.    accepts backslash = n: variable can NOT contain a backslash without
  143.                 causing awk to fail with a syntax error

  144.    expands backslash = y: if the variable contains a backslash, it IS
  145.                 expanded before execution begins
  146.    expands backslash = n: if the variable contains a backslash, it is
  147.                 NOT expanded before execution begins

  148.    null ARGV[] = y: you DO end up with a null entry in the ARGV[]
  149.         array
  150.    null ARGV[] = n: you do NOT end up with a null entry in the ARGV[]
  151.         array

  152.    For most applications, form "a" and "d" provide the most intuitive
  153.    functionality. The only functional differences between the 2 are:

  154.    1) Whether or not backslashes get expanded on variable assignment.
  155.    2) Whether or not ARGV[] ends up containing a null string.

  156.    so which one you choose to use depends on your requirements for
  157.    these 2 situations.

  158. ======================================================================

  159. 25. How do I get input from the user with a timeout?

  160.     In bash or ksh93 you can use the read built-in with the "-t"
  161.     option.

  162.     In zsh, use the zsh/zselect module.

  163.     You can also use your terminal capability to do that.

  164.     {
  165.       s=$(stty -g)
  166.       stty -icanon min 0 time 100
  167.       var=$(head -n 1)
  168.       stty "$s"
  169.     }

  170.     For a 10 second timeout (reset at each key press).


  171. ======================================================================
复制代码

[ 本帖最后由 r2007 于 2006-4-20 22:03 编辑 ]
作者: r2007    时间: 2005-05-25 21:47
标题: comp.unix.shell FAQ(转载http://home.comcast.net/~j.p.h/)
26. How do I get one character input from the user?

    In bash this can be done with the "-n" option to read.
    In ksh93 it's read -N
    In zsh it's read -k

    More portably:

    OLDSTTY=$(stty -g)  # save our terminal settings
    stty cbreak  # enable independent processing of each input character
    ONECHAR=$(dd bs=1 count=1 2>/dev/null)  # read one byte from standard in
    stty "$OLDSTTY"  # restore the terminal settings

    Use the `something` format if your shell doesn't understand
    $(something).  This reads from standard input, which may or may not
    be desirable.  If you want to read from the terminal regardless of
    where standard input is, add "if=$(tty)" to the dd command.

======================================================================

27. why isn't my .profile read?

    ~/.profile is only read for login shells.  In short if you don't
    see a login prompt then your ~/.profile isn't being read.  You
    can fix this by either porting all of the things in your
    ~/.profile to /etc/profile or your shells rc script such as
    ~/.bashrc or ~/.zshrc.

    You may have to set the ENV variable in your login shell to get
    the .*rc shell read. See the man page for your shell to understand
    how it works.

======================================================================

28. why do I get "[5" not found in "[$1 -eq 2]"?

    Because you didn't RTFM :-)

    "[" is an alias for the "test" command. As such, it's called by a
    script like any other command (this applies even if test is
    builtin). Since the command line uses spaces to separate a command
    from its arguments, you have to put a space between '[' and its
    argument. So:

      $ [ -f xxx ] isn't the same as
      $ [-f xxx ]

    In the latter case, the shell will think that "[-f" is the
    command, not "[" with arguments "-f xxx ]

======================================================================

29. How do I exactly display the content of $var (with a \n appended).

    A: on POSIX systems or with shells with builtin printf (bash2,
    ksh93, zsh4.1, dash...)

    printf '%s\n' "$var"

    (except for memory/environment full errors, should be expected
    to work at least if $var is not longer than LINE_MAX (supposed
    to be at least _POSIX2_LINE_MAX == 2048), no hardcoded limits in
    zsh/ksh/bash/dash builtins)

    ksh, zsh:
    print -r -- "$var"

    zsh:
    echo -E - "$var"

    Other bourne like shells:

    cat << EOF
    $var
    EOF
    (creates a temporary file and forks a process)

    expr "x$var" : 'x\(.*\)'
    (limited to 126 characters with some exprs, may return a
    non-null exit code).

    With ash:
    (unset a; ${a?$var}) 2>&1

    printf %s "$var"      # posix
    print -rn -- "$var"   # zsh/ksh
    echo -nE - "$var"     # zsh

    awk 'NR>1{print ""}{printf("%s",$0)}' << EOF
    $var
    EOF

======================================================================

30. How do I split a pathname into the directory and file?

    The most portable way of doing this is to use the external
    commands dirname(1) and basename(1), as in

      pathname='/path/to/some/file'
      dir=`dirname "$pathname"`
      file=`basename "$pathname"`

    However, since this executes an external command, it's slower than
    using shell builtins (if your shell has them). For ksh, bash, zsh
    and POSIX shells the following will do the same thing more
    efficiently:

      pathname=/path/to/some/file
      file=${pathname##*/}

    To get the directory using the shell builtin, you should first
    ensure that the path has a '/' in it.

      case $pathname in
        */*) dir=${pathname%/*};;
        *)   dir=''
      esac

    In zsh, (abd csh, tcsh), you have

      ${pathname:h} (head) ${pathname:t} (tail).

======================================================================

[ 本帖最后由 r2007 于 2005-12-3 17:07 编辑 ]
作者: r2007    时间: 2005-05-25 21:48
标题: comp.unix.shell FAQ(转载http://home.comcast.net/~j.p.h/)
31. How do I make an alias take an argument?

    In Bourne-derived shells aliases cannot take arguments, so if you
    need to be able to do that, define a shell function rather than
    an alias.

    Aliases are often used to reduce the need to type long command
    lines:
      
      alias prog='/opt/bin/prog -x -y -z --long-option'

    Or to make a command default to using certain parameters:

      alias ls='ls -F'

    Shell functions must be used when arguments are needed. For
    example, this will move one or more files to a Trash directory:

      trash() { mv -- "$@" ~/.Trash; }

======================================================================

32. How do I deal with a file whose name begins with a weird character

    Do something to hide the weird character from the command being
    used. Assuming that command is rm, try things like

      rm ./-foo
      rm -- -foo
      rm -i -- * (and then decide what you want to delete interactively)

      If the weird character is not printable, the last option may be
      your best bet. Another possibility in that case is to pipe the
      output of ls into od -c and figure out what the weird character
      is. Then use sed to isolate it and nuke it. However, the rm -i
      approach is probably much safer.

    For more particulars, see the rm man page for your system.

======================================================================

33. Why do I lose the value of global variables that are set in a loop.

    Given the following program

      #!/bin/sh
      x="this is the initial value of x"
      cat dataFile | while read line;do
        x="$line"
      done
      echo x = $x

     You may get the following for output

       x = this is the initial value of x
   
     This is because in the Bourne shell redirected control structures
     run in a subshell, so the value of x only gets changed in the
     subshell, and is lost when the loop ends.

     In other shells the same result may be seen because of the way
     pipelines are handled. In shells other than ksh (not pdksh) and
     zsh elements of a pipeline are run in subshells. In ksh and zsh,
     the last element of the pipeline is run in the current shell.

     An alternative for non-Bourne shells is to use redirection
     instead of the pipeline

      #!/bin/sh
      x="this is the initial value of x"
      while read line;do
        x="$line"
      done < dataFile
      echo x = $x

    With a Bourne shell you need to reassign file descriptors, so no
    pipline or redirection in the loop is involved.

      exec 3<&0         # save stdin
      exec < file
      while read line; do
        x=$line
      done
      exec 0<&3        # restore stdin

    Note that putting #!/bin/sh at the top of a script doesn't
    guarantee you're using the Bourne shell. Some systems link /bin/sh
    to some other shell. Check your system documentation to find out
    what shell you're really getting in this case.

======================================================================

34. How do I batch a FTP download/upload?

    The best way to handle this is with ncftpput and ncftpget which
    are part of the ncftp program.  ncftpput -u username -p password
    somewebsite.com /pics *jpg The above usage of the username and
    password is not recomend though as it will be seen by anyone using
    "ps" while the script is running.  ncftp has a way to handle that
    as well.  Just create a file with the information in the following
    formate:

              host somewebsite.com
              user username
              pass password

    Then just use the -f option on the ncftp program:
    ncftpput -f /home/username/somefile somewebsite.com /pics *jpg

    ncftp can be found at  http://freshmeat.net/projects/ncftp/
   
    If you want to do this interactively, there's no need to keep the
    password in a file. For example, if you're building a program on
    one machine, but testing it on another, and you have to keep
    ftp'ing the files, you can cut down on typing by doing something
    like

    #!/bin/ksh
    ftp -n $remote_host <<EOF
    user <username>
    cd <path to build directory>
    prompt
    bin
    mget "$@"
    bye
    EOF

    The ftp program will automatically ask you for the password, then
    do the rest for you.

[ 本帖最后由 r2007 于 2005-12-3 17:08 编辑 ]
作者: r2007    时间: 2005-05-25 21:49
标题: comp.unix.shell FAQ(转载http://home.comcast.net/~j.p.h/)
Appendix A: Examples

  Web sites:
  -------------------------------------------

    Heiner Steven's Shelldorado site has quite a few example scripts,
    tutorials, and links to other such places.

      http://www.shelldorado.com/

  Arbitrary Date Arithmetic
  -------------------------------------------

    From: Tapani Tarvainen (tt@it.jyu.fi)
    Subject: Re: yesterday's date under the shell
    View: Complete Thread (8 articles)
    Original Format
    Newsgroups: comp.unix.shell
    Date: 2002-02-12 07:45:05 PST
   
    "Jean-No?l" <Jean@freckles.de> writes:
   
    > To determine the yesterday date i do it so:
    > TZ=PST+24 date +%d
    > it work well but my question is:
    > does this work on all systems and all shells
    > or should i do it otherwise ???
   
    No, it does not work on all systems at all times.
    In some it will work practically always,
    on others never, on most it works sometimes
    and sometimes not. I would recommend against it.
   
    Unfortunately there is no short and sweet portable
    solution. If you have or can install Gnu date it
    will do it cleanly, otherwise you can find a number
    of solutions posted in this group in the past.
    For a general solution you could try the following,
    which should work with POSIXy shells (I've only tested
    it with HP's which is essentially ksh88, though):
   
    #! /usr/bin/sh
   
    # Date calculations using POSIX shell
    # Tapani Tarvainen July 1998, February 2001 (POSIXified)
    # This code is in the public domain.
   
    # Julian Day Number from calendar date
    date2julian() #  day month year
    {
      day=$1;  month=$2;  year=$3
      tmpmonth=$((12 * year + month - 3))
      tmpyear=$((tmpmonth / 12))
      print $(( (734 * tmpmonth + 15) / 24 -  2 * tmpyear + \
        tmpyear/4 - tmpyear/100 + tmpyear/400 + day + 1721119 ))
    }
   
    # Calendar date from Julian Day Number
    julian2date() # julianday
    {
      tmpday=$(($1 - 1721119))            
      centuries=$(( (4 * tmpday - 1) / 146097))   
      tmpday=$((tmpday + centuries - centuries/4))      
      year=$(( (4 * tmpday - 1) / 1461))         
      tmpday=$((tmpday - (1461 * year) / 4))            
      month=$(( (10 * tmpday - 5) / 306))         
      day=$((tmpday - (306 * month + 5) / 10))   
      month=$((month + 2))                              
      year=$((year + month/12))                        
      month=$((month % 12 + 1))
      print $day $month $year
    }
   
    # Day of week, Monday=1...Sunday=7
    dow() # day month year
    {
      print $(( $(date2julian $1 $2 $3) % 7 + 1))
    }
   
    ##################### The End ########################
   
    Those allow rather arbitrary date computations.
    For example, yesterday's date can be computed like this:
   
    julian2date $(( $(date2julian $(date +"%d %m %Y") ) - 1 ))
   
    --
    Tapani Tarvainen

======================================================================

[ 本帖最后由 r2007 于 2005-12-3 17:09 编辑 ]
作者: icesummit    时间: 2005-05-25 22:54
标题: comp.unix.shell FAQ(转载http://home.comcast.net/~j.p.h/)
这么好的帖子,实在不好意思水一下。
作者: wys0436    时间: 2005-06-08 14:01
标题: comp.unix.shell FAQ(转载http://home.comcast.net/~j.p.h/)
这么好的帖子也折磨人。
关键是英文水平差,看起来比较慢
所以,我是痛苦的人。
作者: tramp568    时间: 2005-07-07 15:03
标题: comp.unix.shell FAQ(转载http://home.comcast.net/~j.p.h/)
谢谢楼主.收下之~!
作者: ZealeS    时间: 2005-07-07 22:30
标题: comp.unix.shell FAQ(转载http://home.comcast.net/~j.p.h/)
好东西,赶快收下,顺便学习下英语。^_^
作者: xy-coordinate    时间: 2005-09-15 09:35
标题: comp.unix.shell FAQ(转载http://home.comcast.net/~j.p.h/)
悔恨自己当初没有学好english!
帖子很不错!
先收藏!
感谢楼主!
作者: 大蚂蚁    时间: 2005-09-23 20:15
标题: comp.unix.shell FAQ(转载http://home.comcast.net/~j.p.h/)
应该汉化一下啊。。
作者: tonyfu    时间: 2005-09-26 17:00
标题: comp.unix.shell FAQ(转载http://home.comcast.net/~j.p.h/)
英语好的翻译一下,,看着晕哦,,,水平太差,,,,
作者: 寂寞烈火    时间: 2005-12-02 19:21
看了约30%,受益匪浅呀!!!
作者: r2007    时间: 2005-12-02 23:11
哈,开始啃E文了
作者: 寂寞烈火    时间: 2005-12-03 00:56
原帖由 r2007 于 2005-12-2 23:11 发表
哈,开始啃E文了

大多数看不懂,呵呵,手头多了本英汉辞典
作者: 231053469    时间: 2006-01-05 14:23
。。。

[ 本帖最后由 231053469 于 2006-1-26 11:24 编辑 ]
作者: 231053469    时间: 2006-01-05 14:25
顶!!!!
作者: 231053469    时间: 2006-01-05 14:26
。。。。

[ 本帖最后由 231053469 于 2006-1-26 11:23 编辑 ]
作者: 寂寞烈火    时间: 2006-01-08 13:25
标题: 这个连接打不开了 :-(
http://www.shelldorado.com/
无法连接到远程服务器

您试图访问的地址 http://www.shelldorado.com/ 目前不可用。请确保网址(URL)拼写和标点符号使用都正确,然后试着重新装入页面。

作者: 231053469    时间: 2006-01-11 16:21
支持
作者: johnsilver    时间: 2006-01-31 22:18
提示: 作者被禁止或删除 内容自动屏蔽
作者: lrz    时间: 2006-02-13 21:58
原帖由 johnsilver 于 2006-1-31 22:18 发表
这的确是个好东西,有没有人已经译过了,贴出来共享一下,不然我就译了。

热切期待中...........
作者: kindofblue    时间: 2006-04-03 17:34
太好了 用来学英语太好了 吼吼   如果又学英语又学shell  那就太棒啦 嘎嘎嘎……
作者: ceelie    时间: 2006-04-04 15:07
谁要是译出来了,贴出来告诉大家就好了!不知道有多少谢谢等着呢……
作者: liming_    时间: 2006-05-12 12:10
up~~~~~~~~~
作者: china_ymh    时间: 2006-07-19 10:26
希望能早日看到这篇汉化的文档!
作者: mcumsigscr    时间: 2007-01-24 13:34
英文啊.和楼上的感觉一样.
作者: 大蚂蚁    时间: 2007-01-27 06:15
很早以前翻译了一部分,后来一直没时间继续,以后三个月内也不会继续,有兴趣的可以继续翻译,本人语言功底有限,错误地方还请指正
Comp.unix.shell FAQ

来自: j.p.h@comcast.net (Joe Halpin)
新闻组:comp.unix.shell
题目:comp.unix.shell FAQ-常见问题解答
摘要:这份文档记录了一些在comp.unix.shell中经常被问到的问题的答案,你若想在讨论组内提问,你最好先阅读一下这份文档,看能否找到你要的答案。
Followup-To: comp.unix.shell

文档名字:unix-faq/shell/sh
提问频率:每月
版本: $Id: cus-faq.html,v 1.11 2005/09/01 17:39:36 jhalpin Exp $
维护人员:Joe Halpin

这份FAQ给出了一些在comp.unix.shell常常被提到的问题的答案,之所以用小写的unix是为了避免关于是否是Linux或者BSD的讨论,我们都认为是unix,这个不是这份文档的重点,我们忽略这个问题。

This document as a whole is Copyright (c) 2003 Joe Halpin. It may be copied freely. Exceptions are noted in individual answers.

Suggestions, complaints, et al, should be sent to the maintainer at j.p.h@comcast.net or posted to comp.unix.shell

关于shell的讨论有两个级别。

一个级别是shell作为OS的和用户之间的接口的自身使用的问题。例如:“我怎么在后台运行一个程序而继续做其他的事情?”或者“当我登陆的时候,我怎么设置自己的环境变量?”

另外一个级别是怎么去写shell脚本。包含使用标准unix程序编写shell脚本去完成一部分常见的工作,这将需要一些关于shell脚本编写的知识。然而,只要问题不是超出标准unix应用范畴的,应该都可以在这份FAQ中找到。

标准的unix应用程序被定义为:POSIX 或者 Single Unix Specification中的一种规格,他们现在被合称为"POSIX/SUS".详细描述可以在下面的网址中找到
http://www.opengroup.org/onlinepubs/007904975/toc.htm

可以在网上找到所有应用程序的man文档(包含shell自己的)。但是,你仍需要在你的系统上核对一下你将要使用的应用程序或者shell的man文档。目前,在标准和具体应用之间并没有进行完美的交流(事实上,我不清楚他们之间的任何协调和交流)。


这里还有另外一个FAQ,描述了标准化的成果的更多细节。
http://www.opengroup.org/austin/faq.html

这里还有一些其他不错的网站提供了shell和shell编程(包含UNIX应用程序)的信息。

包含:
http://www.shelldorado.com/
http://cfaj.freeshell.org/shell/
http://www.faqs.org/faqs/by-newsgroup/comp/comp.unix.shell.html

有可能用到的FAQ:
http://home.comcast.net/~j.p.h/
http://www.newsville.com/cgi-bin ... tly_Asked_Questions

---------------
免责声明
---------------
这份FAQ中给出的答案提供了最好的意指导,但是,它并不一定能被正确的应用于特殊的系统或者shell。对某些系统或者shell可能是完全错误的。你若不测试结果的话,它将会是你的程序的一个bug。

这份文档没有声明,也不保证所有答案的正确性。事实上,很多甚至没有经过测试。答案来源于一个或者多个新闻组中的正是成员,我相信他们是有能力的(当然,比我更有能力)但仍不保证答案的正确性。

难道我真的需要全部大写?希望不,但是周围有很多无聊的律师,所以,我想清楚的表达一下,在这份文档的精确性上:THERE ARE NO GUARANTEES。希望这份文档对学习shell编程的人有用,但它并不是一份supported product。你必须自己考虑这里的答案对你的所将要的工作是否有。

……
以下省去了,没有翻译,自己看算了 ^_^
Under no circumstances will the maintainer of this FAQ list, or any contributors to it, be held liable for any mistakes in this
document. If the answers work for you, well and good. If not, please tell me and I'll modify them appropriately so that this will be more useful.

If you don't agree to that, don't read any farther than this. Reading beyond this point indicates your agreement.

If you do test the answers and find a problem, please send email to the maintainer (see above), so it can be corrected, or (preferably) post a question to the newsgroup so it can be discussed and corrected if there's a problem.

A number of people have contributed to this FAQ, knowingly or unknowingly. Some of the answers were taken from previous postings in the group, and other people contributed questions and answers directly to the maintainer, which you are welcome to do as well.

Among the contributors is Heiner Steven, who also provided the momentum to get this FAQ list started. He maintains a web site about shell programming that has a lot of good stuff in it.

http://www.shelldorado.com/

目录:
0 拷贝
0a 术语表
   Google
    POSIX/SUS ("the standard")
    UUOC
    dotfile
    portable
    race condition
    shebang
    shells
    top-posting

0b 使用echo的注意事项
1 怎么发送带附件的email
2 怎么在shell脚本中产生随机数
3 怎么使用带错误校验的ftp自动传输文件
4 怎么删除文件名中的空格字符
5 怎么使用telnet自动登录
6 计算日期的算法
7 为什么有人告诉我 RTFM
8 怎么创建一个锁文件
9 怎么将DOS文件转换为unix文件,反之亦然
10 怎么设置shell prompt而改编xterm的标题
11 在命令 cmd1|cmd2 中,我怎么得到cmd1的退出装体.
12 为什么会提示 “script.sh:not found”
13 为什么echo不能输出我想得到的东西.
14 文件名中带有空格,我怎么在循环中使用它们.
15 怎么改变默认的登录shell
16 什么时候我应该使用shell代替perl/python/ruby/tcl...
17 为什么不要使用c shell
18 怎么颠倒(翻转)一个文件
19 怎么删除最后几行
20 怎么得到文件的大小和修改日期.
21 给定一个进程名,我怎么得到进程ID?或者给定一个进程ID,我怎么确认这个进程仍然在运行?
22 怎么使用脚本改变当前的环境变量?
23 怎么把*.foo重命名为*.bar?
24 在awk脚本中怎么调用shell量?
25 怎么获得一个有时间限制的用户输入?
26 怎么从用户的输入中得到一个字符?
27 为什么我的.profile文件没有被读到?
28 为什么提示: "[5" not found in "[$1 -eq 2]"?
29 怎么正确显示变量 $var(带有回车符)的内容?
30 怎么把一个路径名分割为目录名和文件名?
31 带参数的alias
32 怎么处理以特殊字符开始的文件名?
33 为什么得不到循环中全局变量的值?
34 怎么使用ftp批量下载和上传?

附录A 一些实例脚本
附录B 参考书目.相应的用方括号括起来的数字将会出现在文章中,例如[1]


译者注:版权说明,翻译省去,自己注意
ANSWERS

0  COPYING

   Some contributors may copyright their submissions and license them
   differently than this document.

   [1] Chris F.A. Johnson. Examples marked with COPYING[1] were
       contributed by Chris F.A. Johnson. He has copyrighted these
       examples, and licensed them under the GNU General Public
       License (GPL). Copying them directly into another script will
       cause that script to also come under the GPL. For details see

       http://www.fsf.org/licenses/licenses.html

0a 术语表
Google
        它是互联网上的一种搜索工具,它能搜索到数年前的信息,现在作为一个标准的参考指导人们去搜索已经讨论过的话题,这对搜索shell编程(以及其他任何东西)的相关问题来说,是一个非常好的起点。
        http://groups.google.com/advanced_group_search
-------------------------------
POSIX/SUS ("the standard")
        POSIX (Portable Operating System Interface)和SUS (Single Unix
      Specification)已经结合成为一个标准。这就是平时人们讨论unix的时候经常涉及到的“标准”。当在讨论组中涉及到POSIX shell的时候,他们讨论的就是这个标准,你可以在以下网址中找到此标准的详细描述
           http://www.opengroup.org/onlinepubs/007904975/toc.htm
-------------------------------
UUOC
        这个缩写的全拼是"Useless use of cat"(没有使用cat的必要).它经常指本来可以使用重定向来代替cat的shell脚本。使用重定向要比使用cat更加有效率,因为cat是调用外部程序从而产生了一个新的进程。例如:
        $ cat file | tr -d 'xyz'
这个命令产生了两个进程,一个是cat,一个是tr。下面的语句将更有效率
         $ tr -d 'xyz' < file
一般情况下,"cat file | somecommand"可以被更有效率的命令代替为"somecommand < file"或者多文件输入
        $ somecommand file [file ...]
但是,最好阅读一下有关命令“somecommand”的man文档,以确定该命令是否支持该语法。
dotfile 点文件
        这是指文件名以“.”开头的文件,你若不使用ls命令到的-a选项(ls的新版本可能是-A选项,检查一下你的man 文档)的话,将不会显示出来。一般情况下,它们都是配置文件,被应用程序来存储配置文件的子目录,NFS swap 文件等等。
Portable
        The word "portable" means different things to different people,  in different situations, which is to say, there isn't one   definition of "portable".
        “portable”是指在不同的事物,不同的人会有不同的办法。或者是说,对于“portable”不只一种定义方法。
        一个极端,a portable script 可以在任何shell,任何操作系统上完美的运行。但是,这种完美的脚本并不存在(因为有些操作系统甚至可能没有shell)。如果我们把操作系统的范围限定到unix(我们一开始就应该知道这是comp.unix.shell),唯一真正的portable scripts是那些不调用shell的内置命令和语法,完全调用外部命令,例如:
        echo Hello World
也许合格,然而,但是并不是任何一个命令都可以做到更好。
        相对与标准,几乎没有probably的脚本存在,“portable”更多的时候是指脚本能在不同的shell,不同的系统环境下的,脚本的可移植运行的程度。
        例如,如果你要为一个应用程序写一个安装脚本,程序的运行平台是已经被指定了的,这时问题已经被完美的限定在一定范围内。Shell可以选择在限定的系统上都已经安装存在的shell,语法的使用选择在不同的目标平台上的shell的语法的最小公共子集。
        脚本的portable程度取决于你,或者你对指定的shell脚本的需求。
-------------------------------
   race condition(资源竞争)
这是指两个实体(进程,线程等)试图存取一个共享资源,或者完成同一件事。它的结果取决于两个实体的执行顺序。

-------------------------------
shebang (沙邦,或者翻译为shell头吧 ――!)
        这是指shell脚本的第一行,它告诉操作系统应该调用哪个解释器(shell)
        #!/path/to/shell [ argument ]
        /path/to/shell可能是/bin/sh, /usr/local/bin/bash等
       
        这一行仅被操作系统解释,如果一个脚本(test.sh)在命令行下被作为一个参数来执行,例如:
        $ sh test.sh
        这时sh将解释test.sh,对于sh来说shebang只是一个注释,执行的时候将忽略它。


  shells
讨论组中讨论的很多shell类型中包含:
   sh
   csh
   pdksh
   ksh88
   ksh93
   tcsh
   zsh
   rc
   es
   bash
   ash
   dash
        这些(有更多)shell的名字在讨论组中将被做为参考。一些shell的比较可以在下面的地址中看到。
         http://www.faqs.org/faqs/unix-faq/shell/shell-differences/
但是,它并没有比较ksh88, ksh93 和 pdksh的差别,它们并不是完全兼容的。
―――――――――――――――――――
        top-posting

   A. top posting
   Q. What's the most irritating way to respond on usenet?
   Please see the following:
      http://catb.org/~esr/jargon/html/T/top-post.html
      http://www.uwasa.fi/~ts/http/quote.html
      http://members.fortunecity.com/nnqweb/
      http://www.guckes.net/mail/editing.html
      

0b  使用echo的注意事项。
        这不是一个真正的FAQ,但是经常会遇到有关echo的讨论,所以它出现在这份FAQ中是有原因的。
        在不同的环境下,echo命令对它的参数的解释并不是一致的。被引号引起来的带反斜线的字符串有时候被解释为一种意思,有时候又被解释为另外一种意思。
        同样,若被echo输出的字符串并不在脚本中,那么它可能含有shell的meta字符,它同样可以产生影响。事实上,传给echo的外部输入字符串应该被引号引起来。
        例如:
        s="a string with\na newline and\ta tab"
下面是不同shell的输出结果:
        ------
   bash:
   $ echo "$s"
   a string with\na newline and\ta tab
   
   $ echo -e "$s"
   a string with
   a newline and        a tab

   -------
   pdksh:
   $ echo "$s"
   a string with
   a newline and        a tab

   $ echo -e "$s"
   $ echo "$s"
   a string with
   a newline and        a tab

   --------
   ksh88:
   $ echo "$s"
   a string with
   a newline and        a tab

   $ echo -e "$s"
   -e a string with
   a newline and        a tab

   -------
   ksh93:
   $ echo "$s"
   a string with\na newline and\ta tab

   $ echo -e "$s"
   -e a string with\na newline and\ta tab

   注意,ksh93认为参数处理有一下两种情况决定,包含’\’或者第一个传入参数以-开头。
         http://www.cs.princeton.edu/~jlk/kornshell/doc/man93.html
        POSIX 不允许-e选项。它也对-n选项和带’\’的执行结果给了定义。然而,在XSI-conforming系统上,它不接受选项,定义了使用反斜线引用的字符。
        通常,echo的行为是系统或者shell(或者系统与shell的共同结果)取决于它的参数是否包含反斜线,或者第一个参数是否为-n或者-e
        使用echo的最大问题是使用它输出脚本直接获取的输入(或者用户输入,或者从文件中读取)。这些字符串可能包含’\’字符,若是这样的话,结果并不是你想要的。
        Print在许多shell中是一个可以用到的命令,或许printf会更加的portable。另外,here文档将会给出我们一个可以预知的输出结果,它并不展开逃脱字符(反斜线)
        cat <<EOF
           $s
         EOF
        产生
        a string with\na newline and\ta tab
        所以最好不要使用echo,除非你可以确定它将在你的shell中产生什么样的结果。

1,        怎么发送一个带附件的邮件。
a)        使用uuencode
这是一个最简单的方法例如
$ uuencode surfing.jpeg surfing.jpeg | mail someone@some.where
可以很好的发送文档。
$ (cat mailtext; uuencode surfing.jpeg surfing.jpeg) |
        mail someone@some.where
b)        使用MIME
$ metasend -b -t someone@some.where -s "Hear our son!" \
        -m audio/basic -f crying.au
从下面的连接中你可以得到更多的合适的例子
        Http://www.shelldorado.com/articles/mailattachments.html
c)        Use pine (with a patch) or mutt   #  or mutt不知道怎么翻译。

2,        怎么在shell脚本中产生随机数?
这个取决于shell和OS可用的工具。
a.一些shell有一个变量为 RANDOM,每次调用它的时候将产生一个不同的值,若你的shell有这个变量:
     $ number=$RANDOM 将会产生随机数字。
b.有些系统有一个/dev/urandom设备,它产生一个bits流。可以被dd(1)存取。一个例子(from a more extensive discussion of different techniques at http://www.shelldorado.com/scripts/cmds/rand)
         n=`dd if=/dev/urandom bs=1 count=4 2>/dev/null | od -t u4 | \
        awk 'NR==1 {print $2}'`
同样
        od -vAn -N4 -tu4 < /dev/urandom
c.使用外部程序,如awk(1),它可以产生随机数,,在不同的shell和不同的操作系统之间,这是一个非常portable的方法。
        awk 'BEGIN {srand();print rand()}'
注意,旧版本awk并不支持这种用法,这需要支持POSIX srand()的版本。例如,在solaris上,/usr/bin/awk不工作,但是/usr/xpg4/bin/awk却能达到效果。
   同样,你在同一时间内调用此命令的次数多于一次,你将得到和上一次同样的结果。
       
3.怎么使用带错误校验的ftp自动传输文件
        首先,这里有工具可以做到:curl, wget, lftp, ncftp。但是,通常情况下系统并不自带这些程序,你需要安装它们。
        zsh (version 4 and above)提供了一个ftp工具。
查看 "info –f zsh -n 'zsh/zftp Module'"
#! /usr/bin/zsh
             zftp open host user passwd || exit
            zftp get /remote/file > /local/file; r=$?
            zftp close && exit r
使用系统的ftp命令,两种方法。
        1 使用-n选项。不用-n选项的话,系统将等待用户输入password,所以你将不得不使用“expect”,使用-n,你像使用其他ftp命令一样提供用户名和密码。
        #! /bin/sh
    ftp -n << EOF
    open ftp.domain.org
    user anonymous ${LOGNAME:-`who am i`}@
    binary
    get /remote/file /local/file
    bye
    EOF
错误检测将不存在(若open失败,后面的发送语句将同样执行)
        2 使用~/.netrc
        若你使用
        <<
    machine ftp.domain.org
    login mylogin
    password mypasswd
    macdef init
      binary
      get /remote/file /local/file
      bye


    >>
(最后几行空行)在你的~/.netrc(确保它不是所有人可读的),执行"ftp ftp.domain.org",ftp将会找到匹配的“机器”在你的~/.netrc中,使用可用的参数进行ftp传输。
        至少在Linux, FreeBSD, Solaris, HPUX上可以工作。

4 怎么删除文件名中的空格字符
        Unix下的文件名可以包含所有的不可视字符,不只是空格.但下面的只举例一些只对空格有用的例子.
        a.利用awk,sed等工具中的替换功能.
f=`printf '%s\n' "$filename" | sed 's/ /_/g'`
        f=`printf '%s\n' "$filename" | awk '{gsub(" ","_");print $0}'`
     f=`printf '%s\n' "$filename" | tr ' ' _`       
        在tr命令行中添加必要的字符(请参阅tr的man文档,找出逃脱符的顺序)
另外(准确的说,并不一定是一行),例如:
f=`tr ' ' _ <<EOF
        $filename
        EOF
        参考章节0a “使用echo的注意事项” 了解为什么不在这里使用echo
      b.使用shell的substitution功能(若你的shell有此功能的话),检查你的shell的man文档(极有可能在像这样的章节"Parameter expansion"下面).例如:
                f=${filename// /_}
使用zsh
        autoload -U zmv
      zmv '* *' '$f:gs/ /_/'
应该注意zmv的置换功能重命名了文件,而其他的置换功能仅仅是更新了变量.
=============================================
5 怎么使用telnet自动登录
         这个已经超出了shell编程的范畴,最好使用专门的编程语言像expect之类的来完成它,参考http://expect.nist.gov/
        使用perl的CPAN的telnet模块一样完成
=============================================
6 计算日期的算法
       
7 为什么有人告诉我 RTFM
8 怎么创建一个锁文件
9 怎么将DOS文件转换为unix文件,反之亦然
10 怎么设置shell prompt而改编xterm的标题
11 在命令 cmd1|cmd2 中,我怎么得到cmd1的退出装体.
12 为什么会提示 “script.sh:not found”
13 为什么echo不能输出我想得到的东西.
14 文件名中带有空格,我怎么在循环中使用它们.
15 怎么改变默认的登录shell
16 什么时候我应该使用shell代替perl/python/ruby/tcl...
17 为什么不要使用c shell
18 怎么颠倒(翻转)一个文件

[ 本帖最后由 大蚂蚁 于 2007-1-27 06:17 编辑 ]

translate.rar

18.7 KB, 下载次数: 256


作者: xnkjdx1998    时间: 2007-03-15 17:21
第11个问题中的b选项似乎有点问题,PIPESTATUS好象不是数组,只能取第一个命令的返回值
作者: dadupi    时间: 2007-05-11 14:23
真是难得得好贴啊,受益中
作者: waker    时间: 2007-05-12 22:02
原帖由 xnkjdx1998 于 2007-3-15 17:21 发表
第11个问题中的b选项似乎有点问题,PIPESTATUS好象不是数组,只能取第一个命令的返回值

你的bash版本低
作者: fuqiyuan    时间: 2007-07-20 18:51
标题:
怎么都是英文的
作者: ReknihtPeed1    时间: 2007-08-15 22:06
vbhgchjngvnfg
作者: 大蚂蚁    时间: 2007-12-13 05:41
又翻译了一小段,看不懂我翻译的就去看英文原著

6.怎么计算日期:
这主要取决于你大脑里的想法
a.怎么获取昨天的日期
        GNU版本的date有很多好用的人性化的新特性,例如
        获取昨天的日期:
                $ date --date yesterday
        获取明天的日期:
                $ date --date tomorrow
GNU版本的date的man文档中,里面有更多的其他选项,可以得到过去的,或者将来的多余一天的日期.
FreeBSD版本的date同样也有很多扩展选项,可以像下面的方法使用.
                         $ date
                         Wed Oct 22 13:48:29 CDT 2003
                                  $ date -v-1d
                          Tue Oct 21 13:45:16 CDT 2003
任意调整TZ变量(这是个时区变量)并不是一个可靠的办法.如果你的确没有GNU版本或者FreeBSD版本的date,确实需要这么做,那么请参考g章节“任意日期算法”

b.关于文件的日期(新旧)#Finding elapsed time
        若你需要测试一个文件的日期是否比另外一个文件的日期早,你可以这么做(在 bash, pdksh, ksh93环境下)
                 $ [[ file1 -ot file2 ]] && echo file1 is older
        或者你也可以使用find命令,查找一个目录下比目标文件旧或者早的文件:
                  $ find . -name '*.c' -newer test.c
c.获取已经用掉的时间 #Finding elapsed time
        如果你想获取已经用掉的时间,也许是因为你想知道某些操作什么时候超时了,一些shell(bash,ksh,zsh)有一个 SECONDS变量,可以告诉你从shell的某个触发条件开始到现在(since the invocation of the shell)一共多少秒,或者从SECONDS变量最后一次被重置的时间到现在一共多少秒.
        ksh93有一个取决于locale的浮动点seconds .
        在zsh4.1或者以上的版本可以定义浮动点:float SECONDS
        在zsh4.1或者以上的版本中同样有一个$EPOCHSECONDS变量,保存从1970-1-1 0:0:0 UTC到现在的秒数(see zsh/datetime module)
       
d.计算闰年
        在公历中,闰年被定义为:可以被4除尽的年份大部分是闰年,能被100整除而不能被400整除的年份不是闰年,这听起来似乎比较麻烦,实际上,我不会有那么大的岁数 .

        在很久以前,欧洲采用的大都为儒略历,只有被4整除的年份才是闰年.
        为了方便和应该的日期相协调,标准的cal程序可以在儒略历和公历从1752年9月之间切换(see cal 9 1752).公历(由罗马教皇格里高利十三世于1582年颁布施行)从1582开始被很多国家使用.
        Ksh可以完成的一些功能(after 1600 AD/CE#注:这里不知道怎么翻译)有:

        isleap()
        {
          y=$1
          four=$(( $y % 4 ))
          hundred=$(( $y % 100 ))
          fourhundred=$(( $y % 400 ))
          if [ $four -eq 0 ];then
            if [ $hundred -eq 0 ];then
              if [ $fourhundred -eq 0 ];then
                echo leap year
              else
                echo not a leap year
              fi
            else
              echo leap year
           fi
         else
           echo not a leap year
         fi
        }

Or, valid with any date with the same calendar switch day as   POSIX cal's (POSIX syntax):

        is_leap_year() # args: year
        # NB: year before year 1 is year -1, not 0.
        {
          [ "$1" -lt 0 ] && set -- "$(($1 + 1))"
            [ "$(($1 % 4))" -eq 0 ] && {
                [ "$(($1 % 100))" -ne 0 ] || [ "$(($1 % 400))" -eq 0 ] \
                      || [ "$1" -le 1752 ]
            }
        }

或者在一些 Bourne shell 中(see COPYING[1]):       
        is_leap_year() { ## USAGE: is_leap_year [year]
          isl_year=${1:-`date +%Y`}
          case $isl_year in
            *0[48] |\
            *[2468][048] |\
            *[13579][26] |\
            *[13579][26]0|\
            *[2468][048]00 |\
            *[13579][26]00 ) _IS_LEAP_YEAR=1
               return 0 ;;
            *) _IS_LEAP_YEAR=0
               return 1 ;;
          esac
        }
在FreeBSD系统中,date使用-f选项来绕过当前年份(假定)中的2月29日,然后再次打印月中的所有天数来确认是否真的有这一天(注意,你同样需要使用-j参数,负责系统会认为你想要重新设置时间):
      if [ $(date -jf%Y%m%d $(date +%Y0229) +%d) = 29 ]; then
        echo Leap year!
      fi
e.计算一个月中的最后一天.
        这里有N种方法可以实现这个要求,下面是讨论组中的一个取样.
        在任何的Bourne-type shell(调用了上面的is_leap_year() 函数,当月份是2的时候)(see COPYING[1]):
       days_in_month() { ## USAGE: days_in_month [month [year]]
          if [ -n "$1" ]
          then
            dim_m=$1
            dim_y=$2
          else
            eval `date "+dim_m=%m dim_y=%Y"`
          fi
          case $dim_m in
            9|09|4|04|6|06|11) _DAYS_IN_MONTH=30 ;;
            1|01|3|03|5|05|7|07|8|08|10|12) _DAYS_IN_MONTH=31 ;;
            2|02) is_leap_year ${dim_y:-`date +%Y`} &&
              _DAYS_IN_MONTH=29 || _DAYS_IN_MONTH=28 ;;
          esac
          [ ${SILENT_FUNCS:-0} -eq 1 ] || echo $_DAYS_IN_MONTH
        }

用GNU date

        year=2003
        month=9
        date -d "$year/$month/1 +1 month -1 day" +%d

在FreeBSD系统中,使用date的-v-1d 选项可以取得下个月的前一天:

        $ MONTH=12
        $ date -v-1d -jf%Y-%m-%d $(date +%Y-$(((MONTH+1)%12))-01) +%d
        31

在shell中使用cal(但是小心执行cal会一次输出多于一个月)
      month=9 ; year=2003   # adjust
        ##
        for lday in `cal $month $year` ; do : ; done
        echo $lday

        ## or
        set -- `cal $month $year` ; eval lday=\${$#}
        echo $lday
        
      In ksh, bash and zsh:

        : $(cal)
        days_in_month=$_

      In zsh:

        days_in_month=${$(cal)[-1]}

f.给出任意一个日期,计算星期几

        Zeller's Rule是一个大家都熟悉的算法,在数据结构与算法中,我们可以找到它的详细解释.相关网页:
        NIST:
        http://www.nist.gov/dads/

      Also, a fuller explanation is available at
        http://www.merlyn.demon.co.uk/zeller-c.htm#ZC

      An example in C, with a short explanation, is given at
        http://wwwcdf.pd.infn.it/MLO/Calendars/Notes.html#zeller
#译者添加 Zeller's Rule: f = k + [(13*m-1)/5] + D + [D/4] + [C/4] - 2*C.   #[]是取整的意思,例如[3.79]=3.k日、m月(三月为首1, 二月为后12)、D年的后2位(一月、二月时要减1)、C百年.

Ksh(ksh93)中:
        dayofweek()
        {
          # Implementation of a homework assignment given at
          # http://carbon.cudenver.edu/~traup/fa02/lec/hw3.html
          #
          # call with day:   1 - 31
          #           month: March = 1, Jan & Feb are months 11 and
          #                  12 of the previous year.
          #           year:  The year of the century
          #           c:     The previous century
          #
          # For example, for July 4, 1989,
          #   m = 5,  d = 4,  y = 89, and c = 19,
          # while for January 25, 1989,
          #   m = 11, d = 25, y = 88, and c = 19.
          #
          # The output is the day of the week with Sunday = 0,
          # Monday = 1, etc.

          d=$1
          m=$2
          y=$3
          c=$4

          A=$(( ($m * 13 - 1) / 5 ))
          B=$(( $y / 4 ))
          C=$(( $c / 4 ))
          D=$(( $A + $B + $C + $d + $y - ($c * 2) ))
          echo $(( $D % 7 ))
        }
在FreeBSD系统中,使用date的-f选项来跳过date中的有趣的天数,+%A 来打印星期几.

g.任意日期计算

        任意日期计算是十分复杂的.一种方法就是调用外部程序.或者用另外一种内置语言编程实现.例如:perl有封装了UNIX时间相关的函数,可以提供更多的方便的功能调用,C语言也可以轻松的实现(see the examples  section). 要记住一点,严格的来说,unix系统里面的时间是有一个范围限制的.从1970年1月1日0:00:00开始到2038年1月19日3:14:07.c程序和perl程序,在这个范围外可能正常工作,也可能不正常工作,这取决于你的程序.
        利用shell自身计算任意日期也不是不可能的,SysAdmin杂志上的一篇文章描述了一种计算方法:
        http://www.samag.com/documents/s=8284/sam0307b/0307b.htm
        在这个地方给了另外一种算法
        http://groups.google.com/groups? ... f%40ogion.it.jyu.fi
在FreeBSD中,date的-f和-v选项覆盖了大部分你想做的东西,但只能计算上面提及,在这个范围内的日期.范围外的日期将不能工作.
        同样,zsh4.1以上的版本有zsh/datetime 模块,提供了变量$EPOCHSECONDS 和strftime函数.

h.获取从新纪元(1970年1月1日午夜)开始的总秒数
        -GNU date 有s%选项可以返回新纪元时间 (epoch time)

        -更好的方法,使用awk.
        awk 'BEGIN {srand(); printf("%d\n", srand())}'
        因为当没有参数输入的时候,srand()默认采用当前的新纪元时间(epoch time)为种子,它同样也返回它的上一个种子的值,所以第二个调用返回了新纪元时间(epoch time).

        注意:旧版本的awk不支持,这需要支持POSIX标准的srand(),例如,在solaris中/usr/bin/awk不支持,但是nawk 或者 /usr/xpg4/bin/awk则支持.

        取决于顺序,若已经被调用过一次了,或者其它情况,则本次调用可能不是第二次调用.
       
        -另外一个方法,使用perl,若你的系统上安装的有的话.
                perl -le 'print time'
        -同样,zsh4.1或者以上的版本具有 zsh/datetime 提供$EPOCHSECONDS变量和strftime函数


7.为什么有人告诉我RTFM?
        因为你没有
        RTFM 是一个网络行话,全称是 "Read The F-ing Manual".
        当有人问一个过去经常被问的问题的时候,并且man文档中有很清晰的答案,回答者已经疲惫于看到有人再次问同样的问题.
        http://catb.org/~esr/jargon/html/R/RTFM.html
        所以, 在你问问题之前,请RTFM(阅读一下man文档或者说man一下),Also, if you're new to the group, search Google Groups
        http://groups.google.com/advanced_group_search
问问题之前,不要直接把你的作业贴上去除非你尝试了很久没有成功完成,并问一些经过思考过的问题,当你认真的思考过一些问题之后,人们大都乐于和你一起讨论你的问题,而不是直接贴个问题问别人要答案


8.怎么创建一个锁文件.
#译者注: <操作系统>课程中应该讲过,原子操作就是可以再细分的操作,而非原子操作就是不能在细分的操作,课程学的早,不太记得了.

        千万要谨慎
        在一个非原子操作中,调度程序可以停止一个进程,并执行另外一个可能具有同样功能或操作的进程.第二个进程,拥有完整的时间片,可能会完成操作,当控制权回到第一个进程中的时候,可能导致混乱.
        避免这个问题的窍门就是做一些原子操作.有两种方法可以实现.一个是创建一个目录代替文件,另一个是创建一个符号链接.两个操作都被POSIX/SUS定义为原子的,事实上,他们的优点是都需要进行相应的系统调用,并且都是原子操作.
        当在NFS分区上创建任何锁文件的时候都要小心,#不太会翻译这两句 NFS pretty much eliminates anything like atomicity(NFS分区对原子操作有更多的排斥作用? .若你想要创建一个锁文件,确保是在你的本地分区上操作,例如/tmp.
        Netscape/Mozilla 使用链接符号的方法创建锁文件(事实上它在用户的家目录创建了它,尽管用户的家目录可能挂在NFS分区上),当它启动的时候它就创建了一个以及其正在运行的IP地址为名字的文件,然后它在试图创建一个名为"lock"并之前那个文件的链接符号,若这个链接已经存在,则link命令会抱错,在脚本中类似下面:
  touch /tmp/xxx
   ln -s /tmp/xxx /tmp/lockfile 2>/dev/null
   ret=$?
   rm /tmp/xxx
   if [ $ret -ne 0 ];then
     echo lockfile already exists
     exit 1
   else
     echo success
   fi

若你安装了procmail程序,另一个命令lockfile 也会一起被安装的.

9.怎么将DOS文件转换为unix文件,反之亦然
        UNIX文本文件以LF ("line-feed" (ASCII 10)字符结尾,而DOS使用两个字符CR LF ("carriage return", "line feed"; ASCII 13, 10).
        把DOS文件转换为UNIX文件,删除行末尾的CR字符(control-M) 即可,把UNIX文件转换为DOS文件,则在行末尾添加CR字符.
        两个删除CR字符的方法:
      sed 's/^M$//' dos.txt > unix.txt
      tr -d '\r' < dosfile > unixfile
       
        添加方法:
      sed 's/$/^M/' unix.txt > dos.txt
        注意 "^M"在这里是个内部控制字符,(CR,ASCII 13),许多shell允许输入内部控制字符,但要先输入^V 字符 (control-V), 结果如下:
        用 ^V^M  # 输入"^M".
尽管如此,zsh,bash或者ksh93允许以下操作:
        sed $'s/$/\r/'
        这里有个特殊的地方需要考虑OS下的文本文件有时候在文件结尾会包含一个特殊字符^Z (ASCII 26,or octal 32), UNIX文本文件下没有这个字符,文件末尾的字符在unix下是不可见的,同样可以用删除CR字符的方法删除它:
        tr -d '\r\032' < dosfile > unixfile
        注意: sed不能解释这个字符,但awk可以,一个简单的方法做相反的转换:
       
        $ awk '{printf "%s\r\n" $0}END{printf "%c", 26}' unixfile > dosfile
       
        假定我们使用的不是旧版本的awk,在Solaris下不要使用/bin/awk (使用 nawk or /usr/xpg4/bin/awk in Solaris).

        最后,你的系统可能安装了一些名为dos2unix和unix2dos的命令,或者d2u dos2unix 从dos 非标准程序,或者GNU recode:recode /CRLF

10.怎么设置shell prompt而改编xterm的标题
        #不知道怎么翻译这一句"Gives escape sequences for xterm,"例如,把目前窗口的名字改为"XXX" (in bash):
      $ echo -en "\033]2;XXX\007"

      或者,更恰当点的:

      $ printf '%b' '\e]2;XXX\a'

参考"为什么echo输出的结果不是我想要的?"

作者: 大蚂蚁    时间: 2007-12-13 06:02
还有一点,下次什么时候心情好再继续忽悠,闪人......
11.在命令 cmd1|cmd2 中,我怎么得到cmd1的退出装态
        首先,注意cmd1退出代码可能为0但并不代表错误,这个发生在下面的情况:
        cmd | head -1
        你可能得到cmd的退出状态是141(ksh为269)退出状态,那时因为cmd被SIGPIPE信号中断了当"head -1"在读进一行后退出.
        想要了解cmd1 | cmd2 | cmd3 管道的退出状态的原理
a.用zsh:
        退出状态保存在管道特殊数组中,cmd1的退出状态保存在$pipestatus[1], cmd2的退出状态保存在$pipestatus[2], cmd3的退出状态保

存在$pipestatus[3], 所以$? 总是等于$pipestatus[-1].

b.用bash
        退出状态保存在PIPESTATUS特殊数组中,cmd1的退出状态保存在${PIPESTATUS[0]}, cmd3的退出状态保存年在${PIPESTATUS[2]}, 所

以$?总是等于 ${PIPESTATUS: -1}.

c. 使用其他的任意b shell
        你需要使用一些敲门来绕过主函数的退出代码,你可以使用管道.代替使用"cmd1"的方法,可以运行cmd1; echo $?".
      
        exec 3>&1
       eval `
         # now, inside the `...`, fd4 goes to the pipe
         # whose other end is read and passed to eval;
         # fd1 is the normal standard output preserved
         # the line before with exec 3>&1
         exec 4>&1 >&3 3>&-
         {
           cmd1 4>&-; echo "ec1=$?;" >&4
         } | {
           cmd2 4>&-; echo "ec2=$?;" >&4
         } | cmd3
         echo "ec3=$?;" >&4
       `
d.在POSIX shell下
        我们可以很轻松的实现:
       run() {
         j=1
         while eval "\${pipestatus_$j+:} false"; do
           unset pipestatus_$j
           j=$(($j+1))
         done
         j=1 com= k=1 l=
         for a; do
           if [ "x$a" = 'x|' ]; then
             com="$com { $l "'3>&-
                         echo "pipestatus_'$j'=$?" >&3
                       } 4>&- |'
             j=$(($j+1)) l=
           else
             l="$l \"\$$k\""
           fi
           k=$(($k+1))
         done
         com="$com $l"' 3>&- >&4 4>&-
                    echo "pipestatus_'$j'=$?"'
         exec 4>&1
         eval "$(exec 3>&1; eval "$com")"
         exec 4>&-
         j=1
         while eval "\${pipestatus_$j+:} false"; do
           eval "[ \$pipestatus_$j -eq 0 ]" || return 1
           j=$(($j+1))
         done
         return 0
       }
      
       use it as:
      
       run cmd1 \| cmd2 \| cmd3
       exit codes are in $pipestatus_1, $pipestatus_2, $pipestatus_3


12 为什么会提示 “script.sh:not found”  (^M 章节)
a.脚本以"#!/bin/sh" 开始
        当你从windows系统上面ftp一个脚本到unix系统的时候,可能会报这样的错,因为在windows系统上面,每行的结束符为CRLF,在UNIX

系统上面,行结束符只有LF,CR被当作一个普通字符来处理了。(这个在你的windows终端上是看不到的)
        所以,MSDOS下的"#!/bin/sh", 在unix系统下面,它变成了"#!/bin/sh<CR>",(other names for <CR> are \r, \015, ^M,<Ctrl-M>).
        所以,当你运行脚本的时候,系统寻找的是 "sh<CR>"而不是"sh",所以报错"doesn't  exist"
       
        $ sed 'l;d;q' < script.sh
       #!/bin/sh\r$

问题所在, ($ marks the end of line, \r is the CR   character).

b.路径问题
        有时候,shell的安装路径并不是在/bin或者/usr/bin下面.例如,不是操作系统安装的时候自带的shell可能会被安装到/usr/local/bin

目录下.若你在ksh的路径是/usr/bin的机器上写了一个脚本,而在ksh的路径是/usr/local/bin 的机器上运行它,shebang应该更改正确.
        当你使用sh的时候,一般不会遇到这样的问题,但是,当你使用bash ,zsh或者其他的shell的时候,不同的机器可能安装路径不太一致.
        一个解决办法就是更改shebang, 代替
        #!/bin/sh的方法是使用
        #!/usr/bin/env sh
        当然,env也可能会在其他的路径下,但这并不常见。

13 为什么echo不能输出我想得到的东西.
        参考0a章节中“echo 使用注意事项”
       
        不同的shell的echo命令的效果也不尽一致,例如一些shell(bash,pdksh [,?]),使用下面的参数
      -n suppress newline at the end of argument list
      -e interpret backslash-escaped characters
      -E disable interpretation of backslash-escaped characters, even
         on systems where interpretation is the default.
        但是,pdksh也允许使用 在参数末尾使用"\c"来关闭换行符输出。
       
        POSIX标准只允许 \c 来关闭换行符号输出,不支持任何上面的提及的那些参数格式
        ksh88和ksh93对反斜线的翻译取决于程序执行#翻译不太对。

        [欢迎贴出其他shell的行为描述]
        总的来说,当你选择使用echo的时候,你应该清楚你的机器上的echo是怎么工作的,若可以选择的时候,print和printf的效果会更好


       
14 文件名中带有空格,我怎么在循环中使用它们.
        你想在循环中调用一个文件列表?这个列表是怎么存放的?若是以文本方式存放的,我们有一个关于合法字符的假想,在UNIX系统中

,除了\0' (NUL) 字符,都可以当作路径名的一部分,所以在文件中存储一些列文件名的唯一方法就是使用\0' (NUL) 来分隔他们(若你不使

用引用机制作为xagrs的输入的话)
        但是不幸的是大部分shell(zsh除外)和unix文本编辑程序(GNU的一些软件除外)都不能处理\0' (NUL) 符号,并且,很多命令,如

"ls","find","grep -l"的输出都是以换行符作为文件间隔的,所以,若你想处理这些输出,一个比较接近的假想就是我们认为文件名不包含换

行符,但你不能假装你的代码是完美的。
        所以,若你有一个以换行符结尾的文件列表list.txt,有两种方法处理:
        1-
    while IFS= read -r file <&3; do
      something with "$file" # be sure to quote "$file"
    done 3< list.txt
    (if your read doesn't have the "-r" option, either make another
    assumption that filenames don't contain backslashes, or use:

    exec 3<&0
    sed 's/\\/&&/g' < list.txt |
    while IFS= read file; do
      something with "$file" <&3 3<&-
    done
    )
        2-
    IFS="
    " # set the internal field separator to the newline character
      # instead of the default "<space><tab><NL>".
   
    set -f # disable filename generation (or make the assumption that
           # filenames don't contain *, [ or ? characters (maybe more
           # depending on your shell)).
   
    for file in $(cat < list.txt); do
      something with "$file" # it's less a problem if you forget to
                             # quote $file here.
    done

注意,在你构建这个列表之前,你还可以做一些其他事情。我们还有其他的方法来存储文件名.
  this list.txt. There are other ways to store filenames. For
    instance, you have the positional parameters.
   
    with:
    set -- ./*.txt
   
    you have the list of txt files in the current directory, and no
    problem with weird characters. Looping through them is just a
    matter of:
   
    for file
    do something with "$file"
    done
   
    You can also escape the separator. For instance, with
   
    find . -exec sh -c 'printf %s\\n "$1" | sed -n '"':1
      \$!{N;b1
      }
      s/|/|p/g;s/\n/|n/g;p'" '{}' '{}' \;
      
    instead of
   
    find . -print
   
    you have the same list of files except that the \n in filenames
    are changed to "|n" and the "|" to "|p". So that you're sure
    there's one filename per line and you have to convert back "|n"
    to "\n" and "|p" to "|" before referring to the file.

15.怎么改变默认的登录shell
        See  http://www.faqs.org/faqs/unix-faq/shell/shell-differences
        若没有很好的理由,最好不要改变root的默认登陆shell,默认登陆shell是指/etc/passwd中定义的shell,例如,当我使用root登陆

的时候,我不喜欢root的默认shell,这并不是一个好的理由。
        当只有根分区被挂在的时候,root的默认shell需要在单用户模式下使用,这两者(单用户和多用户)的配置有联系,默认shell必须

满足这个要求,所以,当你把root的默认shell改称其他shell,并且这个shell需要调用不在根分区上的库文件,那么你是在制造问题。
        更改root的登陆shell的最安全的方法就是以root登陆后
      # SHELL=/preferred/shell; export SHELL
      # exec <your preferred shell with login flag>

      e.g.

      # SHELL=/usr/bin/ksh; export SHELL
      # exec $SHELL -l

另一个可行的办法就是在root的.profile 或者.login配置文件增加一些语句检查首选shell是否能够运行。但是这却比直接运行"exec

<shell>" 更复杂且具有更多缺陷。例如,某个首选shell所依赖的库文件被破坏了等,一个比较好的做法就是
     if [ -x /usr/bin/ksh ]; then
        SHELL=/usr/bin/ksh; export SHELL
        ENV=/root/.kshrc; export ENV   
        /usr/bin/ksh -l && exit
      fi

一个更安全的方法就是在你运行你首选的shell之前,尝试运行一个命令。这样会减小你的shell对其他库文件的依靠的风险(shell所依赖的库

文件可能被破坏,也可能shell所依赖的库文件在一个没有挂载的文件分区内。
      if [ -x /usr/bin/ksh ]; then
        /usr/bin/ksh -c echo >/dev/null 2>&1
        if [ $? -eq 0 ];then
           SHELL=/usr/bin/ksh; export SHELL
           ENV=/root/.kshrc; export ENV
           /usr/bin/ksh -l && exit
        fi
      fi
另外一个通用的方法就是再创建一个uid为0的超级用户,例如发FREEBSD系统通常创建一个名为toor,可以根据自己的需要随意设置的用户,迂

回解决。

作者: john_zhang    时间: 2008-04-13 16:07
thanks!
作者: ylcqen    时间: 2008-05-29 09:46
不错,值得阅读一下!
作者: gigabyte    时间: 2008-08-01 12:09
精华啊,学习ing
作者: 小溪的号    时间: 2008-09-19 15:24

作者: wstar    时间: 2008-10-05 16:45
真是好帖,我是从另外一个帖子转过来的。
即学习shell又补习了一下英文。
真是一举两得。
作者: fly9527    时间: 2009-01-14 15:53
多谢!!
作者: oldoger    时间: 2009-03-10 08:30
好想翻译一下,可惜英语太差,看看还行。
作者: xiaoe_7    时间: 2009-07-28 10:23
thanks 学习
作者: yuanwendong    时间: 2009-10-28 17:29
well
作者: xiaoe_7    时间: 2009-11-03 14:40
辛苦 辛苦了
作者: qmail-czw    时间: 2010-03-10 15:11
顶!!!
作者: 为什么删我号    时间: 2010-04-14 20:55
嘿嘿,来学习一下
作者: rghliub    时间: 2010-04-26 11:04
回复 1# r2007


   
作者: clement1986    时间: 2010-06-07 19:29
帖子的作者是达人啊,在各种shell之间游刃有余,还能分得这么清楚,年薪至少百万
作者: yingtank    时间: 2010-07-14 16:59
看得眼都花了。
作者: flycentos    时间: 2010-09-27 09:58
顶。。。。
作者: liguxk    时间: 2010-12-24 11:39
不错
作者: 楼。。主    时间: 2011-12-02 23:11
提示: 作者被禁止或删除 内容自动屏蔽
作者: ulovko    时间: 2012-04-21 17:50
好全面的教程,佩服了!




欢迎光临 Chinaunix (http://bbs.chinaunix.net/) Powered by Discuz! X3.2