Chinaunix

标题: [shell问答录]:命令、进程、子shell... [打印本页]

作者: woodie    时间: 2006-04-19 18:14
标题: [shell问答录]:命令、进程、子shell...
前些天在CU上讨论一个统计正在执行的脚本数量的问题过程中,发现自己对于shell如何执行命令方面了解还是甚少,惭愧惭愧...期间得到waker兄的指点,在此表示感谢!他的说法除了个别地方不太准确外,基本上是正确的。这些天抽时间找了些资料研究了一下,又学到了不少!这里把我的一点心得以问答的形式贴出来,供大家参考。小弟才疏学浅,错误的地方一定很多,欢迎大家拍砖、指正!

Q1: shell如何执行“简单”命令?
A: 这里的简单命令和bash参考手册里的含义相同,形式上一般是:命令的名称加上它的参数。有三种不同的简单命令:
1.内置命令(builtin)
是shell解释程序内建的,有shell直接执行,不需要派生新的进程。有一些内部命令可以用来改变当前的shell环境,如:
cd /path
var=value
read var
export var
...

2.外部命令("external command" or "disk command"
二进制可执行文件,需要由磁盘装入内存执行。会派生新的进程,shell解释程序会调用fork自身的一个拷贝,然后用exec系列函数来执行外部命令,然后外部命令就取代了先前fork的子shell。

3.shell脚本(script)
shell解释程序会fork+exec执行这个脚本命令,在exec调用中内核会检查脚本的第一行(如:#!/bin/sh),找到用来执行脚本的解释程序,然后装入这个解释程序,由它解释执行脚本程序。解释程序可能有很多种,各种shell(Bourne shell,Korn shell cshell,rc及其变体ash,dash,bash,zshell,pdksh,tcsh,es...),awk,tcl/tk,expect,perl,python,等等。在此解释程序显然是当前shell的子进程。如果这个解释程序与当前使用的shell是同一种shell,比如都是bash,那么它就是当前shell的子shell,脚本中的命令都是在子shell环境中执行的,不会影响当前shell的环境。


Q2: shell脚本是否作为单独的一个进程执行?
A: 不是,shell脚本本身不能作为一个进程。如上面讲的,shell脚本由一个shell解释程序来解释、运行其中的命令。这个shell解释程序是单独的一个进程,脚本中的外部命令也都作为独立进程依次被运行。这也就是为什么ps不能找到正在运行的脚本的名字的原因了。作为一个替代方案,你可以这样调用脚本:
sh script-name
这时shell解释程序“sh”作为一个外部命令被显式地调用,而script-name作为该命令的命令行参数可以被我们ps到。
另外,如果你的系统上有pidof命令可用,它倒是可以找出shell脚本进程(实际上应该是执行shell脚本的子shell进程)的进程ID:
pidof -x script-name


Q3: shell何时在子shell中执行命令?
A: 在此我们主要讨论Bourne shell及其兼容shell。在许多情况下shell会在子shell中执行命令:
1.(...)结构
小括号内的命令会在一个子shell环境中执行,命令执行的结果不会影响当前的shell环境。需要注意是此时变量$$会显示当前shell的进程id,而不是子shell的进程id。
参考:
{...;}结构中的命令在当前shell中执行,(内部)命令执行的结果会影响当前的shell环境。

2.后台执行或异步执行
command&
命令由一个子shell在后台执行,当前shell立即取得控制等候用户输入。后台命令和当前shell的执行是并行的,但没有互相的依赖、等待关系,所以是异步的并行。

3.命令替换
`command`(Bourn shell及兼容shell/csh)
$(command)(在ksh/bash/zsh中可用)
将command命令执行的标准输出代换到当前的命令行。command在子shell环境中执行,结果不会影响当前的shell环境。

4.管道(不同的shell处理不同)
cmd1|cmd2
cmd1和cmd2并行执行,并且相互有依赖关系,cmd2的标准输入来自cmd1的标准输出,二者是“同步”的。
对管道的处理不同的shell实现的方式不同。
在linux环境下大多数shell(bash/pdksh/ash/dash等,除了zshell例外)都将管道中所有的命令在子shell环境中执行,命令执行的结果不会影响当前的shell环境。
Korn shell的较新的版本(ksh93以后)比较特殊,管道最后一级的命令是在当前shell执行的。这是一个feature而非BUG,在POSIX标准中也是允许的。这样就使下面的命令结构成为可能:
command|read var
由于read var(read是一个内部命令)在当前shell中执行,var的值在当前shell就是可用的。
反之bash/pdksh/ash/dash中read var在子shell环境中执行,var读到的值无法传递到当前shell,所以变量var无法取得期望的值。类似这样的问题在各种论坛和news group中经常被问到。个人认为command|read var的结构很清晰,并且合乎逻辑,所以我认为Korn shell的这个feature很不错。可惜不是所有的shell都是这样实现的。如开源的pdksh就是在子shell执行管道的每一级命令。
Korn shell对管道的处理还有一个特殊的地方,就是管道如果在后台执行的话,管道前面的命令会由最后一级的命令派生,而不是由当前shell派生出来。据说Bourne shell也有这个特点(标准的Bourne shell没有测试环境,感兴趣的朋友有条件的可以自行验证)。但是他们的开源模仿者,pdksh和ash却不是这样处理。
最特殊的是zshell,比较新的zshell实现(好像至少3.0.5以上)会在当前shell中执行管道中的每一级命令,不仅仅是最后一条。每一条命令都由当前shell派生,在后台执行时也是一样。可见在子sehll中执行管道命令并不是不得已的做法,大概只是因为实现上比较方便或者这样的处理已经成为unix的传统之一了吧。
让我们总结一下,不同的shell对管道命令的处理可能不同。有的shell中command|read var这样的结构是ok的,但我们的代码出于兼容性的缘故不能依赖这一点,最好能避免类似的代码。

5.进程替换(仅bash/zsh中,非POSIX兼容)
<(...)
>(...)
与管道有点类似,例子:cmd1 <(cmd2) >(cmd3), cmd1, cmd2, cmd3的执行是同步并行的。
<(command)形式可以用在任何命令行中需要填写输入文件名的地方,command的标准输出会被该命令当作一个输入文件读入。
>(command)形式可以用在任何命令行中需要填写输出文件的地方,该命令的输出会被command作为标准输入读入。
两种形式中的command都在子shell环境中执行,结果不会影响当前的shell环境。

6.if或while命令块的输入输出重定向
在SVR4.2的Bourne shell中对此情况会fork一个子shell执行if块和while块中的命令;在linux下似乎其它的shell中都不这样处理。

7.协进程(ksh)
只有Korn shell和pdksh有协进程的机制(其它shell中可以用命名管道来模拟)。类似于普通的后台命令,协进程在后台同步运行,所以必须在子shell中运行。协进程与后台命令不同的是它要和前台进程(使用read -p和print -p)进行交互,而后者一般只是简单地异步运行。


Q4: 既然在当前shell中执行命令也会派生子shell,那么它与在子shell中执行命令又有什么区别呢?
A: 这种说法不准确。
在当前shell中执行内部命令不会派生子shell,因此有些内部命令才能够改变当前的shell执行环境。
在当前shell中执行外部命令或脚本时会派生子shell,所以这时命令的执行不会影响当前 的shell环境。注意:子shell中执行的内部命令只会改变子shell的执行环境,而不会改变当前shell(父shell)的环境。


Q5: 怎样把子shell中的变量传回父shell?
A: 例如(echo "$a" | read b不能工作,如何找到一个替代方案?下面给出一些可能的方案:
1.使用临时文件
...
#in subshell
a=100
echo "$a">tmpfile
...
#in parent
read b<tmpfile

2.使用命名管道
mkfifo pipef
(...
echo "$a" > pipef
...)
read b <pipef

3.使用coprocess(ksh)
( echo "$a" |&)
read -p b

4.使用命令替换
b=`echo "$a"`

5.使用eval命令
eval `echo "b=$a"`

6.使用here document
read b <<END
`echo "$a"`
END

7.使用here string(bash/pdksh)
read b <<<`echo "$a"`

8.不用子shell,用.命令或source命令执行脚本。
即在当前shell环境下执行脚本,没有子shell,也就没有了子shell的烦恼。

解决的方法还不止于此,其它的进程间通信手段应该也能使用,这有待于大家一起发掘了。^_^

————————————————————————————————————————————————————
参考帖子:
waker兄的帖子

原讨论贴

[ 本帖最后由 woodie 于 2006-4-19 18:20 编辑 ]
作者: sunyone    时间: 2006-04-19 19:10
好,总结的好
作者: r2007    时间: 2006-04-19 20:23
好文,先赞一个,有空好好研究一下
作者: waker    时间: 2006-04-20 08:04
3.使用coprocess(ksh)
( echo "$a" |&)
read -p b

in  kornshell try
echo "$a"|read b
echo $b
作者: waker    时间: 2006-04-20 08:19
Q1中

A: 这里的简单命令和bash参考手册里的含义相同,形式上一般是:命令的名称加上它的参数+赋值操作+IO重定向描述
从上面看
var=value不是一个简单命令,而是上面的第二部分
作者: woodie    时间: 2006-04-20 08:23
原帖由 waker 于 2006-4-20 08:04 发表
3.使用coprocess(ksh)
( echo "$a" |&)
read -p b

in  kornshell try
echo "$a"|read b
echo $b

正确!在Korn shell和zshell的较新版本这样是可以的,但注意pdksh不行。这一点Q3之4已经详细讨论过了。谢谢关注!
作者: waker    时间: 2006-04-20 10:10
这也就是为什么ps不能找到正在运行的脚本的名字的原因了。
或许有另一种解释
如果找不到,不妨在脚本上加入 #!试试
这个可以从manpage中找到解释

       If this execution fails because the file is not in executable  format,
       and the file is not a directory, it is assumed to be a shell script, a
       file containing shell commands.  A subshell is spawned to execute  it.
       This  subshell reinitializes itself, so that the effect is as if a new
       shell had been invoked to handle the script, with the  exception  that
       the  locations  of  commands  remembered by the parent (see hash below
       under SHELL BUILTIN COMMANDS) are retained by the child.

       If the program is a file beginning with #!, the remainder of the first
       line specifies an interpreter for the program.  The shell executes the
       specified interpreter on operating systems that  do  not  handle  this
       executable  format  themselves.  The arguments to the interpreter con-
       sist of a single optional argument following the interpreter  name  on
       the  first  line  of the program, followed by the name of the program,
       followed by the command arguments, if any.

作者: woodie    时间: 2006-04-20 11:21
先谢谢版主加精鼓励!
原帖由 waker 于 2006-4-20 08:19 发表
Q1中

A: 这里的简单命令和bash参考手册里的含义相同,形式上一般是:命令的名称加上它的参数+赋值操作+IO重定向描述
从上面看
var=value不是一个简单命令,而是上面的第二部分


waker兄说的对!嗯,这里是说得不太严谨。
通常我们都会把单独的赋值操作“当作”一条命令,实际使用中也是这样。如:
for i in 1 2 3; do
  a$i=$i
done
赋值显然是作为一条命令来使用的。
但是深究起来,赋值到底是那种类型的命令呢?它显然不是外部命令或脚本命令,也不是函数或alias。那么它是内部命令吗?但在shell的内部命令列表中你又找不到它。
我想赋值系列符号:=, +=, -=,*/,/=, ...应该理解为shell的操作符,变量名和所赋的值是它的“操作对象”,这就可以解释在命令前面的赋值操作,如:
VAR=value command
此处的赋值与IO重定向操作一样,不是单独的命令。
但赋值操作又的确可以作为命令使用,这时,我宁愿把它理解成一条特殊的“内部命令”。
至于你提到的重定向操作,的确有的重定向操作是被作为“简单命令”的一部分来处理的;但是有的重定向操作符却不能这样,如“|”,它是被用来连接多个命令成为一个组合命令来使用的。
情况似乎越来越复杂了!OK,我们不妨从反面来界定一下,什么不是简单命令?
其一,循环/分支等用于流控的复杂命令(字面意义):
if cmd1; then
  cmd2
elif cmd3; then
  cmd4
fi
while true; do
  cmd1
  cmd2
  ...
done
等等,不再列举。
其二,命令列表,如:
cmd1;cmd2
cmd1&&cmd2||cmd3
cmd1&cmd2
...
其三,命令分组:
(cmd1;cmd2)
{ cmd1;cmd2;}
...
其四,管道,如:
cmd1|cmd2

其五,上面提到过的函数或alias,他们都可以组合多条命令因此排除在简单命令之外。
...
其六,命令替换或进程替换,它们也引入了别的命令。
...
也许还有。
...

其实我们使用“简单命令”这个概念只是为了简化问题和能有一个进一步讨论的共同基点,没有必要追究它的定义是否准确、完备。其实完全从字面来理解亦无不可,就是指最简单的“一条命令”,多条命令的组合不算。
作者: woodie    时间: 2006-04-20 11:42
原帖由 waker 于 2006-4-20 10:10 发表
这也就是为什么ps不能找到正在运行的脚本的名字的原因了。
或许有另一种解释
如果找不到,不妨在脚本上加入 #!试试
这个可以从manpage中找到解释

       If this execution fails because the file is not  ...

不加首行的#!的脚本如何能用脚本名自己执行呢?呵呵。^_^
如果首行的解释器与当前shell种类不同,确实可以ps到,不过ps到的脚本名仍是命令行参数而不是命令名。
如果解释器与当前shell是同一种shell,只能ps到当前shell的另一个副本而已。测试是在linux下做的,也许是shell内部的处理不同吧。
我的看法:无论如何,脚本不作为单独的进程运行,单独运行的是脚本的解释器进程。
作者: waker    时间: 2006-04-20 16:16
A:不加首行的#!的脚本如何能用脚本名自己执行呢?呵呵。^_^

*:那一段不是有解释么?


B:至于你提到的重定向操作,的确有的重定向操作是被作为“简单命令”的一部分来处理的;但是有的重定向操作符却不能这样,如“|”,它是被用来连接多个命令成为一个组合命令来使用的。

*:恩,你自己已经找到答案了,加了|就不是simple command了,pipeline也不等于重定向哦

C:但赋值操作又的确可以作为命令使用,这时,我宁愿把它理解成一条特殊的“内部命令”。
*:如果command取 `command line'中的command,我想应该是没问题的,如果取`simple command'中的command,我认为是不恰当的,因为前者的command是不是翻译成“语句”比较合适? for do done if fi是不是一个“内部命令”呢?

以上是我对bash manpage中一些内容的理解

关于进程,可能我们的理解不同,我的理解打个比方,就象单独用车和石头来定义“运石头过程”都是不确切的一样,而且在这个过程中,车和石头都是可以换的,不能换的就是“本次过程的标志”,对系统来说就是PID
作者: woodie    时间: 2006-04-20 19:38
标题: To waker兄。
您的说法我部分同意,另外的部分则不能苟同。^_^
* shell脚本可以不在首行指定解释器,缺省的解释器/bin/sh会被用来解释、执行它。这一点你说的是对的,又是我孤陋寡闻了,受教。

* |不是重定向的操作符,这点也同意,我说错了。

至于赋值操作能不能算作命令,我的理解和你不同。
bash man page没有明确的说法,那让我们看看ksh的。下面摘自是Korn shell 93的man page:
...
Commands.
    A simple-command  is a list of variable assignments (see VariableAssignments  below) or a sequence of blank  separated words which may be preceded by a list of variable assignments (see Environment  below). The first word specifies the name of the command to be executed. Except as specified below, the remaining words are passed as arguments to the invoked command. ...


第一句就明确指出赋值的列表是simple-command的一种。
再看另一段(抱歉,做这样大段的引述):
...
A command  is either a simple-command or one of the following. Unless otherwise stated, the value returned by a command is that of the last simple-command executed in the command.

for vname  [  in word  . . .  ] ;do list  ;done
    Each time a for command is executed, vname  is set to the next word  taken from the in word  list. If in word  . . . is omitted, then the for command executes the do list  once for each positional parameter that is set starting from 1 (see ParameterExpansion below). Execution ends when there are no more words in the list.
for (( [ expr1  ] ; [ expr2  ] ; [ expr3  ] )) ;do list  ;done
    The arithmetic expression expr1 is evaluated first (see ArithmeticEvaluation below). The arithmetic expression expr2 is repeatedly evaluated until it evaluates to zero and when non-zero, list is executed and the arithmetic expression expr3 evaluated. If any expression is omitted, then it behaves as if it evaluated to 1.
select vname  [  in word  . . .  ] ;do list  ;done
    A select command prints on standard error (file descriptor 2) the set of words, each preceded by a number. If in word  . . . is omitted, then the positional parameters starting from 1 are used instead (see ParameterExpansion  below). The PS3 prompt is printed and a line is read from the standard input. If this line consists of the number of one of the listed words, then the value of the variable vname  is set to the word  corresponding to this number. If this line is empty, the selection list is printed again. Otherwise the value of the variable vname  is set to null. The contents of the line read from standard input is saved in the variable REPLY. The list  is executed for each selection until a break  or end-of-file  is encountered. If the REPLY variable is set to null  by the execution of list, then the selection list is printed before displaying the PS3 prompt for the next selection.
case word  in [  [ ( ]pattern  [  | pattern   ] . . . ) list  ;;  ] . . . esac
    A case command executes the list  associated with the first pattern  that matches word. The form of the patterns is the same as that used for file-name generation (see FileNameGeneration  below). The ;; operator causes execution of case to terminate. If ;& is used in place of ;; the next subsequent list, if any, is executed.
if list  ;then list  [  elif list  ;then list   ] . . . [  ;else list   ] ;fi
    The list  following if is executed and, if it returns a zero exit status, the list  following the first then is executed. Otherwise, the list  following elif is executed and, if its value is zero, the list  following the next then is executed. Failing each successive elif list , the else list  is executed. If the if list  has non-zero exit status and there is no else list, then the if command returns a zero exit status.
while list  ;do list  ;done
until list  ;do list  ;done
    A while command repeatedly executes the while list  and, if the exit status of the last command in the list is zero, executes the do list; otherwise the loop terminates. If no commands in the do list  are executed, then the while command returns a zero exit status; until may be used in place of while to negate the loop termination test.
((expression ))

    The expression  is evaluated using the rules for arithmetic evaluation described below. If the value of the arithmetic expression is non-zero, the exit status is 0, otherwise the exit status is 1.
(list )

    Execute list  in a separate environment. Note, that if two adjacent open parentheses are needed for nesting, a space must be inserted to avoid evaluation as an arithmetic command as described above.
{ list ;}

    list  is simply executed. Note that unlike the metacharacters ( and ), { and } are reserved words and must occur at the beginning of a line or after a ; in order to be recognized.
[[ expression  ]]

    Evaluates expression  and returns a zero exit status when expression  is true. See ConditionalExpressions  below, for a description of expression.
function varname  { list  ;}
varname  () { list  ;}
    Define a function which is referenced by varname. A function whose varname  contains a . is called a discipline function and the portion of the varname  preceding the last . must refer to an existing variable. The body of the function is the list  of commands between { and }. A function defined with the function varname  syntax can also be used as an argument to the . special built-in command to get the equivalent behavior as if the varname () syntax were used to define it. (See Functions  below.)
time [  pipeline   ]

    If pipeline  is omitted the user and system time for the current shell and completed child processes is printed on standard error. Otherwise, pipeline  is executed and the elapsed time as well as the user and system time are printed on standard error.

The following reserved words are recognized as reserved only when they are the first word of a command and are not quoted:

    if then else elif fi case esac for while until do done { } function select time [[ ]] !
...

看到吗?连[[...]], ((...))也被承认是command(*not* just statement)。^_^

再看看linux下的ash的man page:
...
   Simple Commands
     If a simple command has been recognized, the shell performs the following actions:

           1.   Leading words of the form "name=value" are stripped off and assigned to the environment of the simple com-
                mand.  Redirection operators and their arguments (as described below) are stripped off and saved for pro-
                cessing.

           2.   The remaining words are expanded as described in the section called "Expansions", and the first remaining
                word is considered the command name and the command is located.  The remaining words are considered the
                arguments of the command. If no command name resulted, then the "name=value" variable assignments recog-
                nized in item 1 affect the current shell.

...

其中也承认只有赋值命令而没有命令名的“Simple Commands”。

呵呵,太长了!先贴这么多,其余的下一回贴再贴。^_^

[ 本帖最后由 woodie 于 2006-4-20 20:11 编辑 ]
作者: woodie    时间: 2006-04-20 20:07
标题: To waker again.
上面是引述几段man page来支持我的观点。下面再提出两条理由:
1.赋值可以出现在一条命令所出现的所有位置(话说得太满了,如果有例外麻烦大家告诉我,我好赶快修改^_^)
a=1 > /dev/null
a=1 < /dev/stdin
a=1 &
if a=1; then
a=1
fi
while a=1; do
a=1
done
echo `a=1`
...
2.赋值“命令”(请先允许我这么称呼它吧,^_^)同样有返回值:
a=1
echo $?
0
执行正常,返回0。
false|a=1
echo $?
0
$?为pipe的最后一条命令的执行状态,而前一级的false应该是返回1。
false|a=1
set|grep PIPE
PIPESTATUS=([0]="1" [1]="0")
这样看的更清楚些。

所以,我认为赋值可以作为命令这一点应该没有问题,至于是否是内部命令则可以商榷。不过就其表现,把它归入builtin命令一类,也无太大不妥。这是我个人看法,大家可以争鸣。^_^也许我们可以开一个新的分类?
作者: woodie    时间: 2006-04-20 20:18
标题: To waker once more.
不好意思,关于shell脚本,我仍然坚持我的观点:
我的看法:无论如何,脚本不作为单独的进程运行,单独运行的是脚本的解释器进程。

今天晚了,我们明天再做友好的争论吧!Anyhow, 谢谢您上面的回贴!^_^
作者: kingofworl    时间: 2006-04-20 20:31
学习,可惜我初学,看不太懂
作者: r2007    时间: 2006-04-20 21:25
标题: 回复 13楼 woodie 的帖子
不管怎么说,woodie的理解是对的。至于表达出来的用词是否准确,无大碍
作者: r2007    时间: 2006-04-20 21:46
以下面代码为例
  1. r2007@www r2007 $ a=1
  2. r2007@www r2007 $ echo $a
  3. 1
  4. r2007@www r2007 $ a=2 :
  5. r2007@www r2007 $ echo $a
  6. 1
  7. r2007@www r2007 $ a=2 echo $a
  8. 1
  9. r2007@www r2007 $ echo $a
  10. 1
  11. r2007@www r2007 $ a=2 eval echo '$a'
  12. 2
  13. r2007@www r2007 $ echo $a
  14. 1
  15. r2007@www r2007 $ a=2 env
  16. a=2
  17. MANPATH=/usr/share/man:/usr/local/share/man
  18. ...
  19. ...
  20. ...
  21. _=/bin/env
复制代码

可以看出var=val,在不同的上下文中有不同的解释。如果单独出现则是普通的赋值语句,如果有其他命令,则是用来定义环境变量的,且不影响当前的环境。
作者: waker    时间: 2006-04-21 08:01
赋值语句是一个简单命令,我理解错了
作者: waker    时间: 2006-04-21 09:20
关于进程,争论究竟是bash的进程还是foobar.script的进程,这本来就是没有意义的
想如果以CMD代表进程的话,你的说法是对的,但我想CMD代表进程是不严谨的,代表进程的只能是PID

找个极端的例子:zombie进程又是哪个CMD的呢?
作者: woodie    时间: 2006-04-21 15:12
To waker:

关于脚本,我想你的理解也有一定的道理,我也不能强求你认同我的观点,大家各自阐述自己的意见也不错啊。
我想再说两条理由,不一定站得住脚:

1.脚本无法自主执行,它从来都没有取得CPU时间片去执行哪怕是一句机器代码,它只是被动的为解释程序所解释执行。
2.ready, running, wait等等进程的状态也不好安在它身上。
3.脚本在实际应用中不能作为系统进程调度的一个基本单元,那么认为它可以作为进程又有何益处呢?
nice script-name?
killall script-name?

至于进程与command,那是两个不同的概念,进程应该属于操作系统的范畴,命令在这里主要是shell中的概念。二进制可执行文件是命令,shell builtin,alias,function,甚至脚本文件自己也可以当作命令。和你一样,我也不同意将两个概念划等号。^_^

还有你举的zombie,它只是一种特殊的进程,或者说是进程的一种特殊的状态。zombie之于进程,就如同死人之于活人。zombie已经死亡,但它死亡之前毕竟曾经是某个程序的运行实例,而那个程序可能恰恰就是一条external command。

我也同意你:争论这一点实际的意义并不大。^_^

不过同你的争论让我提高不少,谢谢你,也谢谢CU这个平台吧!^_^
作者: 大蚂蚁    时间: 2006-04-22 03:21
非常精彩的讨论,呵呵,第一次看到waker的错误,俺以前都把waker当真理的,HOHO

但是,我还有个疑问:eval属于build in还是?
作者: shhgs    时间: 2006-04-30 07:49
同意waker的意见。进程分两部分,一个是运行的环境,一个是CPU执行的指令。关于脚本,其执行的指令应该是解释器。只不过这个解释器比较强大,可以动态生成CPU的指令。
作者: mei1i    时间: 2006-06-11 09:28
very good.
作者: ycguolin    时间: 2006-06-11 11:15
值得学习,收藏,谢谢!
作者: Cygnus    时间: 2006-06-12 05:38
原帖由 woodie 于 2006-4-20 20:07 发表
所以,我认为赋值可以作为命令这一点应该没有问题,至于是否是内部命令则可以商榷。不过就其表现,把它归入builtin命令一类,也无太大不妥。 ...


One or more variable assignments can start a simple command or can be arguments to some special built-in commands.


原帖由 woodie 于 2006-4-20 19:38 发表
...
* shell脚本可以不在首行指定解释器,缺省的解释器/bin/sh会被用来解释、执行它。这一点你说的是对的,....


r u sure?


原帖由 woodie 于 2006-4-21 15:12 发表
... T3.脚本在实际应用中不能作为系统进程调度的一个基本单元,那么认为它可以作为进程又有何益处呢?


Most of the time, you like to "kill" the script.


原帖由 大蚂蚁 于 2006-4-22 03:21 发表
..
但是,我还有个疑问:eval属于build in还是?


builtin command


原帖由 r2007 于 2006-4-20 21:46 发表
可以看出var=val,在不同的上下文中有不同的解释。如果单独出现则是普通的赋值语句,如果有其他命令,则是用来定义环境变量的,且不影响当前的环境。  ...


Acutally, environment variables are a big topic.  For example, do you think the following two lines
can be the same:

    echo a=1 b
    a=1 echo b

[ 本帖最后由 Cygnus 于 2006-6-12 08:30 编辑 ]
作者: woodie    时间: 2006-06-12 15:40
To Cygnus:
这个帖子讨论的问题的确比较复杂,欢迎Cygnus兄加入。^_^
>>* shell脚本可以不在首行指定解释器,缺省的解释器/bin/sh会被用来解释、执行它。这一点你说的是对的,....
>r u sure?
很多资料上是这样说的。不过可能是大多因袭了前人的说法吧,这并不完全准确。当内核不能用"shebang"(#!/path/shell)执行一个脚本时,尝试找到一个合适的interpreter的任务就落到当前shell的头上。这时缺省的解释器是什么呢?这就取决与不同的shell实现了。在我的linux box上ash和pdksh会调用/bin/sh,但你知道在linux下这通常是/bin/bash的一个符号链接,实际上调用的是bash;bash会派生自身的子shell来执行脚本;而zshell会调用不带绝对路径的sh,这当然最终仍会调用/bin/bash。总体来看,Bourne兼容的shell,如ash/pdksh/bash/zsh,都可能会调用Bourne shell或者某个Bourne兼容的shell。
至于c shell或兼容的tcsh则处理不同。如果脚本的第一行的第一个字符是#,csh会用c shell来解释执行脚本;如果首字符是:或任意非#的字符,他会试图调用Bourne shell来解释它,在我的平台下,tcsh会调用/bin/sh。
其它的平台是否类似呢,可惜我没有条件测试。

>>... T3.脚本在实际应用中不能作为系统进程调度的一个基本单元,那么认为它可以作为进程又有何益处呢?
>Most of the time, you like to "kill" the script.
I am not a butcher. I don't *like* killing. ^_^

[ 本帖最后由 woodie 于 2006-6-12 17:44 编辑 ]
作者: r2007    时间: 2006-06-12 16:24
原帖由 Cygnus 于 2006-6-12 05:38 发表


Acutally, environment variables are a big topic.  For example, do you think the following two lines
can be the same:

    echo a=1 b
    a=1 echo b

确实var=val这种形式在有其它命令时并不一定就是设置环境变量的意思。
请看我原文中的例子,明显是放在命令前面的,不至于让人产生误解吧。
这样的例子也不止echo a=1 b这一个。
printf a=1
awk -v a=1 '{print a,$0}'
ls a=1
等等,var=val的形式都是命令参数,一眼就可以看出这些形式是不在当时的讨论范畴之内的。
Cygnus是在挑字眼吧^_^。不过还是感谢指正。
作者: waker    时间: 2006-06-12 16:32
>    echo a=1 b
>    a=1 echo b
在bash中,它们一样不一样取决于你的喜好

  1. [waker@proxy dd3]$ a=1 echo b
  2. b
  3. [waker@proxy dd3]$ echo a=1 b
  4. b
  5. [waker@proxy dd3]$ echo a=1 b
  6. a=1 b
  7. [waker@proxy dd3]$ a=1 echo b
  8. b
复制代码

两种结果都是可以得到的

关于shabang也是月经讨论了吧?
在unixfaq中有些讨论,英文比较牛逼的话可以当小说一读(俺宁愿去风月大陆看看)
http://www.faqs.org/faqs/unix-faq/faq/part3/section-16.html
作者: waker    时间: 2006-06-12 16:38
记得哪个牛逼的书里还介绍过一个强制使用sh的保险套

  1. #!/bin/sh
  2. export $PATH||/bin/sh $0 argv:g
复制代码

作者: r2007    时间: 2006-06-12 17:15
原帖由 waker 于 2006-6-12 16:32 发表
[waker@proxy dd3]$ echo a=1 b
b
[waker@proxy dd3]$ echo a=1 b
a=1 b

请教:第一种情况是如何实现的?有什么特别的设置吗?
作者: waker    时间: 2006-06-12 17:27

  1.        If  the -k option is set (see the set builtin command below), then all
  2.        parameter assignments are placed in the environment for a command, not
  3.        just those that precede the command name.
复制代码


ksh也有这个选项
作者: r2007    时间: 2006-06-12 17:30
原帖由 waker 于 2006-6-12 17:27 发表
[code]
       If  the -k option is set (see the set builtin command below), then all
       parameter assignments are placed in the environment for a command, not
       just those that precede  ...

确实如此,看来该补课了。
作者: woodie    时间: 2006-06-12 17:50
原帖由 r2007 于 2006-6-12 17:30 发表

确实如此,看来该补课了。

Me too.
作者: Cygnus    时间: 2006-06-13 08:31
原帖由 waker 于 2006-6-12 16:32 发表
在bash中,它们一样不一样取决于你的喜好
[code]


You got it.


原帖由 woodie 于 2006-6-12 15:40 发表
... 但你知道在linux下这通常是/bin/bash的一个符号链接, ...



Another quick question for all of you:

           1. Can you list all links (hard and soft) you know about shells.
           2. What is the purpose of these links?
作者: r2007    时间: 2006-06-13 08:50
原帖由 Cygnus 于 2006-6-13 08:31 发表


You got it.





Another quick question for all of you:

           1. Can you list all links (hard and soft) you know about shells.
           2. What is the purpose of these links?

It seems you think waker got it. I don't think just a "-k" option is the all you call a big topic about the environment.
Can you tell us more about environment variables?
作者: Cygnus    时间: 2006-06-13 09:06
原帖由 r2007 于 2006-6-13 08:50 发表

It seems you think waker got it. I don't think just a "-k" option is the all you call a big topic about the environment.
Can you tell us more about environment variables?


Oh, What I meant to say is "EXECUTION ENVIRONMENT" but "ENVIRONMENT VARIABLES".

Sorry about that.
作者: woodie    时间: 2006-06-13 09:17
原帖由 Cygnus 于 2006-6-13 08:31 发表


You got it.





Another quick question for all of you:

           1. Can you list all links (hard and soft) you know about shells.
           2. What is the purpose of these links?


多半是为为了兼容的目的吧。你有什么新东西告诉大家吗?愿闻其详。
作者: -_-    时间: 2006-06-13 09:58
Cygnus  干脆给大家上一课,谢了。




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