Shell 变量分局部变量、环境变量和shell特殊变量。
在shell 中每一个变量的值都是字符串,无论你给变量赋值时有没有使用引号,值都会以字符串的形式存储,在默认情况下不会区分变量类型,即使你将整数和小数赋值给变量,它们也会被视为字符串。
Shell 变量的命名规范和大部分编程语言都一样:
- 变量名由数字、字母、下划线组成;
- 必须以字母或者下划线开头;
- 不能使用 Shell 里的关键字(通过 help 命令可以查看保留关键字);
1.变量基本操作
变量定义:
zhang@zhang-virtual-machine:~$ username='zhang'
zhang@zhang-virtual-machine:~$ hostname=localhost
变量使用:
使用时变量需要加$
zhang@zhang-virtual-machine:~$ echo $username
zhang
zhang@zhang-virtual-machine:~$ echo $hostname
localhost
zhang@zhang-virtual-machine:~$ echo ${username}
zhang
zhang@zhang-virtual-machine:~$ echo ${hostname}
localhost
zhang@zhang-virtual-machine:~$
变量名外面的花括号是可选的,加花括号是为了帮助解释器识别变量的边界,建议一般在容易产生歧义的地方加;比如$cmd_file ,就有歧义,它可能表示$cmd这个变量连上_file ,也可能表示cmd_file这是个整体的变量,如果只想表示$cmd 然后连上_file ,可以这样写${cmd}_file。
变量的修改
已定义的变量,可以被重新赋值,如:
zhang@zhang-virtual-machine:~$ username=root
zhang@zhang-virtual-machine:~$ echo $username
root
zhang@zhang-virtual-machine:~$
删除变量
使用 unset 命令可以删除变量。如:
zhang@zhang-virtual-machine:~$ unset username
zhang@zhang-virtual-machine:~$ unset hostname
zhang@zhang-virtual-machine:~$ echo $username
zhang@zhang-virtual-machine:~$ echo $hostname
zhang@zhang-virtual-machine:~$
只读变量
使用 readonly 命令可以将变量定义为只读变量,只读变量的值不能被改变。
zhang@zhang-virtual-machine:~$ name="zhang guoping"
zhang@zhang-virtual-machine:~$ echo $name
zhang guoping
zhang@zhang-virtual-machine:~$ readonly name
zhang@zhang-virtual-machine:~$ name="xiaomu xizi"
-bash: name: readonly variable
zhang@zhang-virtual-machine:~$
自定义变量的建议 :
1.纯数字(不带空格),定义方式可以不加引号(单或双)
2.没特殊情况,字符串一般用双引号定义,特别是多个字符串中间有空格时
3.变量内容需要原样输出 时,要用单引号('')
2.单引号和双引号的区别
如果你学过php 很容易理解二者的区别:单引号包裹的值不解析,双引号包裹的值会解析。
例如:
zhang@zhang-virtual-machine:~$ machineBrand='Dell'
zhang@zhang-virtual-machine:~$ NickName="xiao mu xi zi"
zhang@zhang-virtual-machine:~$ echo '$machineBrand'
$machineBrand
zhang@zhang-virtual-machine:~$ echo '$NickName'
$NickName
zhang@zhang-virtual-machine:~$ echo "$machineBrand"
Dell
zhang@zhang-virtual-machine:~$ echo "$NickName"
xiao mu xi zi
zhang@zhang-virtual-machine:~$
但也有特例,在awk 中:
zhang@zhang-virtual-machine:~$ flag=123
zhang@zhang-virtual-machine:~$ awk 'BEGIN {print "$flag"}'
$flag
zhang@zhang-virtual-machine:~$ awk 'BEGIN {print '$flag'}'
123
zhang@zhang-virtual-machine:~$
3.命令定义为变量
两种方式:
1. 使用反引号``(键盘上tab 键上一个键)
2. 使用 () ,推荐使用小括号
zhang@zhang-virtual-machine:~$ showMemory=$(free -m)
zhang@zhang-virtual-machine:~$ echo $showMemory
total used free shared buffers cached Mem: 975 845 129 2 68 290 -/+ buffers/cache: 485 489 Swap: 1021 69 952
zhang@zhang-virtual-machine:~$ showHDAvailableCapacity=`df -h`
zhang@zhang-virtual-machine:~$ echo $showHDAvailableCapacity
Filesystem Size Used Avail Use% Mounted on udev 477M 4.0K 477M 1% /dev tmpfs 98M 1.5M 97M 2% /run /dev/sda1 29G 6.2G 21G 23% / none 4.0K 0 4.0K 0% /sys/fs/cgroup none 5.0M 0 5.0M 0% /run/lock none 488M 152K 488M 1% /run/shm none 100M 32K 100M 1% /run/user
zhang@zhang-virtual-machine:~$
4. 特殊变量
变量 | 含义 |
---|---|
$0 | 当前脚本的文件名 -- 常用 |
$n | 传递给脚本或函数的参数。n 是一个数字,表示第几个参数。例如,第一个参数是$1,第二个参数是$2。 |
$# | 传递给脚本或函数的参数个数。 -- 常用 |
$* | 传递给脚本或函数的所有参数。 |
$@ | 传递给脚本或函数的所有参数。被双引号(" ")包含时,与 $* 稍有不同。 |
$? | 上个命令的退出状态,或函数的返回值。--- 常用 |
$$ | 当前Shell进程ID。对于 Shell 脚本,就是这些脚本所在的进程ID。--- 常用 |
示例:
1 #! /bin/bash
2
3 echo '$0 的值为:'$0
4 echo '$n 以此为:' $1 ,$2, $3, $4
5 echo '参数数量为:' $#
6 echo '传递给脚本的参数有:'$*
7 echo '传递给脚本的参数有: '$@
8 echo '上个命令的退出状态:'$?
9 echo '当前shell进程ID 为:'$$
~
zhang@zhang-virtual-machine:~$ /bin/bash 2.sh "hello world" linux shell
$0 的值为:2.sh
$n 以此为: hello world ,linux, shell,
参数数量为: 3
传递给脚本的参数有:hello world linux shell
传递给脚本的参数有: hello world linux shell
上个命令的退出状态:0
当前shell进程ID 为:4750
zhang@zhang-virtual-machine:~$
$* 和 $@ 都表示传递给函数或脚本的所有参数,不被双引号(" ")包含时,都以"$1" "$2" … "$n" 的形式输出所有参数。
但是当它们被双引号(" ")包含时,"$*" 会将所有的参数作为一个整体,以"$1 $2 … $n"的形式输出所有参数;"$@" 会将各个参数分开,以"$1" "$2" … "$n" 的形式输出所有参数。
如果不熟悉set 命令的话,可以看下面的栗子:
zhang@zhang:~/Code/Shell$ cat distinguish.sh
#! /bin/bash
echo 'loop in $* and without double quote'
for var in $*
do
echo "$var"
done
echo 'loop in $@ and without double quote'
for var in $@
do
echo "$var"
done
echo 'loop in $* and within double quote'
for var in "$*"
do
echo "$var"
done
echo 'loop in $@ and without double quote'
for var in "$@"
do
echo "$var"
done
zhang@zhang:~/Code/Shell$
zhang@zhang:~/Code/Shell$ /bin/bash distinguish.sh hello world linux shell
loop in $* and without double quote
hello
world
linux
shell
loop in $@ and without double quote
hello
world
linux
shell
loop in $* and within double quote
hello world linux shell
loop in $@ and without double quote
hello
world
linux
shell
zhang@zhang:~/Code/Shell$
5.bash 内部变量
有些内部命令在目录列表是看不见的,他们由shell 本身提供,常用的内部命令有:echo ,eval ,exec ,export ,readonly,read,shift ,exit 和 点(.)
echo 变量名表
将变量名表指定的变量显示到标准输出。
eval args
读入参数args ,并将它们组合成一个新的命令,然后执行。
zhang@zhang:~$ eval ls -a
. .gconf .vscode
.. .gnome .Xauthority
.atom .gnupg .xinputrc
.bash_history .ICEauthority .xsession-errors
.bash_logout .lantern .xsession-errors.old
.bashrc .local 公共的
.byteexec .mozilla 模板
.cache .mysql_history 视频
Code .pki 图片
.compiz .presage 文档
.config .profile 下载
Data .sogouinput 音乐
.dbus .sudo_as_admin_successful 桌面
.dmrc .thunderbird
examples.desktop .viminfo
zhang@zhang:~$ eval echo $username
zhang
zhang@zhang:~$
exec 命令参数
当shell 执行到exec 语句时,不会去创建新的子进程,而是转去执行指定的命令,当指定的命令执行完时,该进程(也就是最初的shell)就终止了,所以shell程序中的exec 后面的语句讲不再被执行。
zhang@zhang:~/Code/Shell$ cat 2.sh
#! /bin/bash
echo "begin start shell .."
exec ls -a
echo "end shell.."
zhang@zhang:~/Code/Shell$ /bin/bash 2.sh
begin start shell ..
. .. 1.sh 2.sh exec_method.sh fifo foreach.sh if_1.sh if.sh while.sh
zhang@zhang:~/Code/Shell$
可以看到,exec 后面shell 语句都没有再往下执行下去!!!
export 变量名=value
shell 可以用export 把它的变量向下带入子shell,从而让子进程继承父进程中的环境变量。但子shell 不能用export 把它的变量向上带入父shell .
zhang@zhang:~/Code/Shell$ export pcos="ubuntu 16.04"
zhang@zhang:~/Code/Shell$ echo $pcos
ubuntu 16.04
zhang@zhang:~/Code/Shell$ vim export.sh
zhang@zhang:~/Code/Shell$ /bin/bash export.sh
ubuntu 16.04
the shell ending...
zhang@zhang:~/Code/Shell$ cat export.sh
#/ bin/bash
echo $pcos
echo "the shell ending..."
zhang@zhang:~/Code/Shell$
export 在系统shell 中用的很频繁,下面看下栗子:
zhang@zhang:/etc/init.d$ pwd
/etc/init.d
zhang@zhang:/etc/init.d$ grep "export" ./*
./brltty:[ -n "$LANG" ] && export LANG
./console-setup: export LANG
./cron: [ -n "$value" ] && eval export $var=$value
./cups: export TZ
./keyboard-setup: export LANG
./lightdm: export LANG LANGUAGE
./mountall.sh: export LANG
./mysql:export PATH
./rc:export PATH
./rc:export runlevel previous
./rc:export VERBOSE
./rsync:export PATH="${PATH:+$PATH:}/usr/sbin:/sbin"
./whoopsie:DAEMON=/bin/sh -c 'export CRASH_DB_URL=https://daisy.ubuntu.com; exec whoopsie -f'
zhang@zhang:/etc/init.d$
用 vim 打开console-setup 这个文件:
这个LANG 是在全局变量,在这里引用到了。
有关grep 和 vim 的用法,可以参考我之前的文章。
realdonly 变量名
用readonly 声明该变量为只读的
还可显示所有只读变量。
zhang@zhang:/etc/init.d$ curDate
curDate:未找到命令
zhang@zhang:/etc/init.d$ curDate=$(date)
zhang@zhang:/etc/init.d$ echo curDate
curDate
zhang@zhang:/etc/init.d$ echo $curDate
2018年 10月 10日 星期三 00:36:46 CST
zhang@zhang:/etc/init.d$ readonly $curDate
bash: readonly: `2018年': 不是有效的标识符
bash: readonly: `10月': 不是有效的标识符
bash: readonly: `10日': 不是有效的标识符
bash: readonly: `星期三': 不是有效的标识符
bash: readonly: `00:36:46': 不是有效的标识符
zhang@zhang:/etc/init.d$ readonly curDate
zhang@zhang:/etc/init.d$ curDate=$(date)
bash: curDate: 只读变量
zhang@zhang:/etc/init.d$ unset curDate
bash: unset: curDate: 无法取消设定: 只读 variable
zhang@zhang:/etc/init.d$ readonly
declare -r BASHOPTS="checkwinsize:cmdhist:complete_fullquote:expand_aliases:extglob:extquote:force_fignore:histappend:interactive_comments:progcomp:promptvars:sourcepath"
declare -ir BASHPID
declare -r BASH_COMPLETION_COMPAT_DIR="/etc/bash_completion.d"
declare -ar BASH_VERSINFO='([0]="4" [1]="3" [2]="48" [3]="1" [4]="release" [5]="x86_64-pc-linux-gnu")'
declare -r CST
declare -ir EUID="1000"
declare -ir PPID="3036"
declare -r SHELLOPTS="braceexpand:emacs:hashall:histexpand:history:interactive-comments:monitor"
declare -ir UID="1000"
declare -r curDate="2018年 10月 10日 星期三 00:36:46 CST"
zhang@zhang:/etc/init.d$
由上面的执行结果可以得出,当变量被设置为readonly 的时候,其不能被修改及删除!!!
read 变量名表
从标准输入读入字符串并传给指定变量
可以在函数中用local 变量名的方式申明局部变量
zhang@zhang:~$ read username
zhang
zhang@zhang:~$ echo $username
zhang
zhang@zhang:~$
shift 语句
shift 语句按如下方式重新命名所有的位置参数位置,即$2成为$1,$3 成为$2...在程序中每使用一次shift 语句,都使所有的位置参数依次向左移动一个位置,并使位置参数$#减1,直到减为0 为止。
zhang@zhang:~/Code/Shell$ cat shift.sh
#! /bin/bash
shift
echo "begin start execute shell...."
echo '$0 the value is :'$0
echo '$1 the value is :'$1
echo '$2 the value is :'$2
echo "end shell ...."
zhang@zhang:~/Code/Shell$ /bin/bash shift.sh hello world linux
begin start execute shell....
$0 the value is :shift.sh
$1 the value is :world
$2 the value is :linux
end shell ....
zhang@zhang:~/Code/Shell$
关于shift ,可以参考mysql 脚本,下面试mysql 中使用shift 的示例:
110 [ $# -ge 1 ] && shift
111
112
113 other_args="$*" # uncommon, but needed when called from an RPM upgrade action
114 # Expected: "--skip-networking --skip-grant-tables"
115 # They are not checked here, intentionally, as it is the resposibility
116 # of the "spec" file author to give correct arguments only.
117
118 case `echo "testing\c"`,`echo -n testing` in
119 *c*,-n*) echo_n= echo_c= ;;
120 *c*,*) echo_n=-n echo_c= ;;
121 *) echo_n= echo_c='\c' ;;
122 esac
123
124 parse_server_arguments() {
125 for arg do
126 case "$arg" in
127 --basedir=*) basedir=`echo "$arg" | sed -e 's/^[^=]*=//'`
128 bindir="$basedir/bin"
129 if test -z "$datadir_set"; then
130 datadir="$basedir/data"
/shift