环境变量
全局、局部环境变量
用户变量(局部变量):修改的设置只对某个用户的路径或执行起作用;
系统变量(全局变量):影响范围是整个系统 ;
系统环境变量基本上都是使用全大写字母,以区别于普通用户的环境变量。
查看
# 查看全局变量
$ env
$ printenv
$ printenv HOME # 要显示个别环境变量的值,可以使用printenv命令,但是不能用env命令
# 查看当前环境变量,set命令会显示某个特定进程设置的所有环境变量,包括局部变量、全局变量以及用户定义变量
$ set
env、printenv和set之间的差异:
1、set命令会显示出全局变量、局部变量以及用户定义变量。它还会按照字母顺序对结果进行排序。
2、env和printenv命令不会对变量排序,也不会输出局部变量和用户定义变量。在这种情况下,env和printenv的输出是重复的。不过env命令有一个printenv没有的功能,这使得它要更有用一些。
设置变量
# 设置局部变量
$ my_variable= "Hello World"
# 设置全局变量,可以通过export命令使局部变量变成全局变量
$ export my_variable
# 删除环境变量
$ unset my_variable
注意:如果在子进程中删除了一个全局环境变量, 这只对子进程有效。该全局环境变量在父进程中依然可用。
设置 PATH
$ PATH=$PATH:/home/xx/xx
对PATH变量的修改只能持续到退出或重启系统。这种效果并不能一直持续。
环境变量持久化
当登录Linux系统时,bash shell会作为登录shell启动。登录shell会从5个不同的启动文件里读取命令:
- /etc/profile
- $HOME/.bash_profile
- $HOME/.bashrc
- $HOME/.bash_login
- $HOME/.profile
/etc/profile文件是系统上默认的bash shell的主启动文件。系统上的每个用户登录时都会执行这个启动文件。另外4个启动文件是针对用户的,可根据个人需求定制。
1、对全局环境变量来说,可能更倾向于将新的或修改过的变量设置放在**/etc/profile**文件中,但这可不是什么好主意。如果你升级了所用的发行版,这个文件也会跟着更新,那你所有定制过的变量设置可就都没有了。
2、最好是在**/etc/profile.d目录中创建一个以.sh结尾的文件**。把所有新的或修改过的全局环境变量设置放在这个文件中。
3、在大多数发行版中,存储个人用户永久性bash shell变量的地方是** H O M E / . b a s h r c ∗ ∗ 文件。这一点适用于所有类型的 s h e l l 进程。但如果设置了 ∗ ∗ B A S H E N V ∗ ∗ 变量,那么除非它指向的是 ∗ ∗ HOME/.bashrc**文件。这一点适用于所有类型的shell进程。但如果设置了**BASH_ENV**变量,那么除非它指向的是** HOME/.bashrc∗∗文件。这一点适用于所有类型的shell进程。但如果设置了∗∗BASHENV∗∗变量,那么除非它指向的是∗∗HOME/.bashrc**,否则你应该将非交互式shell的用户变量放在别的地方。
alias命令设置就是不能持久的,可以把alias设置放在 $HOME/.bashrc 启动文件中,使其效果永久化。
数组
要给某个环境变量设置多个值,可以把值放在括号里,值与值之间用空格分隔。
$ mytest=(one two three four five)
$ echo ${mytest[0]}
one
$ echo ${mytest[1]}
two
$ echo ${mytest[*]}
one two three four five
#改变某个索引值位置的值
$ mytest[2]=seven
$ echo ${mytest[*]}
one two seven four five
#删除
$ unset mytest[2] //这里只是删除了值,[2]变成空了而已
$ unset mytest
数学运算
expr 命令
expr命令允许在命令行 上处理数学表达式,但需要转义特殊字符。
#!/bin/bash
var1=10
var2=20
var3=$(expr $var2 / $var1)
echo The result is $var3
使用方括号 [ ]
不需要转义特殊字符,但只支持整数运算。
#!/bin/bash
var1=100
var2=50
var3=45
var4=$[$var1 * ($var2 - $var3)]
echo The final result is $var4
内建计算器:bc
bash计算器能够识别:整数和浮点数、变量、注释、表达式、编程语句、函数
#!/bin/bash
var1=100
var2=45
var3=$(echo "scale=4; $var1 / $var2" | bc) # scale:小数位数,默认0
echo The answer for this is $var3
如果需要进行大量运算,EOF字符串标识了重定向给bc命令的数据的起止
#!/bin/bash
var1=10.46
var2=43.67
var3=33.2
var4=71
var5=$(bc << EOF
scale = 4
a1 = ( $var1 * $var2)
b1 = ($var3 * $var4)
a1 + b1
EOF
)
echo $var5
退出状态码
查看退出状态
$?
Linux退出状态码:
状态码 | 描述 |
---|---|
0 | 执行成功 |
1 | 一般性未知错误(参数有误) |
2 | 不适合的shell命令 |
126 | 命令不可执行(无权限) |
127 | 没有找到命令 |
128 | 无效的退出参数 |
128+x | 与Linux信号x相关的严重错误 |
130 | 通过Ctrl+c终止的命令 |
255 | 正常范围之外的退出状态码 |
自定义退出状态码
默认情况下,shell脚本会以脚本中的最后一个命令的退出状态码退出。
exit 命令允许在脚本结束时指定一个退出状态码。例如:
$ cat test13
#!/bin/bash
# testing the exit status
var1=10
var2=30
var3=$[$var1 + $var2]
echo The answer is $var3
exit 5 # 指定退出状态码
# exit var2 # 可以指定变量
$ chmod u+x test13
$ ./test13
The answer is 40
$ echo $?
5
注意:退出状态码最大只能是255,区间为:0~255
字段分隔符 IFS
IFS环境变量定义了bash shell用作字段分隔符的一系列字符。
默认情况下,bash shell会将下列字符当作字段分隔符: 空格、制表符、换行符如果bash shell在数据中看到了这些字符中的任意一个,它就会假定这表明了列表中一个新数据字段的开始。
可以将IFS的值也可以设为其他,也可以设置多个:
# 将IFS的值设为冒号(:)
IFS=:
# 设置多个,将换行符($'\n')、冒号(:)、分号(;)和双引号(")作为字段分隔符
IFS=$'\n':;"
#!/bin/bash
# reading values from a file
file="states"
# 设置字段分割为换行符
IFS=$'\n'
for f in $(cat $file)
do
echo "Visit beautiful $f"
done
在处理代码量较大的脚本时,可能在一个地方需要修改IFS的值,然后忽略这次修改,在脚本的其他地方继续沿用IFS的默认值。一个可参考的安全实践是在改变IFS之前保存原 来的IFS值,之后再恢复它。 这种技术可以这样实现:
IFS.OLD=$IFS
IFS=$'\n'
[代码...]
IFS=$IFS.OLD
这就保证了在脚本的后续操作中使用的是IFS的默认值。
判断条件
test 命令
如果test命令中列出的条件成立,test命令就会退出并返回退出状态码0
test命令可以判断三类条件:数值比较、字符串比较、文件比较
if test condition
then
commands
fi
[ ] 替代test命令
[ ] 定义测试条件,等同 test 命令。
if [ condition ] # condition 前后需要加一个空格
then
commands
fi
数值比较
- -eq 等于
- -ne 不等
- -gt 大于
- -ge 大于等于
- -lt 小于
- -le 小于等于
bash shell只能处理整数,不支持浮点值比较。
字符串比较
- str1 = str2 检查str1是否和str2相同
- str1 != str 检查str1是否和str2不同
- str1 < str2 检查str1是否比str2小
- str1 > str2 检查str1是否比str2大
- -n str1 检查str1的长度是否不为0
- -z str1 检查str1的长度是否为0
字符串比较大小时,有两个问题:
1、大于号和小于号必须转义,否则shell会把它们当作重定向符号,把字符串值当作文件名;
2、大于和小于顺序和sort命令所采用的不同,顺序相反;空的和未初始化的变量也可以用-n、-z判断。
文件比较
- -d file 检查file是否存在,且是否为目录
- -f file 检查file是否存在,且是否为文件
- -e file 检查file目录或文件是否存在
- -r file 检查file目录或文件是否存在,并可读
- -s file 检查file目录或文件是否存在,并非空
- -w file 检查file目录或文件是否存在,并可写
- -x file 检查file目录或文件是否存在,并可执行
- -O file 检查file目录或文件是否存在,并属当前用户所有
- -G file 检查file目录或文件是否存在,并且默认组与当前用户相同
- file1 -nt file2 检查file1是否比file2新
- file1 -ot file2 检查file1是否比file2旧
布尔运算( && 、|| )
- A && B 当A命令执行成功,才执行B
- A || B 仅当A执行失败,才执行B
布尔逻辑是一种能够将可能的返回值简化为TRUE或FALSE的方法。
[ A && B ] 使用AND布尔运算符来组合两个条件。两个条件都必须满足,then部分的命令才会执行。
[ A || B ] 使用OR布尔运算符来组合两个条件。任意条件为TRUE,then部分的命令就会执行。
双括号 (( ))
双括号命令允许你在比较过程中使用高级数学表达式。 只能进行整数运算,不能对小数(浮点数)或者字符串进行运算。
(( expression )) # expression 可以是任意的数学赋值或比较表达式
- 表达式可以有多个,多个表达式之间以逗号,分隔。对于多个表达式的情况,以最后一个表达式的值作为整个 (( )) 命令的执行结果: echo $((a=3+5, b=a+10)) 输出:15 ;
- 可以使用 $ 获取 (( )) 命令的结果,这和使用 获得变量值类似: a = 获得变量值类似:a= 获得变量值类似:a=((10+66);
- 在 (( )) 内使用变量无需加上 前缀,会自动解析变量名: ( ( a + b ) ) ,但要获取结果时需要加上 c = ∗ ∗ 前缀,会自动解析变量名:((a+b)),但要获取结果时需要加上c=** 前缀,会自动解析变量名:((a+b)),但要获取结果时需要加上c=∗∗**((a+b));
除了支持简单的加减乘除外,还支持其他运算符:
符合 | 描述 |
---|---|
val++ | 后增 |
val– | 后减 |
++val | 先增 |
–val | 先减 |
! | 逻辑求反 |
~ | 位求反 |
** | 幂运算 |
<< | 左位移 |
>> | 右位移 |
& | 位布尔和 |
| | 位布尔或 |
&& | 逻辑和 |
|| | 逻辑或 |
可以在 if 语句中用双括号命令,也可以在脚本中的普通命令里使用来赋值:
$ cat test.sh
#!/bin/bash
val1=10
if (( $val1 ** 2 > 90 )) ; then
(( val2 = $val1 ** 2 ))
echo "The square of $val1 is $val2"
fi
$ ./test.sh
The square of 10 is 100
注意:不需要将双括号中表达式里的大于号转义。这是双括号命令提供的另一个高级特性。
双方括号 [[ ]]
双方括号命令提供了针对字符串比较的高级特性。
[[ expression ]]
双方括号里的expression使用了test命令中采用的标准字符串比较。但它提供了test命 令未提供的另一个特性(模式匹配)。在模式匹配中,双方括号中可以定义一些正则表达式来匹配字符串:
$ cat test.sh
#!/bin/bash
if [[ $USER == r* ]]
then
echo "Hello $USER"
else
echo "Sorry, I do not know you"
fi
$ ./test.sh
Hello rich
注意:不是所有的shell都支持双方括号!
小数比较
$ echo "1.8 < 1.6" |bc # 不成立输出 0
0
$ echo "1.8 > 1.6" |bc # 成立输出 1
1
#!/bin/bash
a=0.23
b=0.9
if [ $(echo "$a > $b" | bc) = 1 ]; then
echo "a>b"
else
echo "a<b"
fi
结构化命令
if-then
与其他编程语言不同,shell 的 if 语句会运行 if 后面的命令,如果该命令的退出状态码是0 (该命令成功运行),位于then部分的命令就会被执行。如果该命令的退出状态码是其他值就不会被执行,会继续执行脚本中的下一个命令。
if command ; then
commands
elif command ; then
commands
else
commands
fi
例
$ cat test1.sh
#!/bin/bash
# testing the if statement
if pwd
then
echo "It worked"
fi
# 这个脚本在if行采用了pwd命令。如果命令成功结束,再执行echo语句
$ ./test1.sh
/home/Christine
It worked
case 命令
case命令:为变量每个可能的值指定不同的选项。
case variable in
pattern1 | pattern2) commands1;;
pattern3) commands2;;
*) default commands;;
esac
case 匹配不同的某值,如果匹配成功则执行它下面的命令,直到 ;; 为止
#!/bin/bash
echo '输入 1 到 3 之间的数字:'
echo '你输入的数字为:'
read Num
case $Num in
1) echo '你选择了 1' ;; # 2|3) 值内容可以加上|可以有多个值
2) echo '你选择了 2' ;;
3) echo '你选择了 3' ;;
*) echo '你没有输入 1 到 3 之间的数字' ;; # 如以上都不匹配,则执行这条
esac #结束
echo "test case end" #输出内容
for 循环
#!/bin/bash
list="A B C D"
list=$list" E" # 这是向变量中存储的已有文本字符串尾部添加文本的一个常用方法
for test in $list
do
echo The next number is $test
done
echo The last number is $test # for结束后,test变量会一直保持最后一次迭代的值
# 从命令读取值遍历
#!/bin/bash
file="test.txt"
for f in $(cat $file)
do
echo "Visit beautiful $f"
done
for命令默认用空格、制表符、换行符来划分列表中的每个值。如果在单独的数据值中有空格,就必须用双引号将这些值圈起来。
也可以将do语句和for语句放在同一行,但必须用分号将其同列表中的值分开:for var in list; do
用通配符遍历目录
# 遍历目录文件,支持多个目录
for file in /home/rich/.b* /home/rich/badtest/*
do
if [ -d "$file" ] # 将$file变量用双引号圈起来,预防有带空格的目录
then
echo "$file is a directory"
elif [ -f "$file" ]
then
echo "$file is a file"
else
echo "$file doesn't exist"
fi
done
在Linux中,目录名和文件名中包含空格当然是合法的。要适应这种情况,应该将$file变量用双引号圈起来。如果不这么做,遇到含有空格的目录名或文件名时就会有错误产生:
./test6: line 6: [: too many arguments
for迭代( i++ )
#!/bin/bash
for (( i=1; i <= 10; i++ ))
do
echo "The next number is $i"
done
$ ./test8
The next number is 1
The next number is 2
...
# 可以使用多个变量
#!/bin/bash
# multiple variables
for (( a=1, b=10; a <= 10; a++, b-- ))
do
echo "$a - $b"
done
while 循环
while命令允许定义一个要测试的命令(条件),只要定义的测试命令返回非0退出状态码时,while命令会停止循环。
while命令允许你在while语句行定义多个测试命令。只有最后一个测试命令不成立时停止循环。
#!/bin/bash
var1=10
while echo $var1
[ $var1 -ge 0 ] # 注意:每个测试命令都在单独的一行上
do
echo "This is inside the loop"
var1=$[ $var1 - 1 ]
done
until 循环
until命令和while命令工作的方式完全相反。until命令要求你指定一个通常返回非零退出状态码的测试命令。只有测试命令的退出状态码不为0,bash shell才会执行循环中列出的命令。 一旦测试命令返回了退出状态码0,循环就结束了。
和while命令类似,可以指定的多个测试命令,只有在最后一个测试命令成立时停止循环。
#!/bin/bash
var1=100
until echo $var1
[ $var1 -eq 0 ]
do
echo Inside the loop: $var1
var1=$[ $var1 - 25 ]
done
循环处理文件数据
通过修改IFS环境变量,就能强制for命令将文件中的每行都当成单独的一个条目来处理,即便数据中有空格也是如此。一旦从文件中提取出了单独的行,可能需要再次利用循环来提取行中的数据。
#!/bin/bash
# 遍历/etc/passwd文件中的数据
IFS=$'\n'
for entry in $(cat /etc/passwd)
do
echo "Values in $entry –" # 先换行符间隔遍历
IFS=:
for