目录

Linux Cheat Sheet - Bash 变量的全面整理

变量的取用

变量在取用时,前面需要加上钱字号 “$”。

1
2
3
4
5
6
7
echo $variable

echo ${variable}

echo $PATH

echo ${PATH}

变量的设置与修改

1
2
3
4
myname=Dawn	# 设置变量 myname 的内容为 Dawn
echo ${myname}

unset	myname # 取消 myname 的设置

bash 中假如 echo 一个没有设置内容的变量时,它会显示空的值。但是在某些 shell 中,去 echo 这种变量可能会出现错误。

变量的设置与修改需要注意以下几点:

  • 变量名称只能是英文、字母、数字,开头不能是数字。

  • 等号两边不能直接接空白字符,如 myname = VBird

  • 变量内容若有空白字符可使用双引号 "",或单引号 '' 将变量内容结合起来。

    • 双引号内的特殊字符如 $ 等将会保有原本的特性,如 var="lang is $LANG" ,echo 则会得到 lang is en_US.UTF-8。
    • 单引号的特殊字符就会被当成一般字符。
  • 可使用 \ 将特殊符号([Enter]、$、\、单引号、双引号、反单引号)转换为一般字符。

  • 在一串指令中,还需要借由其他额外的指令所提供的的信息时,可以使用反单引号或者 $。比如 version=$(uname -r),那么 echo $version 的时候就会得到 20.1.0。

    1
    2
    3
    4
    5
    6
    7
    
    # 进入当前 kernel 的模块目录
    cd /lib/modules/`uname -r`/kenrel
    cd /lib/modules/$(uname -r)/kernel	# 这种方式更好
      
    # 上述这种方式其实作了两次动作
    # - 先进行反单引号内的动作,得到结果
    # - 再将结果带入原指令
    
  • 若要为变量扩展内容时,则可用 $变量名称${变量}累加内容,如 PATH=${PATH}:/home/bin

  • 通常大写字符为系统默认变量或者环境变量,自行设置变量可以使用小写字符,方便判断。一般来说,不论是否是环境变量,只要跟正在使用的 shell 的操作接口有关的变量都会被设置为大写字符。

当你有一个常去的工作目录,比如 /home/dawnguo/workspace,怎么使得 cd 到这个目录的动作简化呢?我们可以设置一个变量 work=/home/dawnguo/workspace(这个变量可以在 bash 的配置文件 .bashrc 中直接指定),接下去使用 cd $work 就好了。

变量的有效范围

Bash 中的变量也有范围,默认情况下用户在一个 shell 中定义的变量是不会传到子进程中去的。若希望该变量在其他子进程也可以执行,那么则需要 export 来使变量变成环境变量,如 export PATH。目前这个 shell 的情况下,去启用一个新的 shell 时,新的 shell 就是子进程。假如没有使用 export,那么父进程的自定义变量是无法在子进程中使用的。但是使用 export 将其变为环境变量之后就可以在子进程中使用了。

变量从键盘读取

要想读取来自键盘输入的变量,就需要 read 这个指令。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
read: 读取来自键盘输入的变量内容

-p:后面可以接提示字符!
-t:后面可以接等待的“秒数!”这个比较有趣~不会一直等待使用者啦!

# 将键盘输入的内容作为 demo 这个变量的内容
$ read demo

# 30s 之内输入,如果 30s 之内没有任何输入,那么会自动略过。假如 30s 之内有输入,那么输入的内容将会作为变量 named 的内容。
$ read -p "Please enter your name: " -t 30 named

声明变量

变量类型默认为“字符串”,假如想要声明变量为其他类型,需要使用 declare 或者 typeset(两个是一样的功能)。如果 declare 后面没有接任何参数,那么 bash 就会将所有的变量名称和内容通通列出来,就好像 set 一样。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
declare [-aixr] variable

