为什么“ ls”需要执行一个单独的过程?


14

为什么ls需要一个单独的过程来执行?我知道为什么这样的命令cd不能通过派生机制执行的原因,但是如果ls不执行派生而执行会有什么危害吗?


1
尽管这ls是一个外部程序,但echo *or echo * .*(取决于shell选项)在不分叉的情况下很好地列出了文件。
gerrit 2014年

这是更好的:printf的“%S \ N” *
科斯塔

Shell多样性说明:tcsh具有一个ls-F类似于的内置函数ls -F。它在那里提高效率。您总是会得到-F一个通常是个好主意。如果指定任何其他选项,它将跳至外部命令。

Answers:


18

答案或多或少ls是一个外部可执行文件。您可以通过运行查看其位置type -p ls

那么为什么不ls内置到外壳中呢?好吧,为什么呢?Shell的工作不是包含所有可用命令,而是提供一个能够运行它们的环境。某些现代shell具有echoprintf和其类似的内建插件,从技术上讲,它们不一定是内建插件,但出于性能方面的考虑,当它们重复运行(主要是在紧密循环中)时才这样做。如果不使它们内置,则外壳将不得不为每次对其调用进行分叉并执行一个新进程,这可能会非常慢。

至少,运行ls外部可执行文件需要运行exec系列系统调用之一。您可以不用分叉执行此操作,但是它将替换您正在使用的主shell。通过执行以下操作,您可以查看在该情况下发生的情况:

exec ls; echo "this never gets printed"

由于您的Shell的过程映像已替换,因此执行此操作后将无法再访问当前Shell。为了使外壳程序在运行ls之后能够继续运行,必须将命令内置到外壳程序中。

分叉允许替换不是您的主Shell的进程,这意味着您之后可以继续运行Shell。


1
我认为他是在问为什么ls(1)不是shell的内置功能,有人需要解释为什么不同的供应商为ls(1)提供不同的选择以及如何从文件系统中查询不同的内容,等等。将其“内置”在外壳中的麻烦和挫折。
llua 2014年

@llua我补充有关的一些信息,以及外的情况下echoprintf
克里斯向下

并非总是很清楚为什么有些东西是内置的,而有些不是。例如,为什么cd没有外部可执行文件?
Faheem Mitha 2014年

@FaheemMitha有外部cd的POSIX兼容的操作系统的可执行文件(见这里)。但是,如果要在当前进程中实际使用chdir(),则需要将其内置到shell中。
克里斯·唐纳

成为ls外部用户的习惯已成为习惯,但也可以在shell中实现。请参阅busybox。

15

猛砸参考手册状态:

内置命令对于实现使用单独的实用工具无法获得或不方便的功能是必需的。

也就是说,shell设计为仅在以下情况下包括内置命令:

  1. POSIX标准要求
  2. 需要访问外壳本身的命令,例如内置的作业控制
  3. 以内置形式实现的命令非常简单,与操作系统无关,并且提高了执行效率,例如printf

ls命令不符合以上任何要求。

但是,这里没有编程约束可以防止ls被实现为内置函数,该约束 在与bash解释器相同的过程中执行。Shell内置命令被实施的设计原因是:

  1. Shell应该与文件系统分开-内置命令不应取决于任何文件系统或外围设备的正确操作
  2. 可能与文件系统类型或操作系统相关的命令应为单独的可执行文件
  3. 您可能希望通过管道传递的命令应该是一个单独的过程
  4. 您可能希望在后台运行的命令应该是一个单独的可执行文件
  5. 可以在单独的可执行文件中更好地实现具有大量可能参数的命令
  6. 无论使用哪种类型的shell(bash,csh,tsh等),应具有相同输出的命令应该是独立的可执行文件

关于第一个原因-您希望外壳尽可能独立且具有弹性。您不希望外壳卡在ls“不响应仍在尝试”的NFS挂载上。

关于第二个原因-在许多情况下,您可能希望为使用Busybox或其他ls实现不同的文件系统的系统使用外壳程序。甚至在具有不同ls实现的OS中使用相同的Shell源。

关于第三个原因-对于像find . -type d | xargs ls -lad这样的表达式,很难或不可能ls在与shell解释器相同的过程中实现。

关于第四个原因-有些ls命令可能需要很长时间才能完成。您可能希望外壳在此期间继续执行其他操作。


注意:有关类似问题,请参见沃伦·杨Warren Young)的这篇有用的帖子


如果错过了使用单独命令执行输出的便利性,以及将shell原语通过管道传递到单独的可执行文件中所需要的全部编程,则可能会错过它的便利性。
Bruce Ediger 2014年

