深入理解linux命令


我们在linux shell命令行下输入的命令,最终会被计算机执行,这中间发生了什么?背后的原理是什么?这是很多初学者在开始学习linux的时候最困扰的问题。 想要深入理解命令执行背后的机制,需要对计算机原理、linux内核有深入的了解。

一.命令类型type

回到顶部

type可以查看命令的类型:

              [peter@study ~]$ type [-tpa] commandName
            
选项与参数:
-t: 命令的类型,type将commandName显示为以下几种类型:
file: 表示外部命令;
alias: 表示该命令为命令别名;
builtin: 表示该命令为bash的内置命令;
-p: 如果后面接的commandName为外部命令,会显示命令的完整文件名;
-a: 在PATH变量定义的路径中,将所有包含commandName的命令都列出来,包括alias;
不加任何选项和参数时,type会显示出commandName是外置命令还是内置命令:
              [peter@study ~]$ type ls
              ls is aliased to `ls --color=auto'
              [peter@study ~]$ type cd
              cd is a shell builtin
              [peter@study ~]$ type pwd
              pwd is a shell builtin
              [peter@study ~]$ type cp
              cp is /bin/cp
            
可以看到ls是ls --color=auto的别名,cd和pwd都是shell builtin,cp是一个文件。

命令可能是命令别名、内置命令、bash函数、外置命令(可执行程序文件)。
shell将命令解释为以下几种类型:

1.命令别名alias,内置或者外置命令的别名;
2.shell函数;
3.shell内置命令,shell自身的内置功能;
4.shell外置命令,外置命令都有对应的二进制可执行文件或者shell脚本文件,存储在磁盘文件系统路径中。可直接在命令行输入可执行文件的相对路径或者绝对路径来执行命令;
bash执行的命令分为内置命令和外置命令,内置命令属于shell功能的一部分,例如cd;
外置命令都有对应的可执行程序文件,存储在磁盘文件系统路径中。包括二进制可执行程序文件、shell脚本等,例如cat。
执行外置命令就是将命令对应的可执行程序文件从磁盘加载到内存中执行。
命令除了内置和外置之分外,也有可能是命令别名。
bash会根据命令的名字按照一定的顺序判断命令的类型并执行:
1. 如果命令是绝对路径或者相对路径,那么直接执行路径指定的文件;
2. 如果没有提供命令路径,那么先判断该命令是否是命令别名(alias),如果是命令别名,按照命令别名执行;
3. 如果不是命令别名,则判断该命令是否是bash的内置(builtin)命令,如果是内置命令,则按照内置命令执行;
4. 如果不是内置命令,shell会在$PATH环境变量指定的路径中寻找与命令匹配的可执行程序文件,只要找到就停止搜索并立即执行命令的可执行程序文件, 也就是将可执行程序文件从磁盘加载到内存中执行。如果有多个同名文件,shell只执行找到的第一个可执行文件.

二.bash也是命令

回到顶部
我们在linux命令行下输入命令的时候,其实是在跟一个叫shell的程序打交道,所以我们也经常将命令行称为shell命令行。 shell有很多不同的版本,主流发行版默认的shell基本都是bash,所以经常将shell称为bash shell。 简单来讲,shell就是一个命令解释器。shell提供命令行供用户输入,然后解释并执行用户输入的命令。 本质上shell和用户输入的命令都是运行在linux内核之上的应用程序,都是内核要调度执行的进程。这些进程在运行之前,都对应着一个可执行程序文件。
shell进程一般是在用户登陆后由init->login进程启动,可以通过查看/etc/passwd文件中每条记录的最后一个字段查看每个用户在启动后执行的shell可执行文件路径, 也可以通过SHELL环境变量查看当前启动的shell。
            [peter@study ~]$ cat /etc/passwd
            root:x:0:0:root:/root:/bin/bash
            ...
            peter:x:1000:1000:peter,1920302,13861828579,13861828579:/home/peter:/bin/bash
            ...
            [peter@study ~]$ echo $SHELL
            /bin/bash
          
可以再在shell中手动启动一个shell,只需输入bash即可:
            #通过ps查看系统中有一个bash进程,这个bash进程就是正在与用户交互的进程:
            [peter@study ~]$ ps aux | grep bash
            peter 26862 0.2 0.0 24604 5372 pts/0 Ss 23:05 0:00 bash
            peter 26872 0.0 0.0 16184 1052 pts/0 S+ 23:05 0:00 grep --color=auto bash
            #此时输入bash,就会进入到一个新的bash shell环境中:
            [peter@study ~]$ bash
            #再次通过ps查看系统中已经有两个bash进程:
            [peter@study ~]$ ps aux | grep bash
            peter 26862 0.1 0.0 24604 5584 pts/0 Ss 23:05 0:00 bash
            peter 26874 0.2 0.0 24612 5596 pts/0 S 23:05 0:00 bash
            peter 26885 0.0 0.0 16184 1056 pts/0 S+ 23:05 0:00 grep --color=auto bash
            #输入exit退出当前bash环境,并返回到上一个bash shell环境:
            [peter@study ~]$ exit
            exit
            #此时已返回到最初的bash shell的环境中,再次通过ps查看,系统中还是之前的bash进程:
            [peter@study ~]$ ps aux | grep bash
            peter 26862 0.0 0.0 24604 5584 pts/0 Ss 23:05 0:00 bash
            peter 27003 0.0 0.0 16184 1016 pts/0 S+ 23:12 0:00 grep --color=auto bash
          
分别通过type和whereis查看bash:
            [peter@study ~]$ type bash
            bash is hashed (/bin/bash)
            [peter@study ~]$ whereis bash
            bash: /bin/bash /etc/bash.bashrc /usr/share/man/man1/bash.1.gz
          
可以看到bash也对应着一个可执行程序文件,也是一个外置命令。
linux系统下会有一个hash表,当你刚开机时这个hash表为空:
            [peter@study ~]$ hash
            hash: hash table empty
          
每当执行过一条命令时,hash表会记录下这条命令的路径,就相当于缓存。 第一次执行命令shell解释器默认的会从PATH路径下寻找该命令的路径,当第二次使用该命令时,shell解释器首先会查看hash表,没有该命令才会去PATH路径下寻找。
            [peter@study ~]$ hash
            hits command
            3 /bin/bash
            1 /bin/cat
          

三.内置命令和外置命令

回到顶部

bash的内置命令是bash的内部功能。例如cd、umask等。 直接在命令行下输入help可以查看bash提供的所有内置命令。 我们在使用man bash查看bash的帮助说明的时候,也会看到一堆命令的说明,这些命令都是bash的内置命令。
可以通过type命令查看命令是bash内置命令还是外置命令!
用type命令查看ls命令和cd命令:

            [peter@initroot ~]$ type ls
            ls is aliased to `ls --color=auto'
            [peter@initroot ~]$ type -t ls
            alias
            [peter@initroot ~]$ type -p ls
            [peter@initroot ~]$ type -a ls
            ls is aliased to `ls --color=auto'
            ls is /bin/ls
            [peter@initroot ~]$ 
            [peter@initroot ~]$ type cd
            cd is a shell builtin
          
我们看到ls其实是一条命令别名,而cd是shell的内置命令。
透过 type 这个命令我们可以知道每个命令是否为bash的内置命令。 此外,由于利用 type 搜寻后 面的名称时,如果无法找到与命令匹配的可执行文件后面接的名称并不能以执行文件的状态被找到, 那么该名称是不会被显示出来的。
也就是说, type 主要在找出可执行文件而不是一般文件名! type类似which命令的用途!

whereis查看外置命令可执行程序文件路径 对于外置命令,bash会创建一个子进程,在子进程中将命令对应的可执行程序文件从磁盘加载到内存中执行。 那么这个可执行文件存放在磁盘的哪个位置呢?
在使用type查看命令的类型时,如果是外置命令,type会列出命令的可执行程序文件路径。除了type, whereis也可以查看命令对应的可执行程序文件的路径,例如查看ls命令的可执行程序文件路径:

              [peter@study ~]$ whereis ls
              ls: /bin/ls /usr/share/man/man1/ls.1.gz
            
可以看到ls命令对应的可执行程序文件为/bin/ls,相关的手册文档为/usr/share/man/man1/ls.1.gz。
如果将/bin/ls文件删掉,然后再执行ls命令,那么shell就会告诉你找不到这条命令:
              [peter@study ~]$ mv /bin/ls /bin/ls_bak
              [peter@study ~]$ ls
              ls: command not found
            
查看cd命令的可执行文件路径:
              [peter@study ~]$ whereis cd
              cd:
            
whereis的输出是:“cd:”,这是因为cd是shell的一个内置命令,不以可执行文件的形式存在,所以没有路径名。
所以咱们平时敲的命令主要分为两种,一种是以可执行文件存在的外部命令,一种是没有可执行文件的内置命令。 这里的外部和内置是从shell的角度说的。

那么上面提到的环境变量又是什么东西呢?可执行文件是什么?他从哪里来?是怎么被加载到内核中变成一个可调度的进程的呢? shell是怎么创建子进程的呢?进程又是什么鬼?
shell是被init->login进程启动,init(systemd)进程又是什么?linux的启动流程是怎么样的?进程是如何被内核调度的?
说到这里,好像问题越来越多了,本来只是一个命令的问题,现在又引出这么多问题。技术就是这样,知道的越多,不知道的也就越多。
在initroot linux学习网站,这些问题您都可以找到答案。

type命令显示命令的类型信息,比如是内部命令还是外部命令:

              [peter@study ~]$ type cd
                cd is a shell builtin
              [peter@study ~]$ type ls
                ls is aliased to `ls --color=auto'
              [peter@study ~]$ type tree
                tree is hashed (/usr/bin/tree)
              

