0%

Shell env

Local Variables

shell 局部变量只在当前 shell 进程可见。

1
2
# vartest
echo :$x:
1
2
3
4
5
6
7
8
9
10
$ x=100  # local var
$ ./vartest # subshell can not see x
::
$ cat vartest2
x=50
echo :$x:
$ ./vattest2
:50:
$ echo $x
100

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 不可见)。

总结

  1. Any variable that is not exported is a local variable whose existence will not be known to subshells.
  2. 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.
  3. 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).
  4. 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
2
DB=/user/data
bin=/dev/bin

现在知道子 shell 无法改变父 shell 中的变量和当前路径,假设我有一个文件中很多变量要设置,怎么办,简单执行脚本不会起作用。

. Command

1
. file

为了解决这个问题,有个内置的命令 .,在当前 shell 中执行文件中的内容,就像在当前命令行直接输入一样。

命令使用 PATH 来找到相关的文件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# db
# Set up and export variables related to the base
#
HOME=/usr2/data
BIN=$HOME/bin
RPTS=$HOME/rpts
DATA=$HOME/rawdata
PATH=$PATH$BIN
CDPATH=:$HOME:$RPTS

PS1="DB: "

export HOME BIN RPTS DATA PATH CDPATH PS1

/bin/sh

./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
2
3
4
5
$ x=50
$ (x=100) # execute in a subshell
$ {x=100;} # execute in current shell
$ (cd somewhere; ls)
$ { cd somewhere; } # 会改变当前目录

如果花括号里面的命令都在一行中,那么左右花括号需要空格,最后一个命令需要有分号,括号不需要这样。

1
$ (sort 2016data -0 2016data; plotdata 2016data) & # 后台执行这些命令

同样管道和重定向依然可以使用。

1
2
$ { echo ".ls 2"; cat memo; } | nroff -Tlp | lp
$ { prog1; prog2; prog3; } 2> errors # 三个命令的标准错误都写到文件中。

向 subshell 传递变量的另一种方式

1
2
$ DBHOME=/uxn2/data DBID=452 dbrun # 变量会传递给子 shell,而且父 shell 中没有这些变量
$ (DBHOME=/uxn2/data; DBID=452; export DBHOME DBID; dbrun) # 其实就相当于这种形式

.profile

在打印命令提示符前,shell 还会读取两个文件 /etc/profile.profile,前者通常由管理员配置,后者我们自己可以配置。

可以在这个文件设置很多好玩的东西。