原帖由 r2007 于 2005-12-2 23:11 发表
哈,开始啃E文了![]()
无法连接到远程服务器
您试图访问的地址 http://www.shelldorado.com/ 目前不可用。请确保网址(URL)拼写和标点符号使用都正确,然后试着重新装入页面。
原帖由 johnsilver 于 2006-1-31 22:18 发表
这的确是个好东西,有没有人已经译过了,贴出来共享一下,不然我就译了。
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 术语表
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 术语表
它是互联网上的一种搜索工具,它能搜索到数年前的信息,现在作为一个标准的参考指导人们去搜索已经讨论过的话题,这对搜索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 怎么颠倒(翻转)一个文件
18.7 KB, 下载次数: 256
原帖由 xnkjdx1998 于 2007-3-15 17:21 发表
第11个问题中的b选项似乎有点问题,PIPESTATUS好象不是数组,只能取第一个命令的返回值
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" ![]() 把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/' 这里有个特殊的地方需要考虑 ![]() 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输出的结果不是我想要的?" |
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,可以根据自己的需要随意设置的用户,迂
回解决。
欢迎光临 Chinaunix (http://bbs.chinaunix.net/) | Powered by Discuz! X3.2 |