1.用type命令查看命令类别

回到顶部
当然我们也可以通过type -a追踪命令的搜索顺序,例如我们查看ls命令:
            [peter@study ~]$ type -a ls
            ls is aliased to `ls --color=auto'
            ls is /bin/ls
          
设置echo为echo -n的命令别名,然后观察echo执行的顺序:
            [peter@study ~]$ alias echo='echo -n'
            [peter@study ~]$ type -a echo
            echo is aliased to `echo -n'
            echo is a shell builtin
            echo is /bin/echo
          
通过上面的输出我们发现,我们设置了echo为echo -n的命令别名,所以在命令行输入echo后,bash实际执行的是echo -n,而echo既是shell的内置命令,也有对应的二进制可执行文件. bash会先检查是否是内置命令,如果是内置命令,bash就按照内置命令执行. 如果不是内置命令,bash就在PATH命令中寻找匹配的二进制可执行文件并执行.有上面的输出可知,bash最终执行的是bash自己的内置命令.

1.内置命令和外置命令

回到顶部
刚才提到linux的命令有两种存在形式:一种是shell内置命令,另一种是shell外置命令,那么到底什么是内置命令什么是外置命令呢?
我们前面提到linux命令其实就是计算机软件程序, 以可执行程序文件的形式存放在磁盘的某个目录中。
那么如何知道一个命令的可执行文件存放在哪个目录下呢? 可以通过whereis命令查看命令的可执行文件存放目录:
            [peter@study ~]$ whereis ls
            ls: /bin/ls /usr/share/man/man1/ls.1.gz
            [peter@study ~]$ whereis rm
            rm: /bin/rm /usr/share/man/man1/rm.1.gz
          
