Bash 小括号()和大括号{}的区别(附带实例)
介绍小括号和大括号的区别之前,我们先来回顾父 shell 和子 shell 的概念,接下来将会从执行脚本和变量赋值的角度来考虑父子 shell 带来的影响。
用户在登录系统时,系统会根据用户的 shell 类型运行一个 shell,以便用户执行命令,root 用户和普通用户在默认情况下的 shell 类型都是 bash。因此,通常在查看进程或查看进程树的时候都能看到 bash 进程的存在,例如:
我们之前使用 bash 命令执行脚本,这也相当于开启一个 bash 进程并在进程内执行脚本中的语句,执行效果如下:
切换终端查看:
我们可以说 PID 为 1159 的进程是 PID 为 1052 的进程的子进程,反过来也可以说 PID 为 1052 的进程是 PID 为 1159 的父进程。又因为这两个进程都是 bash 进程(通过 pstree 命令可以得知),所以可以说两个进程不仅可以被称为父子进程,还可以被称为父子 shell。
在父子 shell 的基础上,接着解释小括号和大括号的区别。如果用于一串命令的执行,那么小括号和大括号的主要区别在于:
接下来逐一解释说明上述区别:
其实,日常在使用绝对路径(包括相对路径)和 bash 执行脚本时,就是开启子 shell 来运行的,这样一来在脚本中定义的值不会影响当前 shell。
其实,在执行变量赋值时,如果使用的是小括号,那么变量赋值只在子 shell 中生效,一旦命令执行结束,回到父 shell 中,变量赋值就会消失;而如果使用的是大括号,那么变量赋值在当前 shell 中执行,在命令执行结束后,修改依然会生效。
用户在登录系统时,系统会根据用户的 shell 类型运行一个 shell,以便用户执行命令,root 用户和普通用户在默认情况下的 shell 类型都是 bash。因此,通常在查看进程或查看进程树的时候都能看到 bash 进程的存在,例如:
[root@localhost ~]# pstree | grep bash |-sshd---sshd---sshd--sshd--bash--+-grep
我们之前使用 bash 命令执行脚本,这也相当于开启一个 bash 进程并在进程内执行脚本中的语句,执行效果如下:
[root@localhost ~]# cat /root/bash1.sh #!/bin/bash echo "$$$" > /root/bash.pid sleep 300 #脚本运行后会输出当前进程PID到/root/bash.pid文件中 #脚本会执行sleep睡眠300秒,在这300秒内我们切换终端查看进程 [root@localhost ~]# bash /root/bash1.sh #使用bash的方式执行bash1.sh脚本
切换终端查看:
[root@localhost ~]# pstree -p | grep bash | sshd(895)+-sshd(1047)--sshd(1051)--sshd(1162)--bash(1052)--bash(1159)--sleep(1160) | `-sshd(1081)--sshd(1085)--bash(1086)+-+-grep(1162) #在另一终端中执行pstree命令,查找包含bash关键字的行 #会在命令结果中看到bash进程及进程PID [root@localhost ~]# cat /root/bash.pid 1159 #查看bash.pid文件会得到/root/bash1.sh脚本的进程PID [root@localhost ~]# ps -ef | grep 1159 root 1159 1052 0 14:50 pts/1 00:00:00 bash /root/bash1.sh root 1 160 1159 0 14:50 pts/1 00:00:00 sleep 300 #使用ps -ef命令查找PID为1159的进程及父子进程 #在ps -ef命令返回结果中,第二列为当前进程PID,第三列为父进程PID,出现在最后一列的是当前进程执行的指令我们在 ps -ef 命令返回结果中会看到,PID 为 1159 的是我们执行 /root/bash1.sh 脚本的进程,它的父进程 PID 为 1052。这样一来,我们便可以将当前在 ps -ef 命令中看到的父子进程关系和 pstree 命令中的进程关系对应起来了。
我们可以说 PID 为 1159 的进程是 PID 为 1052 的进程的子进程,反过来也可以说 PID 为 1052 的进程是 PID 为 1159 的父进程。又因为这两个进程都是 bash 进程(通过 pstree 命令可以得知),所以可以说两个进程不仅可以被称为父子进程,还可以被称为父子 shell。
在父子 shell 的基础上,接着解释小括号和大括号的区别。如果用于一串命令的执行,那么小括号和大括号的主要区别在于:
- 在执行一串命令时,需要重新开启一个子 shell 来执行;
- {} 在执行一串命令时,在当前 shell 中执行;
- () 和 {} 都是把一串命令放在括号里面,并且命令之间用分号(;)隔开;
- () 的最后一条命令可以不用分号;
- {} 的最后一条命令要用分号;
- {} 的第一条命令和左括号之间必须有一个空格;
- () 中的各命令不必和括号之间有空格;
- 在 () 和 {} 中,括号里面的某条命令的重定向只影响该命令,但括号外的重定向会影响括号里的所有命令。
接下来逐一解释说明上述区别:
[root@localhost ~]# name=hb #在命令行中定义变量name的值为hb [root@localhost ~]# (name=rs; echo $name) rs #在小括号中定义name的值并进行输出 [root@localhost ~]# echo $name hb #在当前shell中查看变量name的值在上面的命令中,我们分别在当前命令行中和小括号中对变量 name 进行了两次赋值。但因为小括号中的赋值是在子 shell 中完成的,所以不会影响当前命令行中赋值的 name=hb,不会出现变量覆盖的情况。
其实,日常在使用绝对路径(包括相对路径)和 bash 执行脚本时,就是开启子 shell 来运行的,这样一来在脚本中定义的值不会影响当前 shell。
[root@localhost ~]# name=hb # 在命令行中定义变量 name 的值为 hb [root@localhost ~]# { name=rs;echo $name; } rs # 在大括号中定义变量 name 的值并进行输出 [root@localhost ~]# echo $name rs # 在当前 shell 中查看变量 name 的值同样是对变量 name 进行了两次赋值,但是使用大括号进行第二次赋值时会发现,赋值最终会覆盖在当前 shell 中对变量 name 的赋值。
其实,在执行变量赋值时,如果使用的是小括号,那么变量赋值只在子 shell 中生效,一旦命令执行结束,回到父 shell 中,变量赋值就会消失;而如果使用的是大括号,那么变量赋值在当前 shell 中执行,在命令执行结束后,修改依然会生效。