標  題: UNIX FAQ 中文版 Part 2

七篇文章包含一些在 comp.unix.questions 和 comp.unix.shell 常見到的問
題。請不再問這些問題,因為這些問題已經被回答過太多次了。但也請不要因為
有人問這些問題而發火,因為他們可能尚未讀過這些文章。

This collection of documents is Copyright (c) 1994, Ted Timar, except
Part 6, which is Copyright (c) 1994, Pierre Lewis and Ted Timar.

All rights reserved. Permission to distribute the collection is
hereby granted providing that distribution is electronic, no money is
involved, reasonable attempts are made to use the latest version and
all credits and this copyright notice are maintained.

Other requests for distribution will be considered.

All reasonable requests will be granted.

中文翻譯 by {chenjl,freedom,jjyang}@csie.nctu.edu.tw
若您對中文翻譯有任何意見請發 e-mail 給 cfaq@csie.nctu.edu.tw

我們希望這些文件中的資訊能對你有所幫助﹐但是並不保証其正確性。若發生損
害請自行負責。

您可以在 rtfm.mit.edu 的 pub/usenet/news.answers 找到包括此文件在內的
許多 FAQ。 在此目錄下的 FAQ 的名字可在文章的頂端的 "Archive-Name:"
那一行找到。


  此一 FAQ 是以"unix-faq/faq/part[1-7]" 為名。

這些文章大約分成:

      1.*)一般性的問題
      2.*)初學者可能會問的基本問題
      3.*) 中級的問題
      4.*) 自以為已經知道所有答案的人可能會問的高級問題
      5.*) 關於各種 shell 的問題
      6.*) 各式各樣的 Unix
      7.*) An comparison of configuration management systems (RCS, SCCS).

This article includes answers to:

        2.1)  我要怎麼刪除以 '-' 字元開頭為檔名的檔案?
        2.2)  我要怎樣才能把檔名當中含有特殊字元的檔案刪除?
        2.3)  我要如何列出整個目錄樹呢?
        2.4)  要怎麼設定 prompt 才會顯示出目前所在的目錄?
        2.5)  當我在寫 shell script 時,要如何從 terminal 讀入字元?
        2.6)  怎麼樣把 "*.foo" 改名為 "*.bar" 呢?怎樣把檔案名稱改成小寫呢?
        2.7)  為什麼我用 "rsh host command" 會有一些奇怪的訊息出現?
        2.8)  我要怎要用程式或者是 shell script 中設定目前所用的 shell 的環境
        2.9)  我要如何將 csh 的 stdout 與 stderr 導向到不同的地方呢?
        2.10) 我如何在 .cshrc 中判斷是否在 login shell 中?
        2.11) 在 shell 中要用怎樣的 pattern 來表示除了 "." 與 ".." 外的所有檔
        2.12) 在 Bourne shell script 裡要怎麼找出最後一個參數?
        2.13) 為什麼有人說 $PATH 裡不可以放 '.' 呢?
        2.14) 在 shell script 中要怎麼讓終端機發出聲音呢?
        2.15) 為什麼我不能用 "talk" 與我在某機器上的朋友交談呢?
        2.16) 為什麼我月曆是錯的?

若要找問題 2.5 的答案, 用 regular expression 往前找 "^2.5)" 即可。

因為這些都是正當合理的問題, 所以在 comp.unix.questions 或是
comp.unix.shell 中。每隔一陣子, 就會有這些問題與答案出現, 緊接著就會
有人對同樣問題一再出現發牢騷。關於 UNIX 代表啥呢? 請參考每月 post 在
news.announce.newusers 中名為 "Answers to Frequently Asked Questions"
的文章。

因為 Unix 有太多不同的種類了, 所以很難保證此文件所提供的答案必然會有
用。在嘗試本文件提供的作法前, 請先讀讀你所使用系統的手冊。若你對答案
有任何建議或更正, 請送 email 給 tmtaimar@isgtec.com.