whereis命令不仅列出命令的可执行文件,同时也列出了命令的帮助文档manpage的路径。 我们把以可执行程序文件形式存在的linux命令称为linux shell外置命令。 而不以单独可执行文件形式存在的命令称为linux shell内置命令。由于内置命令没有可执行程序文件, 所以通过whereis命令是查不到内置命令的可执行文件存放路径的。 例如我们知道cd是内置命令,我们用whereis cd命令查看结果如下:
            [peter@study ~]$ whereis cd
            cd:
          
并没有列出cd的可执行文件路径,因为cd是内置命令,根本就不存在cd的可执行程序文件。 但是我们知道命令就是计算机程序软件,肯定需要存放在可执行程序文件中,那么内置命令到底存在哪里了呢? 其实内置命令存放在shell的可执行程序文件中了,有上面我们知道bash shell的可执行程序文件为/bin/bash, 也就是所有的内置命令都存放在/bin/bash中,相当于bash的内置功能。 这也就是为什么我们说内置命令和外置命令的时候,前面总是喜欢加个shell的原因.因为内置和外置是相对shell的角度来说的。
如何看出一条命令是内置命令还是外置命令呢?可通过type命令查看命令的类型,例如:
            [peter@study ~]$ type cd
            cd is a shell builtin
            [peter@study ~]$ type pwd
            pwd is a shell builtin
            [peter@study ~]$ type rm
            rm is /bin/rm
            [peter@study ~]$ type ls
            ls is aliased to `ls --color=auto'
          
通过命令的输出可以看出,cd和pwd命令都是shell内置命令, 如果输出中没有'shell builtin',则表明命令是一条外置命令。 例如,rm命令是一条外置命令,可执行文件名为/bin/rm,ls命令也是一条外置命令,是`ls --color=auto'的命令别名。
如何查看shell有哪些内置命令呢?help命令可以打印出shell的所有内置命令。

四.$PATH环境变量

回到顶部
我们在前面讲过,每个外置命令都有对应的可执行程序文件,这些可执行程序文件可能分布在不同的磁盘目录中。 对于shell内置命令,因为内置命令本身就是shell功能的一部分,所以可以很顺利的执行内置命令的功能。 但是对于shell外置命令来说,因为外置命令的可执行程序文件分布在不同的磁盘目录中,那么当在shell命令行下执行一条外置命令的时候,shell是怎么找到命令相对应的可执行程序文件呢? 这就要借助$PATH环境变量了。我们通过echo命令打印$PATH环境变量的值:
            [peter@study ~]$ echo $PATH
            /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games
          
通过上面的输出可以看出,$PATH环境变量的值为一堆路径的集合,路径之间用:分隔。 在执行命令的时候,shell按顺序到这些目录中寻找和命令名匹配的可执行程序文件名,然后将找到的第一个匹配的可执行程序文件加载到内存中执行。 如果没有找到,就会给出提示:"command not found".
更多关于shell变量和环境变量的内容可参考:
linux shell变量与环境变量

initroot编辑整理,转载请注明www.initroot.com

100次点赞 100次阅读