@BruceEdiger:很高兴收到尊敬的BE的评论。谢谢!我相信原因3涵盖了您的评论,不是吗?
乔纳森·本·阿夫拉罕

1
我一直在考虑以下问题:如果外壳程序必须处理外部流程的管道并将内部命令(如假设的)的输出传输到外部流程,那么外壳本身的源代码将变得多么复杂ls。可以做到,但是会很复杂。
Bruce Ediger 2014年

1
恐怕您5分中的大多数(如果不是全部)都没有意义。1:ls(希望)与文件系统实现无关。这取决于内核为标准库和应用程序提供一致的接口。2:ls可能比shell更少依赖OS。3:shell绝对允许在管道中使用内置函数。4:shell绝对允许内置插件在后台运行。5:那是很主观的
jlliagre 2014年

1
@ JonathanBen-Avraham @BruceEdiger外壳程序是否已经处理带有子外壳程序的内置管箱?例如bash输出alias | grep ls。输入cat /etc/passwd | while read a; do echo "$a"; done
Matt

2

ls不需要单独的过程。实际上,很少有命令需要单独的过程:只有那些需要更改特权的命令。

通常,仅当需要将命令实现为内置命令时,shell才将命令实现为内置命令。命令状aliascdexitexportjobs,...需要读取或修改壳体内部的一些状态,因此不能单独的程序。没有这样要求的命令可以是单独的命令。这样,可以从任何外壳程序或其他程序中调用它们。

查看bash中的内置程序列表,只有以下内置程序可以实现为单独的命令。对于其中的一些功能,会有些损失。

  • command-但是在PATH可能无法正确设置并且脚本正在将其command用作设置一部分的情况下,它将失去其用处。
  • echo —这是提高效率的内置功能。
  • help —它可以使用单独的数据库,但是将帮助文本嵌入到shell可执行文件中的优点是使shell可执行文件具有独立性。
  • kill —内置有两个优点:它除了可以识别进程ID外,还可以识别作业指定,并且即使没有足够的资源来启动单独的进程,也可以使用它。
  • printf—出于与相同的原因echo,并且还支持-v将输出放入变量的选项。
  • pwd —内置函数提供了逻辑当前目录跟踪的附加功能(保持符号链接完整而不是扩展它们)。
  • test—它是提高效率的内置工具(bash /dev/fd/…对某些操作系统上调用的文件也起到了神奇作用)。

一些外壳程序提供了大量的附加内置函数。有一个sash,它是一个外壳,旨在作为紧急修复的独立二进制文件(某些外部命令可能不可用时)。它有一个内置的ls,所谓的-ls,以及其他工具,如-grep-tar。Sash的内置函数比完整的命令要少。Zsh在其zsh / files模块中提供了一些类似的内置函数。它没有ls,但是通配符扩展(echo *zstat可以起到类似的作用。


2

我认为人们在这里缺少的是lsLinux上GNU 程序的复杂性。将Debian系统上和的可执行文件大小lsbashdash外壳程序进行比较,我们发现它很大:

graeme@graeme:~$ ls -lh /bin/{ls,bash,dash}
-rwxr-xr-x 1 root root 953K Mar 30  2013 /bin/bash
-rwxr-xr-x 1 root root 115K Dec 25 20:25 /bin/dash
-rwxr-xr-x 1 root root 108K Jul 20 22:52 /bin/ls

包含一个ls与GNU版本一样的功能,bash将使可执行文件的大小增加10%。它的大小几乎与整个dash外壳相同!

之所以选择大多数shell内置程序,是因为它们以外部可执行文件无法与外壳程序集成的方式(问题指出cd,但另一个示例是kill与bash作业控件集成的bash版本),或者因为它们是实现起来非常简单的命令,给出较大的速度与大小的收益(true并且false就这么简单)。

GNU的ls开发周期很长,可以实现一些选项来定制显示结果的方式。默认情况下使用内置的ls可能会失去此功能或显着增加外壳的复杂性和尺寸。


1

cd内置于外壳中,ls是一个单独的程序,您将在看到/bin/ls


0

这可以满足您的需求:

printf "%s\n" *

您也可以将文件名存储在数组中:

files=(`printf "%s\n" *`)  #items are separated by whitespace
echo ${#files[*]} files
for index in ${!a[*]}
do printf "%d: %s\n" $index ${a[$index]};
done

但是它并不关心名称中的空格,
这会传递给变量并关心空格:

printf "%s\n" * | while read a; do echo $a; done
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.