----------------------------------------------------------------------
Subject: How do I remove a file whose name begins with a "-" ?
Date: Thu Mar 18 17:16:55 EST 1993

2.1)    我要怎麼刪除以 '-' 字元開頭為檔名的檔案?

        找一個方法讓檔案名稱開頭不要是 '-' 就可以了,最簡單的方法就是使用

                rm ./-filename

        (當然,我們假設 "-filename" 位於目前的目錄)。這個方法可以避免讓其
        他指令解釋 "-"。

        有許多指令,特別是呼叫 "getopt(3)" 的參數剖析常式的程式,會接受一
        個 "--" 的參數,代表「這是最後一個選項」,此後出現的項目都不再是選
        項,因此您的 rm 可能會接受這個 "rm -- -filename" 這種寫法。有些不用
        getopt() 的 rm 程式也會以同樣的方式處理單一字元 "-",因此您也可以試
        試 "rm - -filename"。

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

Subject: How do I remove a file with funny characters in the filename
?
Date: Thu Mar 18 17:16:55 EST 1993

2.2)    我要怎樣才能把檔名當中含有特殊字元的檔案刪除?

        如果這個「特殊字元」是 '/',請跳到這題的結尾;如果這個特殊的字元是
        一個 ' 或者控制字元或者中文字,請繼續往下讀。

        典型的解法是:

                rm -i some*pattern*that*matches*only*the*file*you*want

        這樣子的話 rm 會在要刪除符合你給的條件的檔案前,要你確定,不
        過若你的 shell 會將每個字元的第八個 bit 變成零,那以中文作檔
        名的檔案可能就刪除不掉了!

        與

                rm -ri .

        這樣子的話 rm 會刪除目前目錄下的所有檔案,而在刪除一個檔案之
        前會問你是否要刪除此檔。不過很不幸的,並非每一個版本的 rm 都
        能這麼用。再者,就算能用的話,這麼做的話會把目前所在目錄的所
        有子目錄都找進去,可能要用 "chmod a-x" 避免使子目錄無法搜尋才
        能避免可怕的後果。要做 "rm -r" 或含有萬用字元的 "rm" 前請先深
        呼吸,搞清楚自己是在做什麼!

        與

                find . -type f ... -ok rm '{}' \;

        "..." 是一堆用以辨識檔案名稱的述詞,譬如在找出一有問的檔案的
        inode 為何後,用

                find . -num 12345 -ok rm '{}' \;

        或

                find . -inum 12345 -ok mv '{}' new-file-name \;

        刪除或改名。 選項 "-ok" 是告訴 find 要執行指令前先要求你確認
        。若你能確定所下的指令沒有問題,或者怕所要處理檔案有奇怪的字
        元印出來會使螢幕亂七八糟,那用選項 "-exec" 就不會先要求你的確
        認。

        那當檔案名稱裡含有 '/' 時要怎麼辦呢?

        這類檔案是很特別的情形,並且只會因為 kernel 的 bug 而發生(通
        常是在寫 NFS 的時候,沒有把從遠端機器來的檔案名稱中不合規定的
        字元過濾掉)。我們第一件要做的事情就是,試著去瞭解為什麼這個
        問題會如此奇怪。

        UNIX 的目錄其實就只是單純的檔名和 inode number  的成對組合。
        舉例來說,目錄包含了如下的資訊:

                filename  inode
                         
                file1     12345
                file2.c   12349
                file3     12347

        理論上,只有 '/' 和 '\0' 兩個字元不能用在檔案名稱中,
        因為它們有以下的特殊用途:

                '/'     :用來分隔目錄名稱及檔案名稱。
                '\0'    :用來當檔名的終結字元。

        非常、極端、很不幸的,某些廠商做出來的 NFS 在回應遠端機器的要
        求時,會很白痴地造出含有斜線(/)的檔名。例如,當某人在 Mac
        或其他非 Unix 機器透過 NFS 造一個以日期為名稱的檔案到你的
        Unix 中。那麼,你的 Unix 目錄看起來可能就會像這個樣子:

                filename  inode

                91/02/07  12357

        我們前面所提過的 'find' 或 'rm' 都無法刪除這個檔案,因為這些或
        其他的 Unix 程式都會強制把 '/' 當作前述的分隔字元解釋。
                                              
        其實,任何一般的程式都會試著做 unlink("91/02/07"),而這對
        kernel 來說,它的意義是 "unlink 目錄 91 下的子目錄 02 中的檔
        案 07",但是,我們並沒有這樣的檔案,我們有的是一個名叫
        "91/02/07" 的檔案在目前的目錄中。這是個極細微但極重要的區別。

        這時該怎麼辦呢?首先,先回到產生這種亂七八糟檔名的 Mac,試試
        看 NFS daemon 要不要讓你改成不含 '/' 的檔名。如果不行,那就得
        找你的系統管理者幫忙了。請他試試以下幾種方法之一:

        1. 用 "ls -i" 找出檔案的 inode number,umount 掉這個
           file system 然後以 "clri" 將這個 inode 清除,然後
           祈求“fsck" 的成功。這個作法會刪除這亂七八糟檔名的
           檔案。

        2. 若還想保存這個檔案的資料,試試以下的做法:

           -在那亂七八糟檔名的檔案所在之目錄的親目錄底下建一
            個子目錄,將舊的目錄下能搬動的檔案都搬到新的目錄
            裡。
           -以 "ls -id" 取得舊目錄的 inode number
           -unmount 掉這個 file system, 用 "clri" 清掉那個
            目錄的 inode
           -"fsck" 那個 file system
           -從新 mount 上那個 file system
           -將新的目錄改名為舊的目錄名
           -從 lost+found 下找回那個檔案,改個好名字,放回原
            來的目錄。

        3.若你有一個叫做 "fsdb" 的程式,那你可以試試看嘍!

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

