专注各种脚本编程
Baidu
加入收藏夹
本站内容有下面分类知识,欢迎您的到来^_^
shell相关:指令篇 基础篇 脚本欣赏 编程实例 shell问问 shell视频教程 技巧篇 水平测试 E文资料 vi编辑器 高级Bash脚本编程指南
其他:mysql perl c语言 oracle
当前位置:| 主页>高级Bash脚本编程指南>

9.1. 内部变量(1)

百度收藏 QQ搜藏

内建变量
影响Bash脚本行为的变量。

$BASH
Bash二进制程序文件的路径  bash$ echo $BASH
 /bin/bash
 


$BASH_ENV
该环境变量保存一个Bash启动文件路径,当启动一个脚本程序时会去读该环境变量指定的文件。

$BASH_SUBSHELL
一个指示子shell(subshell)等级的变量。它是Bash版本3新加入的。

参考例子 20-1的用法.

$BASH_VERSINFO[n]
这个数组含有6个元素,指示了安装的Bash版本的信息。它和$BASH_VERSION相似,但它们还是有一些小小的不同。

   1 # Bash版本信息:
   2
   3 for n in 0 1 2 3 4 5
   4 do
   5   echo "BASH_VERSINFO[$n] = ${BASH_VERSINFO[$n]}"
   6 done 
   7
   8 # BASH_VERSINFO[0] = 3                      # 主版本号.
   9 # BASH_VERSINFO[1] = 00                     # 次版本号.
  10 # BASH_VERSINFO[2] = 14                     # 补丁级.
  11 # BASH_VERSINFO[3] = 1                      # 编译版本.
  12 # BASH_VERSINFO[4] = release                # 发行状态.
  13 # BASH_VERSINFO[5] = i386-redhat-linux-gnu  # 结构体系
  14                                             # (和变量$MACHTYPE相同).
 


$BASH_VERSION
安装在系统里的Bash版本。

 bash$ echo $BASH_VERSION
 3.00.14(1)-release
       
 


 tcsh% echo $BASH_VERSION
 BASH_VERSION: Undefined variable.
       
 


检查$BASH_VERSION是检测哪个shell在运行的好办法。$SHELL变量不一定能给出正确的答案。

$DIRSTACK
在目录堆栈里面最顶端的值(它受pushd和popd的控制)

这个内建的变量和dirs命令相符,但dirs是给出整个目录堆栈的内容。

$EDITOR
由脚本调用的默认的编辑器,一般是vi或是emacs.

$EUID
有效用户ID

当前用户无论是什么标识都会被认为是这个有效用户ID,这可能依赖于su.

 变量$UID不一定和$EUID相同。
 

$FUNCNAME
当前函数的名字

   1 xyz23 ()
   2 {
   3   echo "$FUNCNAME now executing."  # 打印:xyz23 now executing.
   4 }
   5
   6 xyz23
   7
   8 echo "FUNCNAME = $FUNCNAME"        # FUNCNAME =
   9                                    # 在一个函数体外则没有值输出.
 


$GLOBIGNORE
由通配符(globbing)扩展的一列文件名模式。

$GROUPS
目前用户所属的组

它是当前用户在/etc/passwd文件中记录的所属的组列表(数组)。

 root# echo $GROUPS
 0
 
 
 root# echo ${GROUPS[1]}
 1
 
 
 root# echo ${GROUPS[5]}
 6
       
 


$HOME
用户的家目录,通常是/home/username (参考例子 9-14)

$HOSTNAME
在系统启动时由一个初始化脚本中用hostname命令给系统指派一个名字。然而,gethostname()函数能设置Bash内部变量E$HOSTNAME。参考例子 9-14.

$HOSTTYPE
机器类型

像$MACHTYPE一样标识系统硬件。

 bash$ echo $HOSTTYPE
 i686
 

$IFS
内部字段分隔符

此变量决定Bash如何分割字段,或是解释字符串时的字标识分割。

