Shell脚本攻略02-玩转变量与环境变量

2021-08-16 14:52:32 浏览数 (1)

概述

变量是任何一种编程语言都必不可少的组成部分,用于存放各类数据。

脚本语言通常不需要在使用变量之前声明其类型。只需要直接赋值就可以了。

在Bash中,每一个变量的值都是字符串。

无论你给变量赋值时有没有使用引号,值都会以字符串的形式存储。

有一些特殊的变量会被shell环境和操作系统环境用来存储一些特别的值,这类变量就被称为环境变量。


理论知识

变量采用常见的命名方式进行命名。当应用程序执行时,它接收一组环境变量。

可以使用env命令查看所有与终端相关的环境变量。

代码语言:javascript复制
[root@entel1 ~]# env
HOSTNAME=entel1
TERM=xterm
SHELL=/bin/bash
HISTSIZE=1000
.......省略

对于进程来说,其运行时的环境变量可以使用下面的命令来查看:

代码语言:javascript复制
cat /proc/$PID/environ

其中,将PID设置成相关进程的进程ID(PID总是一个整数)。

如何获取$PID呢?

假设有一个叫做gedit的应用程序正在运行。我们可以使用pgrep命令获得gedit的进程ID:

代码语言:javascript复制
$ pgrep gedit
12501

那么,你就可以通过以下命令获得与该进程相关的环境变量:

代码语言:javascript复制
$ cat /proc/12501/environ
GDM_KEYBOARD_LAYOUT=usGNOME_KEYRING_PID=1560USER=slynuxHOME=/home/slynux
.........

环境变量远不止这些,只是由于对页面篇幅的限制,在这里删除了其他很多环境变量。


pgrep 用法

代码语言:javascript复制
[root@entel2 ~]# pgrep --help
pgrep: invalid option -- '-'
Usage: pgrep [-flvx] [-d DELIM] [-n|-o] [-P PPIDLIST] [-g PGRPLIST] [-s SIDLIST]
    [-u EUIDLIST] [-U UIDLIST] [-G GIDLIST] [-t TERMLIST] [PATTERN]

  1. pgrep程序检查在系统的中活动进程,报告进程属性匹配命令行上指定条件的进程的ID。
  2. pgrep 查找的是程序名,而不包其参数
  3. pgrep相当于 ps -eo pid,cmd |awk '{print 1,2}'|grep keyword
  1. pgrep 通常和pkill在一起用,在指定条件下kill进程

如果pgrep不是很熟悉,可以用ps -ef

代码语言:javascript复制
[root@entel2 ~]# ps -ef|less
UID        PID  PPID  C STIME TTY          TIME CMD
root         1     0  0 Oct25 ?        00:00:15 /sbin/init
root         2     0  0 Oct25 ?        00:00:00 [kthreadd]

取 第二列 PID


举例:

代码语言:javascript复制
[root@entel2 ~]# pgrep java
4309
代码语言:javascript复制
[root@entel2 ~]# cat /proc/4309/environ 
APP_XMX_SIZE_zmcDaemon=128MHOSTNAME=tbpr_app1SHELL=/bin/bashTERM=vt100HISTSIZE=1000SSH_CLIENT=172.25.246.66 42562 22OLDPWD=/home/settWeb/zmc_agent/daemonSSH_TTY=/dev/pts/9USER=settWebJRE_HOME=/usr/lib/jvm/jdk1.7.0_80/jreLS_COLORS=rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01:mi=01;05;37;41:su=37;41:sg=30;43:ca=30;41:tw=30;42:ow=34;42:st=37;44:ex=01;32:*.tar=01;31:*.tgz=01;31:*.arj=01;31:*.taz=01;31:*.lzh=01;31:*.lzma=01;31:*.tlz=01;31:*.txz=01;31:*.zip=01;31:*.z=01;31:*.Z=01;31:*.dz=01;31:*.gz=01;31:*.lz=01;31:*.xz=01;31:*.bz2=01;31:*.......省略

上面的命令返回一个包含环境变量以及对应变量值的列表。每一个变量以name=value的形式来描述,彼此之间由null字符()分隔。

看起来是不是很懵逼,一团乱糟糟的很难以查看。

如果你将替换成n,那么就可以将输出重新格式化,使得每一行显示一组“变量=值”。替换可以使用tr命令来实现:

代码语言:javascript复制
[root@entel2 ~]# cat /proc/4309/environ |tr '' 'n'
APP_XMX_SIZE_zmcDaemon=128M
HOSTNAME=tbpr_app1
SHELL=/bin/bash
TERM=vt100
HISTSIZE=1000
SSH_CLIENT=172.25.246.66 42562 22
OLDPWD=/home/settWeb/zmc_agent/daemon
SSH_TTY=/dev/pts/9
USER=settWeb
JRE_HOME=/usr/lib/jvm/jdk1.7.0_80/jre
LS_COLORS=rs=0:di=01;34:ln=01;36:mh=00:pi=40;....省略

对变量和环境变量进行赋值

变量赋值

变量可以通过以下方式进行赋值:

代码语言:javascript复制
var=value

var是变量名, value是赋给变量的值。

如果value不包含任何空白字符(例如空格),那么它就不需要使用引号进行引用,否则必须使用单引号或双引号。

注意, var = value不同于var=value。把var=value写成var = value是一个常见的错误,但前者是赋值操作,后者则是相等操作。

在变量名之前加上$前缀就可以打印出变量的内容:

代码语言:javascript复制
var="value" #给变量var赋值
echo $var

或者

代码语言:javascript复制
echo ${var}

输出value

栗子:

代码语言:javascript复制
[root@entel1 ~]# cat -b var.sh 
     1  #!/bin/bash
     2  var="xiaogongjiang bash"
     3  var2=xiaogongjaing
     4  var3 = wrong_use

     5  echo $var
     6  echo ${var2}
     7  echo $var3
[root@entel1 ~]# ./var.sh 
./var.sh: line 4: var3: command not found
xiaogongjiang bash
xiaogongjaing

[root@entel1 ~]# 

可以在printf或echo命令的双引号中引用变量值。

代码语言:javascript复制
[root@entel1 Templates]# cat variables.sh 
#!/bin/bash
lesson=shell
count=100
echo "we have ${count}  $lesson  ,output by echo"
#换行 n
printf "we have $count $lesson output by printf n" 
[root@entel1 Templates]# ./variables.sh 
we have 100  shell  ,output by echo
we have 100 shell output by printf 
[root@entel1 Templates]# 

注意:

使用单引号时,变量不会被扩展(expand),将依照原样显示。这意味着:

代码语言:javascript复制
$ echo '$var' will print $var

但如果变量var已经定义过,那么 echo "

代码语言:javascript复制
[root@entel1 Templates]# cat test.sh 
#!/bin/bash
var=xiaogongjiang
echo '$var' will print $var
echo '$var2' will print $var2
[root@entel1 Templates]# ./test.sh --观察var2的输出为空,因为未定义
$var will print xiaogongjiang
$var2 will print
[root@entel1 Templates]# 

环境变量赋值

环境变量是未在当前进程中定义,而从父进程中继承而来的变量。

例如环境变量,HTTP_PROXY,它定义了互联网连接应该使用哪个代理服务器。

该环境变量通常被设置成:

代码语言:javascript复制
HTTP_PROXY=192.168.1.23:3128
export HTTP_PROXY

export命令用来设置环境变量。至此之后,从当前shell脚本执行的任何应用程序都会继承这个变量。

我们可以按照自己的需要,在执行的应用程序或者shell脚本中导出特定的变量。

在默认情况下,有很多标准环境变量可供shell使用。 PATH就是其中之一。

代码语言:javascript复制
[root@entel1 Templates]# echo $PATH
/usr/lib64/qt-3.3/bin:/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/root/bin

在给出所要执行的命令后, shell会自动在PATH环境变量所包含的目录列表中(各目录路径之间以冒号分隔)查找对应的可执行文件。

PATH通常定义在/etc/environment或/etc/profile或~/.bashrc中。

PATH中添加一条新路径

如果需要在PATH中添加一条新路径,可以使用:

代码语言:javascript复制
$ export PATH="$PATH:/home/user/bin"

也可以使用

代码语言:javascript复制
$ PATH="$PATH:/home/user/bin"
$ export PATH

这样,我们就将/home/user/bin添加到了PATH中。

还有一些众所周知的环境变量: HOME、 PWD、 USER、 UID、 SHELL等


Addition

下面我们再多看些有关标准变量和环境变量的技巧。