-a :将后面名为 variable 的变量定义成为阵列 (array) 类型
-i :将后面名为 variable 的变量定义成为整数数字 (integer) 类型
-x :用法与 export 一样,就是将后面的 variable 变成环境变量;
-r :将变量设置成为 readonly 类型,该变量不可被更改内容,也不能 unset,只能退出登陆才能恢复哦
-p :可以单独列出变量的类型
+x :将 - 变成 + 可以取消 x 动作

# echo 的话会显示设置的内容,而不会进行相加
$ sum=100+30+50

# 这种情况下会进行相加
$ declare -i sum=100+30+50

需要注意的是 bash 中的数值运算,默认最多仅能到达整数形态,所以 1/3 结果为 0。

array 变量类型

在 bash 里面设置 array 类型的变量(目前 bash 提供的只有一维阵列),如下所示。

1
2
3
4
5
$ declare -a var[index]=content
$ var[1]="small min"
$ var[2]="big min"
$ var[3]="nice min"
$ echo ${var[1]}

变量内容的删除、取代与替换

变量除了可以直接设置来修改原来的内容之外,还可以对变量的内容进行删除、取代与替换。

变量内容的删除与取代

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# 格式是:{variable#/*local/bin:}
# - variable 就是变量的名称,比如上面的 path
# - # 表示从变量内容的最前面开始往右删除,但是仅删除最短匹配的那个
# - ## 表示从变量内容的最前面开始往右删除,但是删除的是最长匹配的那个
# - /*local/bin: 相当于匹配模式,用于匹配符合模式的字符串

$ path=${PATH}
$ echo $path
/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin 