$IFS默认是空白字符(空格,制表符和新行符),它可以被重新设置。例如,在解释一个以逗号分割的数据文件里可设置成逗号分割。注意$*使用了保存在$IFS中的第一个字符。 参考例子 5-1.

 bash$ echo $IFS | cat -vte
 $
 
 
 bash$ bash -c 'set w x y z; IFS=":-;"; echo "$*"'
 w:x:y:z
       
 


 $IFS处理空白字符和其他的字符不相同。


--------------------------------------------------------------------------------

例子 9-1. $IFS和空白符

   1 #!/bin/bash
   2 # $IFS处理空白字符和其他字符不相同。
   3
   4 output_args_one_per_line()
   5 {
   6   for arg
   7   do echo "[$arg]"
   8   done
   9 }
  10
  11 echo; echo "IFS=\" \""
  12 echo "-------"
  13
  14 IFS=" "
  15 var=" a  b c   "
  16 output_args_one_per_line $var  # output_args_one_per_line函数相当于`echo " a  b c   "`
  17 #
  18 # [a]
  19 # [b]
  20 # [c]
  21
  22
  23 echo; echo "IFS=:"
  24 echo "-----"
  25
  26 IFS=:
  27 var=":a::b:c:::"               # 像上面一样, 但用":"代替了" ".
  28 output_args_one_per_line $var
  29 #
  30 # []
  31 # [a]
  32 # []
  33 # [b]
  34 # [c]
  35 # []
  36 # []
  37 # []
  38
  39 # 在awk中字段分隔符"FS"也有相同的特性.
  40
  41 # 多谢Stephane Chazelas.
  42
  43 echo
  44
  45 exit 0
 

--------------------------------------------------------------------------------

 

(多谢S.C.澄清了问题和举的例子)

参考例子 12-37来看一个关于理解$IFS的教学例子。

$IGNOREEOF
忽略EOF:在退出控制台前有多少文件结尾标识(end-of-files,control-D)会被shell忽略。

$LC_COLLATE
它常常在.bashrc或/etc/profile文件里被设置,它控制文件名扩展和模式匹配的展开顺序。如果设置不当,LC_COLLATE会在文件名通配符(filename globbing)里引起不可预料的结果。

 到Bash2.05版本止,文件名通配符不再区分在方括号里的字符串范围中的大小写了。例如,ls [A-M]*会匹配File1.txt和file1.txt。为了保持方括号区分大小写的惯例,在/etc/profile文件和/或在~/.bashrc文件里由命令export LC_COLLATE=C把LC_COLLATE环境变量设置成C可以达到目的。
 

$LC_CTYPE
这个内部变量控制通配符(globbing)和模式匹配中的字符解释。

$LINENO
这个变量表示在本shell脚本中该变量出现时所在的行数。它只在脚本中它出现时有意义,它一般可用于调试。

   1 # *** 开始调试代码块 ***
   2 last_cmd_arg=$_  # 保存.
   3
   4 echo "At line number $LINENO, variable \"v1\" = $v1"
   5 echo "Last command argument processed = $last_cmd_arg"
   6 # *** 调试代码结束 ***
 


$MACHTYPE
机器类型

识别系统的硬件类型。

 bash$ echo $MACHTYPE
 i686
 

$OLDPWD
上一次工作的目录("OLD-print-working-directory",你上一次进入工作的目录)

$OSTYPE
操作系统类型

 bash$ echo $OSTYPE
 linux
 

$PATH
可执行程序文件的搜索路径。一般有/usr/bin/, /usr/X11R6/bin/, /usr/local/bin,等等。

当给出一个命令时,shell会自动在一个哈希表里搜索由PATH变量里所列的路径寻找该命令程序。$PATH变量被保存在环境变量里,是一串由冒号(:)分割的目录名的列表。通常,系统把此变量的值在/etc/profile文件和/或在~/.bashrc文件中被定义赋值。(参考附录 G).

 bash$ echo $PATH
 /bin:/usr/bin:/usr/local/bin:/usr/X11R6/bin:/sbin:/usr/sbin
 