Subject: How do I get a recursive directory listing?
Date: Thu Mar 18 17:16:55 EST 1993

2.3)  我要如何列出整個目錄樹呢?

        底下有幾種做法自己挑一個吧:

                ls -R             (not all versions of "ls" have -R)
                find . -print     (should work everywhere)
                du -a .           (shows you both the name and size)

        若你要找的是特定的檔案,例如說是檔名結尾為 ".c" 者,可用

                find . -name '*.c' -print

        "find" 是一個很強很好用的程式。值得一學。

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

Subject: How do I get the current directory into my prompt?
Date: Thu Mar 18 17:16:55 EST 1993

2.4)  要怎麼設定 prompt 才會顯示出目前所在的目錄?

        這得視你的 shell 而定。有些 shell 很容易,有些 shell 很難,有些根
        本辦不到。

        C Shell (csh):

          將以下的東西加入你的 .cshrc 裡。

                alias setprompt 'set prompt="${cwd}% "'
                setprompt           # to set the initial prompt
                alias cd 'chdir \!* && setprompt'

          假如你有用 pushd 與 popd, 把底下的東西也加進去。

                alias pushd 'pushd \!* && setprompt'
                alias popd  'popd  \!* && setprompt'

          若你的 C shell 沒有 $cwd 這個變數,那就得用 `pwd` 代替之。

          若你想要的只是 prompt 裡有目前所在目錄的最後一個成分
          ("mail%" 而非 "/usr/spool/mail%") 則用

                alias setprompt 'set prompt="$cwd:t% "'

          有些舊版的 csh 將 && 和 || 的意義弄反了。你可以試試看:

                false && echo bug

          若結果是印出 "bug",那就把 && 和 || 對調,或找一個沒有這種
          bug 的 csh 來用。

        Bourn Shell (sh):
                        
          如果你有較新版的 Bourn Shell(SVR2 或更新的版本),那麼你就可
          以用一個 shell function 來造你自己的命令,譬如 "xcd":

                xcd() { cd $* ; PS1="`pwd` $ ";}

          如果你的 Bourn Shell 是比較舊的版本,也是可以做到,但是方法比
          較複雜。這裡提供一個方法。把以下的內容加入你的 .profile:

                LOGIN_SHELL=$$ export LOGIN_SHELL
                CMDFILE=/tmp/cd.$$ export CMDFILE
                # 16 is SIGURG, pick a signal that's not likely to be
                used
                PROMPTSIG=16 export PROMPTSIG
                trap '. $CMDFILE' $PROMPTSIG

          然後把以下的部份寫成一個可執行的 script(不需要縮排),名字就
          叫做 "xcd",放在你的 PATH 中

                : xcd directory - change directory and set prompt
                : by signalling the login shell to read a command file

                cat >${CMDFILE?"not set"} </dev/null`
                stty -cbreak
                echo "Thank you for typing a $readchar ."

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

Subject: How do I rename "*.foo" to "*.bar", or change file names to
lowercase?
Date: Thu Mar 18 17:16:55 EST 1993

2.6) 怎麼樣把 "*.foo" 改名為 "*.bar" 呢?怎樣把檔案名稱改成小寫呢?

        為什麼 "mv *.foo *.bar" 不對呢? 想想 shell 是怎樣把萬用字元展開的
        吧。 在 mv 讀取參數前 "*.foo" 與 "*.bar" 就已經展開了。"mv *.foo
        *.bar" 在各種不同的 shell 會有不同的結果。 Csh 的話會印出 "No
        match",因為找不到 "*.bar"。 Sh 則是會執行 "mv a.foo b.foo c.foo
        *.bar",也就是說如果只有在有一名為 "*.bar" 的子目錄存在時 mv 才會
        認為執行成功,不過就算成功也不是你所預期的結果。
                                    
        正確的做法是用你所用的 shell 提供的迴圈來做。若你的系統中有
        "basename" 這個指令:

        C Shell:
                foreach f ( *.foo )
                    set base=`basename $f .foo`
                    mv $f $base.bar
                end

        Bourne Shell:
                for f in *.foo; do
                    base=`basename $f .foo`
                    mv $f $base.bar
                done

        有些 shell 會提供就自己的變數替代功能,那就可以不用 "basename",
        而用更簡單的迴圈了:

        C Shell:

                foreach f ( *.foo )
                    mv $f $f:r.bar
                end

        Korn Shell:

                for f in *.foo; do
                    mv $f ${f%foo}bar
                done

        如果沒有 "basename" 可用或是想要做像把 foo.* 改名為 bar.* 之類的
        事,那麼可以用其他的方法如 "sed" 把原來的檔案名稱做分隔的動作,但
        是迴圈的想法是一樣的。你也可以利用 "sed" 把檔名轉換成 "mv" 的命令
        ,然後再把這些命令轉給 "sh" 執行。如下:

                ls -d *.foo | sed -e 's/.*/mv & &/' -e 's/foo$/bar/' | sh

        在 1990 年 4 月時,Vladimir Lanin 把他自己寫的一個叫 "mmv" 的程式
        post 到 comp.sources.unix (Volumn 21, issues 87 and 88),這個程式
        就能夠把這件事處理得很好。 你可以這樣使用:

                mmv '*.foo' '=1.bar'

        以上所提的 shell 中的迴圈也可以用來做檔案名稱的大、小寫轉換。你可
        以用改檔名的方式把大寫檔名改為小寫:

        C Shell:
            foreach f ( * )
                mv $f `echo $f | tr '[A-Z]' '[a-z]'`
            end

        Bourne Shell:
            for f in *; do
                mv $f `echo $f | tr '[A-Z]' '[a-z]'`
            done

        Korn Shell:
            typeset -l l
            for f in *; do
                l="$f"
                mv $f $l
            done

        如果你還希望能處理含有特殊字元(空白或其他的奇怪字元)的檔名,那
        麼你最好用:
                   
        Bourne Shell:

            for f in *; do
              g=`expr "xxx$f" : 'xxx\(.*\)' | tr '[A-Z]' '[a-z]'`
              mv "$f" "$g"
            done

        'expr' 不管檔名裡有沒有特殊字元都會印出檔名。

        有些版本的 "tr" 需要用 '[' 和 ']',有些則不必。不過,不管是不是一
        定要用 '['  與 ']' 的 "tr",加了總是沒有害處。

        若你的系統裡有裝 "perl",那你可以用 Larry Wall 寫的這個多用途改檔
        名的程式。

        #!/usr/bin/perl
        #
        # rename script examples from lwall:
        #       rename 's/\.orig$//' *.orig
        #       rename 'y/A-Z/a-z/ unless /^Make/' *
        #       rename '$_ .= ".bad"' *.f
        #       rename 'print "$_: "; s/foo/bar/ if  =~ /^y/I' *  

        $op = shift;
        for (@ARGV) {
             $was = $_;
             eval $op;
             die $@ if $@;
             rename($was,$_) unless $was eq $_;
        }

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

Subject: Why do I get [some strange error message] when I "rsh host
command" ?
Date: Thu Mar 18 17:16:55 EST 1993

2.7)  為什麼我用 "rsh host command" 會有一些奇怪的訊息出現?

        (這裡所指的 "rsh"[也可能是 "remsh" 或 "remote"] 是 remote shell,
        而不是在有些系統中名為 "rsh" 的 restricted shell,這兩者天差地遠
        了!)

        若你在遠端的帳號用的是 C shell,那遠端的主機會幫你啟動一個 C 
        shell 來完成你所下的那個 'command',這個 shell 會讀取你在遠端的
        .cshrc 檔。若你的 .cshrc 中有 "stty" 或 "biff" 這類不適合 non-
        interactive shell 的指令。那就可能會有你所意想不到的結果,舉例來
        說,若你把

                stty erase ^H
                biff y

        放在你的 .cshrc 檔裡面。你可能會得到類似以下的奇怪訊息

                % rsh some-machine date
                stty: : Can't assign requested address
                Where are you?
                Tue Oct  1 09:24:45 EST 1991

        若你使用 "at" 或 "cron",那可能也會得到類似的錯誤訊息。

        不過沒關係,解決的方法非常簡單。若你的 ".cshrc" 裡面有一堆只有在
        interactive shell 中才有用的 operation,那就將那些 operation 都用
        以下的做法包起來:

            if ( $?prompt ) then 
                    operations....
            endif

        因為在一個 non-interactive 中不應該也沒有必要去設定 "prompt"。

        還有一些只有在開啟一個 login session時才有用的東西,最好搬到
        ".login" 中去。

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

Subject: How do I ... and have that change affect my current shell?
Date: Thu Mar 18 17:16:55 EST 1993

2.8)  我要怎要用程式或者是 shell script 中設定目前所用的 shell 的環境
      變數或改變所在的目錄?

        若沒有做一些特殊安排是做不到的。因為,當我們造出一 child process
        時,此 process 會繼承其 parent 的變數與所在的目錄。在這個 child
        process 只能改到自己的變數與所在目錄而無法影響到其 parent。

        要達到此目的,parent process 要與 child process 有所溝通。當
        child process 要改變變數值時得把要改變的變數及其內容寫到一個講好
        的地方,讓 parent process 去讀取, 並改變 parent process 的變數。


        另一個做法則是寫一個 shell script,然後在 Bourne shell 或 Korn
        shell 中用 ".",在 C shell 中用 source 去執行那個 shell script。
        若此 sript 名為 "myscript" :

        在 Bourne shell 或 Korn shell 中就用

                . myscript

        在 C shell 中則用

                source myscript

        若你想做的只是要改變所在目錄或是設定一個環境變數,那使用 C shell
        中的 alias 或是 Bourne/Korn shell 中的函數就可達成你的目的。可參
        考"要怎麼設定 prompt 才會顯示出目前所在的目錄"一節中的做法。

        Thomas Michanek (xtm@telelogic.se) 提供一個更詳細的解答(
        ftp://ftp.wg.omron.co.jp/pub/unix-faq/docs/script-vs-env)。
                               
------------------------------

Subject: How do I redirect stdout and stderr separately in csh?
>From: msb@sq.com (Mark Brader)
Date: Mon, 26 Oct 1992 20:15:00 -0500

2.9)  我要如何將 csh 的 stdout 與 stderr 導向到不同的地方呢?

        在 csh 中,用 ">" 將 stdout 導向,用 ">&" 則能將 stdout 與 stderr
        一起導向。可是不能只單獨把 stderr 轉向。最好的方法是

                ( command >stdout_file ) >&stderr_file

        以上的命令會開一個 subshell 執行 "command";而這個 subshell 的
        stdout 則被轉向到 stdout_file,同時這個 subshell 的 stdout 和
        stderr 則都被轉向到 stderr_file,但是因為 stdour 已經先被轉向了,
        所以 stderr 就會被轉到 stderr_file 了。

        如果你只是單純的不想把 stdout 做轉向,那麼就用 sh 來幫你吧。

                sh -c 'command 2>stderr_file'
               
------------------------------

Subject: How do I tell inside .cshrc if I'm a login shell?
Date: Thu Mar 18 17:16:55 EST 1993

2.10) 我如何在 .cshrc 中判斷是否在 login shell 中?

        當有人這麼問的時候,通常要問的是

        要如何判斷是否是一個 interactive shell? 或是問要如何判斷是否是
        最上層的 shell ?

        若你是要問 "是否在 login shell 中"(註:就是在做完 .cshrc 後,會
        再去做 .login),那麼你也許用 "ps" 和 "$$" 隨便弄一弄,就能知道了
        。因為通常 login shells 的名字在 "ps" 看起來都是由 '-' 做開頭的。
        如果你是真的對另外兩個問題感興趣,那麼這裡有個方法可以讓你在
        .cshrc 中判斷。

                if (! $?CSHLEVEL) then
                #
                # This is a "top-level" shell,
                # perhaps a login shell, perhaps a shell started up by
                # 'rsh machine some-command'
                # This is where we should set PATH and anything else we
                # want to apply to every one of our shells.
                #
                    setenv CSHLEVEL 0
                    set home = ~username   # just to be sure
                    source ~/.env          # environment stuff we always want
                else
                #
                # This shell is a child of one of our other shells so
                # we don't need to set all the environment variables again.
                #
                    set tmp = $CSHLEVEL
                    @ tmp++
                    setenv      CSHLEVEL        $tmp
                endif

                # Exit from .cshrc if not interactive, e.g. under rsh
                if (! $?prompt) exit

                # Here we could set the prompt or aliases that would be useful
                # for interactive shells only.

                source ~/.aliases

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

Subject: How do I construct a ... matches all files except "." and
".." ?
Date: Thu Mar 18 17:16:55 EST 1993

2.11) 在 shell 中要用怎樣的 pattern 來表示除了 "." 與 ".." 外的所有檔案?

        這個問題看來容易。因為你可以用

        *       表示所有不是以 "." 為開端的檔案

        .*      表示所有以 "." 為開端的檔案,但是這樣會把 "." 和 ".." 也包
                含進來,但是通常你並不會想把這兩個也含進來。

        .[!.]*  這只有比較新的 shells 才能用;某些 shells 用 "^" 代替 "!";
                而符合 POSIX 標準的 shells 一定能用 "!",但是大部份也都能接
                受 "^";所有具可移植性的應用程式都不應該在 "[" 之後緊接著沒被
                quota 起來的 "^")表示所有以 "." 為開頭並且第二個字元不是 "." 
                的檔案;但是這樣卻會漏掉 "..foo" 這類的檔案。

        .??*    表示所有以 "." 為開頭且檔名長度至少為 3 的檔案,這樣大概
                就能避開 "." 和 ".." 了,但是卻還是會漏掉 ".a" 這類的檔。

        所以想要正確地表示除了 "." 與 ".." 之外所有的檔案,你必須要用到 3
        個 patterns(如果你沒有像 ".a" 這樣的檔案,那你可以去掉第一個
        pattern):

                .[!.]* .??* *

        或者你也可以用一兩個外部程式和 backquote substitution。這樣就很完
        美了:


              `ls -a | sed -e '/^\.$/d' -e '/^\.\.$/d'`

                (or `ls -A` in some Unix versions)

        不過即使是這樣做,碰上檔名裡面含有換行字元, IFS 字元,或是萬用字
        元仍然是沒輒。
                     
------------------------------

Subject: How do I find the last argument in a Bourne shell script?
Date: Thu Mar 18 17:16:55 EST 1993

2.12) 在 Bourne shell script 裡要怎麼找出最後一個參數?

        由
                Martin Weitzel <@mikros.systemware.de:martin@mwtech.uucp>
                Maarten Litmaath 
        提供的答案:

        若你能確定參數不會超過九個的話,可用:

                eval last=\${$#}

        在符合 POSIX 標準的 shell 裡,不管有多少個參數都可用上述的方法。

        底下的方法是一定有用的:

                for last
                do 
                        :
                done

        更一般性的做法是:

                for i
                do
                        third_last=$second_last
                        second_last=$last
                        last=$i
                done

        若你想做的是將最後一個參數去除或是將一堆參數的順序反過來或是取用
        第 N 個參數。底下是一個不用造出 subprocess 只用 shell 內建功能的
        做法:

                t0= u0= rest='1 2 3 4 5 6 7 8 9' argv=

                for h in '' $rest
                do
                        for t in "$t0" $rest
                        do
                                for u in $u0 $rest
                                do
                                        case $# in
                                        0)
                                                break 3
                                        esac
                                        eval argv$h$t$u=\$1
                                        argv="$argv \"\$argv$h$t$u\""   # (1)
                                        shift
                                done
                                u0=0
                        done
                        t0=0
                done

                # now restore the arguments
                eval set x "$argv"                                      # (2)
                shift

        這個例子可以用到 999 個參數,應該夠用了吧?仔細看看(1)與(2)標示的
        地方,想辦法說服你自己不管參數裡面有什麼奇怪的字元這兩行都不會出
        差錯。

        要找第 N 個參數,用:

                eval argN=\$argv$N

        要將參數的順序反過來,標示為(1)的那一行必須改成

                argv="\"\$argv$h$t$u\" $argv"

        自己練習最後一個參數去除的方法。

        若允許呼叫外部指令這類造出 subprocess 的做法,代誌就更加好辦了。
        底下是找出 argvN:

                N=1

                for i
                do
                        eval argv$N=\$i
                        N=`expr $N + 1`
                done
                
        要將參數的順序反過來還有一個不用造出 subprocess,有更簡單的方法。
        這個方法也可以用來去除最後一個參數, 不過要注意的是 argvN 不在是
        原來的第 N 個參數:

                argv=

                for i
                do
                        eval argv$#=\$i
                        argv="\"\$argv$#\" $argv"
                        shift
                done

                eval set x "$argv"
                shift

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

Subject: What's wrong with having '.' in your $PATH ?
Date: Thu Mar 18 17:16:55 EST 1993

2.13) 為什麼有人說 $PATH 裡不可以放 '.' 呢?

        背景知識: 環境變數 PATH 是一串用冒號隔開的目錄。當你下一個指令而
        沒有指定所在的目錄,例如 "ls" 而非 "/bin/ls",則你的 shell 就會在
        PATH 所指定的目錄中去找尋指令。


        你可以在 PATH 裡面放入目前所在的目錄 "." 。或者,在 PATH 中加入一
        個空的目錄,這兩者是等效的

              for csh users:

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

              for sh or ksh users

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

        把 "." 放在 PATH 中是很方便的,若要執行 "./a.out" 只要打 "a.out"
        即可,但是這麼做會有大麻煩。
                                  
        當把 "." 放在 PATH 的最前面情況下。若你目前所在目錄是如 "/tmp" 這
        樣大家都可以寫的地方。如果有別的使用這放了一個名為 "ls" 的程式在
        那裡,而你打入 "ls" 那你可能就死得非常難看了。

        把 "." 放到 PATH 的結尾是個比較好的做法:

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

        這麼一來,當你在 "/tmp" 中打 "ls" 時, shell 就會先找 "/usr/ucb",
        "/bin" 與 "/usr/bin" 裡的 "ls"。減少了一點危險。不過仍然不是百分之
        百安全。假如你是個笨拙的打字者,有一天你把 "ls -l" 打成 "sl -l",
        而又有一個聰明的使用者能猜到這種常見的打字錯誤,在 "/tmp" 底下放
        了一隻 "sl",你還是得死。所以啊,千萬要小心。

        有許多 Unix 老手都不把 "." 放在 PATH 裡:

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

        要用目前目錄的 "program" 就打 "./program"。他們認為犧牲一點方便換
        取安全是值得的。

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

Subject: How do I ring the terminal bell during a shell script?
>From: uwe@mpi-sb.mpg.de (Uwe Waldmann)
Date: Fri, 30 Apr 93 16:33:00 +0200

2.14) 在 shell script 中要怎麼讓終端機發出聲音呢?

        這個問題的答案視你所用的 Unix 而定,更精確的說是視你所用的 "echo"
        而定。

        BSD-like 的 "echo" 有一個選項 "-n" 可以用來避免換行,可是無法使用
        \nnn 來表示八進位。所以用法是

                echo -n '^G'

        ^G 是表示 ASCII 中的 bell 字元。在 emacs 中可以用 "Ctrl-Q Ctrl-G"
        產生之,在 vi 中則用 "Ctrl-V Ctrl-G"。

        SysV-like 的 "echo" 可用 \c 避免換行,並且也可以用 \nnn。所以用

                echo '\007\c'
                   
------------------------------

Subject: Why can't I use "talk" to talk with my friend on machine X?
>From: tmatimar@isgtec.com (Ted Timar)
Date: Thu Mar 18 17:16:55 EST 1993

2.15) 為什麼我不能用 "talk" 與我在某機器上的朋友交談呢?

        斯斯有三種,Unix 常見的 "talk" 程式也有三種,兩兩不能互通。舊的
        "talk" 有兩種。為什麼有兩種呢?因為一般稱為 "otalk" 的這個 "talk"
        沒有把 "endian" 考慮進去,所以就有兩種了。例如 big-endian 的 Sun
        ,與 little-endian 的 Vax 所用的 otalk 就無法溝通。 這一版的 talk
        使用 port 517。

        在 1987 年左右,除了 Sun 以外的所有廠商定了一個知曉 network byte
        order 的 talk,這個新的 talk 通常稱為 ntalk。所有使用 ntalk 的機
        器都能互通。這個 talk 使用 port 518。

        有一些 talk 程式懂得 ntalk 與一種 otalk。其中最廣為使用的是
        "ytalk"。

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

Subject: Why does calendar produce the wrong output?
>From: tmatimar@isgtec.com (Ted Timar)
Date: Thu Sep  8 09:45:46 EDT 1994

2.16) 為什麼我月曆是錯的?

        常常有人發現 Unix 的月曆程式 'cal',產生的結果與預期的不同。

        1752 年 9 月的月曆就非常奇怪

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

        這是因為這個月開始整個大不列顛帝國(美國那時還是英國殖民地),從凱
        撒曆換成現行的太陽曆。

        [譯注:教皇格理高十三世(Pope Gregory XIII)於 1582 年改革曆法,可
               是大不列顛帝國直到 1752 年才換用之。] 

        另一個常見的問題是參數給錯。舉例來說,要看 1994 年 9 月的月曆,應
        是 'cal 9 1994' 而非 'cal 9 94'。

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