# 删除从左到第一个 local/bin 的路径
$ echo ${path#/*local/bin:}
/usr/bin:/bin:/usr/sbin:/sbin

# 删除第一个匹配 /*: 的内容,即删除第一个路径
$ echo ${path#/*:}
/usr/bin:/bin:/usr/sbin:/sbin

# 删除前面所有的路径,仅保留最后一个目录
$ echo ${path##/*:}
/sbin

# % 从右往左开始删除内容,最后一个假如含 bin 的话,则删除最后一个目录
$ echo ${path%:*bin}
/usr/local/bin:/usr/bin:/bin:/usr/sbin

# %% 则是最长匹配,删除只剩一个目录了
$ echo ${path%%:*bin}
/usr/local/bin

# 将 path 变量内容内的 sbin 替换为 SBIN
$ echo ${path/sbin/SBIN}
/usr/local/bin:/usr/bin:/bin:/usr/SBIN:/sbin

# 如果是两条斜线,那么所有符合的内容都会被替换
$ echo ${path//sbin/SBIN}
/usr/local/bin:/usr/bin:/bin:/usr/SBIN:/SBIN

更加完整的设置方式如下所示:

变量设置方式 说明
${变量#关键字} ${变量##关键字} 若变量内容从头开始的数据符合“关键字”,则将符合的最短数据删除;若变量内容从头开始的数据符合“关键字”,则将符合的最长数据删除
${变量%关键字} ${变量%%关键字} 若变量内容从尾向前的数据符合“关键字”,则将符合的最短数据删除;若变量内容从尾向前的数据符合“关键字”,则将符合的最长数据删除
${变量/旧字串/新字串} ${变量//旧字串/新字串} 若变量内容符合“旧字串”则“第一个旧字串会被新字串取代”;若变量内容符合“旧字串”则“全部的旧字串会被新字串取代”

变量的测试与内容替换

在某些时刻我们需要判断某个变量是否存在,若变量存在则使用既有的设置,若变量不存在则给予一个常用的设置。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# username 假如没有设置的话,那么 username 被设置为 root
$ username=${username-root}
$ echo ${username}
root
$ username=dawn
$ username=${username-root}
$ echo ${username}
dawn

# 格式如下:new_var=${old_var-content}
# - new_var 是新的变量用来替换 old_var。new_var 和 old_var 常常是一样的
# - content 假如 old_var 没有被设置的话,那么则设置为 content 的内容

# 假如 username 没有被设置或者设置为空串,那么 username 则被设置为 root
$ username=${username:-root}
$ echo ${username}
root

# 假如 str 不存在,那么 str 和 var 的内容都将被设置为 newvar
$ unset str; var=${str=newvar}
$ echo "var=${var} str=${str}"
var=newvar str=newvar

# 假如 str 存在,那么 str 和 var 的内容都将被设置为 oldvar
$ str="oldvar"; var=${str=newvar}
bash-3.2$ echo "var=${var} str=${str}"
var=oldvar str=oldvar

# 假如 str 不存在,那么 var 的测试结果直接显示 “novar”
$ unset str; var=${str?novar}
bash: str: novar

# 假如 str 存在,那么 var 的内容与 str 的内容一样
$ str="oldvar"; var=${str?novar}
$ echo "var=${var} str=${str}"
var=oldvar str=oldvar

更加完整的替换表格如下,一般来说,str 表示仅为“没有该变量”,str: 表示 str 没有设置或者设置成了空串。

变量设置方式 str 没有设置 str 为空字串 str 已设置非为空字串
var=${str-expr} var=expr var= var=$str
var=${str:-expr} var=expr var=expr var=$str
var=${str+expr} var= var=expr var=expr
var=${str:+expr} var= var= var=expr
var=${str=expr} str=expr var=expr str 不变 var= str 不变 var=$str
var=${str:=expr} str=expr var=expr str=expr var=expr str 不变 var=$str
var=${str?expr} expr 输出至 stderr var= var=$str
var=${str:?expr} expr 输出至 stderr expr 输出至 stderr var=$str

环境变量

常用命令

  • env 查看环境变量与常见环境变量说明。env 是 environment 的简写,会列出来所有的环境变量。

  • set 查看所有变量(含环境变量与自定义变量)。set 除了将环境变量显示出来之外,还会将 bash 中的其他变量都显示出来。

  • export 将自定义变量(局部变量)转成环境变量(全局变量)。自定义变量和环境变量的区别在于该变量能否被子进程所继续使用,子进程仅会继续父进程的环境变量,不会进程父进程的自定义变量。export 后面没有接要导出的变量时,那么会把所有的环境变量显示出来。

    在理论上为什么环境变量的数据可以被子进程所引用呢?这是因为内存配置的关系:

    • 当启动一个 shell 之后,操作系统会分配一内存区域给 shell 使用,此内存中的变量可以给子进程使用;
    • 若在父进程中使用 export ,那么可以让自定义变量的内容写到上述的内存区域中(环境变量);
    • 当启动一个新的 shell 时,子 shell 可以将父 shell 环境变量所在的内存区域导入自己的环境变量区域中。

    这个环境变量跟 bash 的操作环境有点不大一样,PS1 并不是环境变量,但是会影响到 bash 的提示字符。

常见系统变量

  • HOME 代表使用者的主目录。

  • SHELL 表示目前这个环境使用的是哪个 SHELL。

  • HISTSIZE 表示历史命令的数量。

  • PATH 就是可执行文件搜索的路径啦,目录和目录中间以冒号(:)分隔,由于文件的搜索依序由 PATH 的变量内的目录来查询,所以目录的顺序也是很重要的。

    当下达 ls 这个指令时,系统就是通过 PATH 这个变量里面的内容所记录的路径顺序来寻找指令的呢!如果在搜索完 PATH 变量内的路径还找不到 ls 这个指令时,就会在屏幕上显示 “command not found” 的错误讯息。

  • LANG 是语系数据的变量,也就是编码相关的变量。Linux 到底支持多少语系,我们可以使用 locale -a 这个指令来进行查询。而查看当前除 LANG 这个变量之外,其他跟语系相关的变量可以使用 locale 这个指令(不加任何参数)查询得到。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    
    $ locale
    LANG="zh_CN.UTF-8"
    LC_COLLATE="zh_CN.UTF-8"
    LC_CTYPE="zh_CN.UTF-8"
    LC_MESSAGES="zh_CN.UTF-8"
    LC_MONETARY="zh_CN.UTF-8"
    LC_NUMERIC="zh_CN.UTF-8"
    LC_TIME="zh_CN.UTF-8"
    LC_ALL=
    

    上面这些变量都是跟语系相关的,假如其他变量你都未设置,仅仅设置了 LANG 或者 LC_ALL 这两个变量,那么其他的变量都会被这两个变量所取代。这也是为啥在 Linux 中仅设置 LANG 和 LC_ALL 这两个变量就可以了。

    有时候 Linux 主机的终端机接口(tty1 ~tty6)的环境下,虽然设置了中文的语系,但是还是会有一堆乱码。这是因为终端机接口环境下无法显示像中文这么复杂的编码文字,所以会产生乱码。这个时候就需要在 tty1~tty6 的环境下加装一些中文化接口的软件,才能看到中文。此时,通过 ssh 的方式可能会看到中文,因为 ssh 的终端软件支持中文。

  • RANDOM 是随机数的变量了,目前大多数的 distributions 都会有乱数产生器,就是 /dev/random 这个文件。可以通过这个乱数文件相关的变量 $RANDOM 来随机取得数据,这个值介于 0~32767。

    假如想要取得 0~9 之间的数值的话,那么可以如下所示:

    1
    
    declare -i number=$RANDOM*10/32768; echo $number
    
  • MAIL 当使用 mail 这个指令收信时,系统会读取的邮件信箱文件。

其他常用变量

  • PS1 提示字符的设置,也就是输入命令前面的提示内容,比如 root@pc~。每次按下 [Enter] 按键去执行某条指令后,最后需要再出现提示字符的时候就会主动读取这个变量值了。PS1 中设置的内容是一些特殊符号,这些符号会显示不同信息,如下所示,更详细的可以使用 man bash

    • \d :可显示出“星期 月 日”的日期格式,如:“Mon Feb 2”
    • \H :完整的主机名称。举例来说,鸟哥的练习机为“study.centos.vbird”
    • \h :仅取主机名称在第一个小数点之前的名字,如鸟哥主机则为“study”后面省略
    • \t :显示时间,为 24 小时格式的“HH:MM:SS”
    • \T :显示时间,为 12 小时格式的“HH:MM:SS”
    • \A :显示时间,为 24 小时格式的“HH:MM”
    • @ :显示时间,为 12 小时格式的“am/pm”样式
    • \u :目前使用者的帐号名称,如“dmtsai”;
    • \v :BASH 的版本信息,如鸟哥的测试主机版本为 4.2.46(1)-release,仅取“4.2”显示
    • \w :完整的工作目录名称,由根目录写起的目录名称。但主文件夹会以 ~ 取代;
    • \W :利用 basename 函数取得工作目录名称,所以仅会列出最后一个目录名。
    • \#:下达的第几个指令。
    • $ :提示字符,如果是 root 时,提示字符为 # ,否则就是 ​$ 啰~

    假如要设置成下面这样的效果,其中 12 表示第 12 条指令,那么 PS1 可以如下这样设置

    1
    
    PS1='[\u@\h \w \A #\#]\$ '
    
  • $,当前 shell 的 PID。

    1
    
    echo $$
    
  • ? 上一条执行的指令的回传值。当我们执行某些指令时,这些指令都会回传一个执行后的代码,一般来说,如果成功地执行了指令,则会回传一个 0 值;执行过程发生了错误,那么会回传“错误代码”。

  • OSTYPE, HOSTTYPE, MACHTYPE,主机硬件与 kernel 的等级。比如 linux-gnu、x86-64。