Local Variables
shell 局部变量只在当前 shell 进程可见。
1 | vartest |
1 | x=100 # local var |
subshell
为什么 shell 程序不能看到或修改变量呢?因为他们是在 subshell 中执行的,subshell 是一个新的 shell。来执行相关脚本。
新的 shell 运行时都有自己的环境,有自己的 local var
,子 shell 也不能修改父 shell 的变量和环境。
Exported Variables
在 Shell 编程中,export
命令用于将一个变量导出为环境变量,使其在当前 Shell 进程及其子进程中都可以访问。
export variables
,variables 是要导出的变量列表。于在导出命令之后执行的任何子 shell,导出变量的值将传递到子 shell。
subshell 不能修改导出的变量,即对导出变量修改不会反映到父 shell。事实上,这些导出变量一旦传递到 subshell 后就会成为 subshell 的 local var。
这些导出的变量可能是从 login shell 导出的,或者是从子 shell 中新导出的(对父 shell 不可见)。
总结
- Any variable that is not exported is a local variable whose existence will not be known to subshells.
- Exported variables and their values are copied into a subshell’s environment, where they may be accessed and changed. However, such changes have no effect on the variables in the parent shell.
- Exported variables retain this characteristic not only for directly spawned subshells, but also for subshells spawned by those subshells (and so on down the line).
- A variable can be exported any time before or after it is assigned a value but takes on its value at the moment of export; subsequent changes aren’t tracked.
export -p
查看当前 shell 导出的变量。
PS1 PS2 HOME
PATH
不要把 .
放第一个,容易引起攻击,为什么 shell 脚本要用 ./
执行,就和这个变量有关。
Current Directory PWD
在一个 shell 脚本中使用 cd 会影响当前 shell 吗?并不会,当前目前也是一个环境变量,子 shell 是没办法修改夫 shell 的环境变量的。
CDPATH
CDPATH
变量的工作方式与 PATH
变量类似:它指定每当执行 cd 命令时 shell 将搜索的目录列表。仅当指定目录未给出完整路径名且 CDPATH
不为空时,才会执行此搜索。
默认未设置,这个变量很好玩。
subshells
1 | DB=/user/data |
现在知道子 shell 无法改变父 shell 中的变量和当前路径,假设我有一个文件中很多变量要设置,怎么办,简单执行脚本不会起作用。
. Command
1 | . file |
为了解决这个问题,有个内置的命令 .
,在当前 shell 中执行文件中的内容,就像在当前命令行直接输入一样。
命令使用 PATH
来找到相关的文件。
1 | db |
./db
运行。
执行导出后,调用 /bin/sh
,从这时起,这个新的 shell 会处理所有用户输入的命令,直到用户键入 exit
或使用 Ctrl+d
序列。退出后,控制权返回到 db,而 db 又将控制权返回到你的登录 shell。
The exec Command
在上个例子中,最后执行 /bin/sh
会导致子 shell(./db) 一直等待 /bin/sh
的完成,但是完成后什么都不做,就退出,很显然不太好。
Instead of having db wait around for the subshell to finish, you can use the exec command to replace the current program (db) with the new one (/bin/sh).
exec program
,被 exec
的程序会替代当前进程,所以会减少一个进程空等的情况,能让系统运行更快,被执行的进程启动的速度也会变快,直接利用父进程的数据。
exec /bin/sh
,执行之后,db 进程会被 /bin/sh
替代。这意味着现在在 exec
之后执行任何命令都是毫无意义的,因为它们永远不会被执行。
exec
还可用于关闭标准输入并使用要读取的任何文件重新打开它。exec < infile
,随后从标准输入读取数据的任何命令都将从 infile
读取。
exec > report
,关闭标准数据并重新打开,然后写入文件,影响后续的命令。
注意上面的例子中,exec
并不是用来启动一个新的程序,仅仅用来重定向输入输出。
exec < /dev/tty
恢复标准输入。
The (…) and {…;} constructs
有时需要把多个命令放在一起先后执行,也不需要使用管道。可以使用括号和花括号来实现,前者会让命令组合在子 shell 中执行,后者在当前 shell 执行。
1 | x=50 |
如果花括号里面的命令都在一行中,那么左右花括号需要空格,最后一个命令需要有分号,括号不需要这样。
1 | (sort 2016data -0 2016data; plotdata 2016data) & # 后台执行这些命令 |
同样管道和重定向依然可以使用。
1 | { echo ".ls 2"; cat memo; } | nroff -Tlp | lp |
向 subshell 传递变量的另一种方式
1 | DBHOME=/uxn2/data DBID=452 dbrun # 变量会传递给子 shell,而且父 shell 中没有这些变量 |
.profile
在打印命令提示符前,shell 还会读取两个文件 /etc/profile
和 .profile
,前者通常由管理员配置,后者我们自己可以配置。
可以在这个文件设置很多好玩的东西。