PATH=${PATH}:/opt/bin 能把/opt/bin目录加到当前现有的目录列表中去。在一个脚本中,它可以用这种方法临时地加一个目录到目录列表中去。当一个脚本退出时,此变量会恢复回原先的$PATH值(一个子进程[比如一个脚本],不能改变父进程的环境变量[比如启动脚本的shell])。

 保存在$PATH目录列表中的当前"工作目录"(./)通常因为会引发安全漏洞而被忽略。
 

$PIPESTATUS
此数组变量保存了最后执行的前台管道的退出状态。相当有趣的是,它不一定和最后执行的命令的退出状态一样。

 bash$ echo $PIPESTATUS
 0
 
 bash$ ls -al | bogus_command
 bash: bogus_command: command not found
 bash$ echo $PIPESTATUS
 141
 
 bash$ ls -al | bogus_command
 bash: bogus_command: command not found
 bash$ echo $?
 127
       
 


$PIPESTATUS数组的成员保存了每一个在管道里执行的命令各自的退出状态。$PIPESTATUS[0]保存了管道里第一个命令的退出状态,$PIPESTATUS[1]保存了管道里第二个命令的退出状态,以此类推。

 在一个登录的shell里$PIPESTATUS变量可能包含了一个无用的0值。(在Bash 3.0以前)

 tcsh% bash
 
 bash$ who | grep nobody | sort
 bash$ echo ${PIPESTATUS[*]}
 0
       
 


如果在一个脚本包含上面的命令,就会产生0 1 0的输出。

多谢Wayne Pollock指出这一点并提供上面的例子。
 

 在某些上下文中,$PIPESTATUS变量会给出一些不可预料的结果。

 bash$ echo $BASH_VERSION
 3.00.14(1)-release
 
 bash$ $ ls | bogus_command | wc
 bash: bogus_command: command not found
 0       0       0
 
 bash$ echo ${PIPESTATUS[@]}
 141 127 0
       
 


Chet Ramey贡献了上面描述ls输出的行为的例子。如果ls写到一个没有进程在读的管道,SIGPIPE信号会杀死它并使它的退出状态为141。否则ls的退出状态为预料之中的0。这个和tr的情况一样。
 

 $PIPESTATUS是一个"挥发性"变量。它需要在管道结束之后并在任何命令干涉之前立即查询。

 bash$ $ ls | bogus_command | wc
 bash: bogus_command: command not found
 0       0       0
 
 bash$ echo ${PIPESTATUS[@]}
 0 127 0
 
 bash$ echo ${PIPESTATUS[@]}
 0
       
 

 

$PPID

一个进程的$PPID变量保存它的父进程的进程ID(pid)。[1]

用这个变量和pidof命令比较。

$PROMPT_COMMAND
这个变量在主提示符前($PS1显示之前)执行它的值里保存的命令。

$PS1
这是主提示符(第一提示符),它能在命令行上看见。

$PS2

副提示符(第二提示符),它在期望有附加的输入时能看见。它显示像">"的提示。

$PS3
第三提示符。它在一个select循环里显示 (参考例子 10-29).

$PS4
第四提示符,它在用-x选项调用一个脚本时的输出的每一行开头显示。它通常显示像"+"的提示。

$PWD
工作目录(即你现在所处的目录)

它类似于内建命令pwd。

   1 #!/bin/bash
   2
   3 E_WRONG_DIRECTORY=73
   4
   5 clear # 清屏.
   6
   7 TargetDirectory=/home/bozo/projects/GreatAmericanNovel
   8
   9 cd $TargetDirectory
  10 echo "Deleting stale files in $TargetDirectory."
  11
  12 if [ "$PWD" != "$TargetDirectory" ]
  13 then    # 防止意外工作在错误的目录中.
  14   echo "Wrong directory!"
  15   echo "In $PWD, rather than $TargetDirectory!"
  16   echo "Bailing out!"
  17   exit $E_WRONG_DIRECTORY
  18 fi 
  19
  20 rm -rf *
  21 rm .[A-Za-z0-9]*    # 删除点文件.
  22 # rm -f .[^.]* ..?*   删除以多个点开始为文件名的文件.
  23 # (shopt -s dotglob; rm -f *)   也可以.
  24 # 多谢S.C.指出来.
  25
  26 # 文件名除了"/"字符外可以包含ASCII值在0 - 255范围的所有字符
  27 # 删除以奇怪的字符开头的文件作为练习由读者实现.
  28
  29 # 如果需要,这儿有多种其他的操作.
  30
  31 echo
  32 echo "Done."
  33 echo "Old files deleted in $TargetDirectory."
  34 echo
  35
  36
  37 exit 0
 


