组命令,就是将多个命令划分为一组,或者看成一个整体。
用法区别
Shell 组命令的写法有两种:
{ command1; command2;. . .; }
(command1; command2;. . . )
两种写法的重要不同: 由 {}
包围的组命令在当前 Shell 进程中执行,由 ()
包围的组命令会创建一个子Shell,所有命令都会在这个子 Shell 中执行。
在子 Shell 中执行意味着,运行环境被复制给了一个新的 shell 进程,当这个子 Shell 退出时,新的进程也会被销毁,环境副本也会消失,所以在子 Shell 环境中的任何更改都会消失(包括给变量赋值)。因此,在大多数情况下,除非脚本要求一个子 Shell,否则 使用 {}
比使用 ()
更受欢迎,并且 {}
的进行速度更快,占用的内存更少。
举栗 将多条命令的输出重定向到out.txt文件
1.普通模式
2.使用组命令
{ ls -l ;echo “test432”;cat test .txt; }>out.txt
(ls -l ;echo “test432”;cat test .txt)>out.txt
组命令与管道结合
(ls -l ;echo “test432”;cat ../test.txt)|wc -l
子进程的概念是由父进程的概念引申而来的。在 Linux 系统中,系统运行的应用程序几乎都是从 init(pid为 1 的进程)进程派生而来的,所有这些应用程序都可以视为 init 进程的子进程,而 init 则为它们的父进程。
Shell 脚本是从上至下、从左至右依次执行的,即执行完一个命令之后再执行下一个。 如果在 Shell 脚本中遇到子脚本(即脚本嵌套,但是必须以新进程的方式运行)或者外部命令,就会向系统内核申请创建一个新的进程,以便在该进程中执行子脚本或者外部命令,这个新的进程就是子进程。子进程执行完毕后才能回到父进程,才能继续执行父脚本中后续的命令及语句。
使用 pstree -p
命令就可以看到 init 及系统中其他进程的进程树信息(包括 pid):
systemd(1)─┬─ModemManager(796)─┬─{ModemManager}(821)
│ └─{ModemManager}(882)
├─NetworkManager(975)─┬─{NetworkManager}(1061)
│ └─{NetworkManager}(1077)
├─abrt-watch-log(774)
├─abrt-watch-log(776)
├─abrtd(773)
├─accounts-daemon(806)─┬─{accounts-daemon}(839)
│ └─{accounts-daemon}(883)
├─alsactl(768)
├─at-spi-bus-laun(1954)─┬─dbus-daemon(1958)───{dbus-daemon}(1960)
│ ├─{at-spi-bus-laun}(1955)
│ ├─{at-spi-bus-laun}(1957)
│ └─{at-spi-bus-laun}(1959)
├─at-spi2-registr(1962)───{at-spi2-registr}(1965)
├─atd(842)
├─auditd(739)─┬─audispd(753)─┬─sedispatch(757)
│ │ └─{audispd}(759)
│ └─{auditd}(752)
创建子进程的方式说明
-
第一种只使用 fork() 函数,子进程和父进程几乎是一模一样的,父进程中的函数、变量(全局变量、局部变量)、文件描述符、别名等在子进程中仍然有效。我们将这种子进程称为 *子 Shell(sub shell)
-
第二种使用 fork() 和 exec() 函数,即使用 fork()创建子进程后立即调用 exec() 函数加载新的可执行文件,而不使用从父进程继承来的一切,子进程和父进程之间除了硬生生地维持一种”父子关系”外,再也没有任何联系了,它们就是两个完全不同的程序。
举栗:
在 ~/bin 目录下有两个可执行文件分别叫 a.out 和 b.out。现在运行 a.out,就会产生一个进程,比如叫做 A。在进程 A 中我又调用 fork() 函数创建了一个进程 B,那么 B 就是 A 的子进程,此时它们是一模一样的。但是,我调用 fork() 后立即又调用 exec() 去加载 b.out,这可就坏事了,B 进程中的一切(包括代码、数据、堆栈等)都会被销毁,然后再根据 b.out 重建建立一切。这样一折腾,B 进程除了 ID 没有变,其它的都变了,再也没有属于 A 的东西了。
子 Shell 虽然能使用父 Shell 的的一切,但是如果子 Shell 对数据做了修改,比如修改了全局变量,这种修改也只能停留在子 Shell,无法传递给父 Shell。 不管是子进程还是子 Shell,都是”传子不传父”。
子 Shell 才是真正继承了父进程的一切,这才像”一个模子刻出来的”;普通子进程和父进程是完全不同的两个程序,只是维持着父子关系而已。
echo $$输出当前进程ID,echo $PPID输出父shell ID
输出当前进程与父进程ID
echo $$;echo $PPID
34451
34450
子进程形式输出进程ID
子进程
bash
echo $$;echo $PPID
exit
52886
34451
在普通的子进程中,$ 被展开为子进程的 ID
组命令形式输出进程ID
子shell
(echo $$;echo $PPID)
34451
34450
子shell和父shell中的ID是一样的
这是因为$ 变量在子 Shell 中无效!Base 官方文档说,在普通的子进程中,$ 确实被展开为子进程的 ID;但是在 子 Shell 中,$ 却被展开成父进程的 ID
管道形式输出进程ID
子shell
echo “test” | { echo $$;echo $PPID; }
34451
34450
进程替换形式输出进程ID
read <
Original: https://www.cnblogs.com/saolv/p/13197549.html
Author: 扫驴
Title: shell相关知识1
原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/529809/
转载文章受原作者版权保护。转载请注明原作者出处!