获得字符串长度${#}

代码语言:javascript复制
[root@entel1 Templates]# cat test.sh 
#!/bin/bash
var=xiaogongjiang
echo "var's length  ${#var}"
[root@entel1 Templates]# ./test.sh 
var's length  13

识别当前所使用的shell

可以用下面的方法获知当前使用的是哪种shell:

代码语言:javascript复制
echo $SHELL

也可以用

代码语言:javascript复制
echo $0

检查是否为超级用户

UID是一个重要的环境变量,可以用于检查当前脚本是以超级用户还是以普通用户的身份运行的。

root用户的UID是0。

例如:

代码语言:javascript复制
[root@entel1 Templates]# cat test.sh 
#!/bin/bash
#检测用户是否是root用户
#[] 两侧记得带空格 [$UID -ne 0] 会报错
if [ $UID -ne  0 ]; then 
    echo Non root user 
else 
    echo Root user 
fi
[root@entel1 Templates]# ./test.sh 
Root user

修改Bash提示字符串( username@hostname:~$)

http://blog.csdn.net/yangshangwei/article/details/52928009

当我们打开终端或是运行shell时,会看到类似于user@hostname:/home/$的提示字符串。

不同GNU/Linux发布版中的提示及颜色略有不同。我们可以利用PS1环境变量来定制提示文本。

默认的shell提示文本是在文件~/.bashrc中的某一行设置的。

 可以使用如下命令列出设置变量PS1的那一行:

代码语言:javascript复制
$ cat ~/.bashrc | grep PS1
PS1='${debian_chroot: ($debian_chroot)}u@h:w$ '

 如果要设置一个定制的提示字符串,可以输入:

代码语言:javascript复制
$ PS1="PROMPT>"
PROMPT> Type commands here #提示字符串已经改变

 我们可以利用类似e[1;31的特定转义序列来设置彩色的提示字符串

还有一些特殊的字符可以扩展成系统参数。

例如: u可以扩展为用户名, h可以扩展为主机名,而w可以扩展为当前工作目录。


使用函数添加环境变量

环境变量通常用于存储路径列表,这些路径用于搜索可执行文件、库文件等。

例如PATH、LD_LIBRARY_PATH,它们通常看起来像这样:

代码语言:javascript复制
PATH=/usr/bin;/bin
LD_LIBRARY_PATH=/usr/lib;/lib

这意味着只要shell需要运行二进制可执行文件时,它会首先查找/usr/bin,然后是/bin。

场景举例

当你必须使用源代码编译生成程序并将其安装到某个特定路径中时,有项极其常见的任务就是将该程序的bin目录加入PATH环境变量。

假设我们要将myapp安装到/opt/myapp,它的二进制文件在bin目录中,库文件在lib目录中。

实现方法

代码语言:javascript复制
export PATH=/opt/myapp/bin:$PATH
export LD_LIBRARY_PATH=/opt/myapp/lib;$LD_LIBRARY_PATH

PATH和LD_LIBRARY_PATH现在看起来应该像这样:

代码语言:javascript复制
PATH=/opt/myapp/bin:/usr/bin:/bin
LD_LIBRARY_PATH=/opt/myapp/lib:/usr/lib;/lib

不过我们可以把下面的函数加入.bashrc-,让一切变得更轻松些:

代码语言:javascript复制
prepend() { [ -d "$2" ] && eval $1="$2':'$$1" && export $1; }

像下面这样来使用该函数:

代码语言:javascript复制
prepend PATH /opt/myapp/bin
prepend LD_LIBRARY_PATH /opt/myapp/lib

函数分析

我们定义了名为prepend()的函数,它首先检查该函数第二个参数所指定的目录是否存在。

如果存在, eval表达式将第一个参数所指定的变量值设置成第二个参数的值加上“ :”(路径分隔符),随后再跟上首个参数的原始值。


完善函数

不过,有一点需要留意。在进行添加时,如果变量为空,则会在末尾留下一个“ :”。

要解决这个问题,可以将该函数再进行一些修改

代码语言:javascript复制
prepend() { [ -d "$2" ] && eval $1="$2${$1: ':'$$1}" && export $1 ; }

在这个函数中,我们引入了一种shell参数扩展的形式:

代码语言:javascript复制
${parameter: expression}

如果parameter有值且不为空,则使用expression的值。

通过这次修改,在追加环境变量时,当且仅当旧值存在,才会增加。


0 人点赞