$REPLY
没有变量提供给read命令时的默认变量.这也适用于select命令的目录,但只是提供被选择的变量项目编号而不是变量本身的值.

   1 #!/bin/bash
   2 # reply.sh
   3
   4 # REPLY 是一个read命令的默认变量.
   5
   6 echo
   7 echo -n "What is your favorite vegetable? "
   8 read
   9
  10 echo "Your favorite vegetable is $REPLY."
  11 #  如果没有变量提供且仅在这种情况,REPLY保存"read"命令上次读到的值
  12 #
  13
  14 echo
  15 echo -n "What is your favorite fruit? "
  16 read fruit
  17 echo "Your favorite fruit is $fruit."
  18 echo "but..."
  19 echo "Value of \$REPLY is still $REPLY."
  20 #  $REPLY仍然被设置了它先前的值,
  21 #+ 因为变量$fruit保存了新的"read"读到的值.
  22
  23 echo
  24
  25 exit 0
 


$SECONDS
脚本已运行的秒数.

   1 #!/bin/bash
   2
   3 TIME_LIMIT=10
   4 INTERVAL=1
   5
   6 echo
   7 echo "Hit Control-C to exit before $TIME_LIMIT seconds."
   8 echo
   9
  10 while [ "$SECONDS" -le "$TIME_LIMIT" ]
  11 do
  12   if [ "$SECONDS" -eq 1 ]
  13   then
  14     units=second
  15   else 
  16     units=seconds
  17   fi
  18
  19   echo "This script has been running $SECONDS $units."
  20   #  在一个缓慢或负担过重的机器上,
  21   #+ 脚本可能偶尔会跳过一个计数.
  22   sleep $INTERVAL
  23 done
  24
  25 echo -e "\a"  # Beep!(BB声)
  26
  27 exit 0
 


$SHELLOPTS
已经激活的shell选项列表,它是一个只读变量.  bash$ echo $SHELLOPTS
 braceexpand:hashall:histexpand:monitor:history:interactive-comments:emacs
       
 


$SHLVL
SHELL的嵌套级别.指示了Bash被嵌套了多深.在命令行里,$SHLVL是1,因此在一个脚本里,它是2.

$TMOUT
如果$TMOUT环境变量被设为非零值时间值time,那么经过time这么长的时间后,shell提示符会超时.这将使此shell退出登录.

在Bash版本2.05b以上,可以在脚本中把$TMOUT和read命令结合使用.

   1 # 在Bash版本2.05b以上运行.
   2
   3 TMOUT=3    # 提示输入时间为三秒.
   4
   5 echo "What is your favorite song?"
   6 echo "Quickly now, you only have $TMOUT seconds to answer!"
   7 read song
   8
   9 if [ -z "$song" ]
  10 then
  11   song="(no answer)"
  12   # 默认输出.
  13 fi
  14
  15 echo "Your favorite song is $song."
 


有其他更复杂的在脚本中实现定时输入的方法.另一个方法是设置一个定时循环,超时时给脚本发送一个信号.这个办法要求有一个处理例程来捕捉(trap)(参考例子 29-5)由定时循环产生的信号.(哇哦!)


--------------------------------------------------------------------------------

例子 9-2. 定时输入

   1 #!/bin/bash
   2 # timed-input.sh
   3
   4 # TMOUT=3    在新一点的版本中,这个也可以.
   5
   6
   7 TIMELIMIT=3  # 在这个实例中设置成三秒,但可以设置成其它的值。
   8
   9 PrintAnswer()
  10 {
  11   if [ "$answer" = TIMEOUT ]
  12   then
  13     echo $answer
  14   else       # 不要和上面那个例子弄混了.
  15     echo "Your favorite veggie is $answer"
  16     kill $!  # 不再需要后台运行的TimerOn函数了,杀掉它
  17              # $!变量是上一个在后台运行的作业进程的PID
  18   fi
  19
  20 } 
  21
  22
  23
  24 TimerOn()
  25 {
  26   sleep $TIMELIMIT && kill -s 14 $$ &
  27   # 等3秒,然后给脚本发送sigalarm信号.
  28 } 
  29
  30 Int14Vector()
  31 {
  32   answer="TIMEOUT"
  33   PrintAnswer
  34   exit 14
  35 } 
  36
  37 trap Int14Vector 14   # 设置定时中断(14)能暗中给定时间限制
  38
  39 echo "What is your favorite vegetable "
  40 TimerOn
  41 read answer
  42 PrintAnswer
  43
  44
  45 #  无可否认,这是一个定时输入的复杂的实现,
  46 #+ 然而"read"命令的"-t"选项可以简化这个任务。
  47 #  参考后面的"t-out.sh"脚本
  48
  49 #  如果你想要真正优雅的东西...
  50 #+ 可以考虑用C或C++写你的应用程序,
  51 #+ 使用合适的函数库,例如'alarm'或是'setitimer'.
  52
  53 exit 0
 

--------------------------------------------------------------------------------

另外一种选择是使用stty.


--------------------------------------------------------------------------------

例子 9-3. 再来一个定时输入

   1 #!/bin/bash
   2 # timeout.sh
   3
   4 #  由Stephane Chazelas所写,
   5 #+ 由本书作者作了些修改.
   6
   7 INTERVAL=5                # 超时间隔
   8
   9 timedout_read() {
  10   timeout=$1
  11   varname=$2
  12   old_tty_settings=`stty -g`
  13   stty -icanon min 0 time ${timeout}0
  14   eval read $varname      # 或只是读$varname变量
  15   stty "$old_tty_settings"
  16   # 请参考"stty"的man手册.
  17 }
  18
  19 echo; echo -n "What's your name? Quick! "
  20 timedout_read $INTERVAL your_name
  21
  22 #  这个可能不一定在每种终端都能运行.
  23 #  最大的超时值依赖于终端.
  24 #+ (通常是25.5秒).
  25
  26 echo
  27
  28 if [ ! -z "$your_name" ]  # 如果在超时之前名字被键入...
  29 then
  30   echo "Your name is $your_name."
  31 else
  32   echo "Timed out."
  33 fi
  34
  35 echo
  36
  37 # 这个脚本和"timed-input.sh"脚本的行为稍微有点不同.
  38 # 每一次击键,计时器都会重新设置(即重新开始).
  39
  40 exit 0
 

--------------------------------------------------------------------------------

可能最容易的方法就是使用read命令的-t选项了。


--------------------------------------------------------------------------------

例子 9-4. 定时read

   1 #!/bin/bash
   2 # t-out.sh
   3 # 从"syngin seven"的建议中得到灵感(多谢).
   4
   5
   6 TIMELIMIT=4         # 4秒
   7
   8 read -t $TIMELIMIT variable <&1
   9 #                           ^^^
  10 #  在这儿, Bash 1.x and 2.x需要"<&1",
  11 #  但Bash 3.x则不需要.
  12
  13 echo
  14
  15 if [ -z "$variable" ]  # 值为null?
  16 then
  17   echo "Timed out, variable still unset."
  18 else 
  19   echo "variable = $variable"
  20 fi 
  21
  22 exit 0
 

--------------------------------------------------------------------------------

$UID
用户ID号

这是当前用户的用户标识号,它在/etc/passwd文件中记录。

这是当前用户的真实ID,即使只是临时通过su命令转换成另外一个用户也会显示成转换成的ID号。$UID是个只读变量,不能在命令行或是脚本中更改它,并且它和内建命令id是有些相似的。


--------------------------------------------------------------------------------

上一篇:8.2 数字常量 下一篇:9.1. 内部变量(2)

power by soyo123 2007-2008