1、数学运算
在Bash shell环境中,可以使用let、(())和[]执行基本的算术操作,而在进行高级操作时,也会使用到expr和bc这两个工具。
1)let命令
使用普通的变量赋值方法定义赋值,存储为字符串,可使用let命令进行基本的算术操作。当使用let时,变量名之前不需要再添加$,如下:
#!/bin/bash
no1=4
no2=5
let result=no1+no2
echo $result
自加操作
$ let no1++
自减操作
$ let no1--
简写形式
let no+=6等同于let no=no+6
let no-=6等同于let no=no-6
2)操作符[]
操作符[]的使用方法和let命令类似:
result=$[ no1 + no2 ]
在[]中也可以使用$前缀,如:
result=$[ $no1 + 5 ]
3)操作符(())
使用操作符(())操作如下:
result=$(( no1 + 5 ))
4)工具expr
expr同样可以用于基本算术操作,如下:
$ result=`expr 3 + 4 `
$ result=$(expr $no1 + 5)
注意有操作符间有无空格的区别:
$ expr 3 + 4
7
$ expo 3+4
3+4
5)工具bc
let命令、运算符(())、运算符[]及工具expr值能用于整数运算,不支持浮点数。
bc是一个用于数学运算的高级工具,这个精密计算器包含了大量的选项。可以使用bc执行浮点数运算,并应用一些高级函数。
$ echo "4*0.56" |bc
2.24
$ no=54
$ result=`echo "$no*1.5" |bc`
$ echo $result
81.0
其他参数可以置于要执行的具体操作之前,同时以分号座位定界符,通过stdin传递给bc。
1)设定小数精度
scale的值表示小数的位数,如下:
$ echo "scale=2;3/8" |bc
0.37
2)进制转换
通过obase和ibase的设置进行进制转换。
$ no=100
$ echo "obase=2;$no" |bc
1100100
$ no=1100100
$ echo "obase=10;ibase=2;$no" |bc
100
[root@iZ2zeafpnt57xvg0qrzjaiZ liubc]#
3)平方及平方根
通过运算符^和sqrt进行平方和平方根的运算。
$ echo "sqrt(100)" |bc
10
$ echo "10^10" |bc
10000000000
2、文件描述符和重定向
1)文件描述符
文件描述符是与文件输入、输出相关联的整数,用来跟踪已打开的文件。
最常见的文件描述符是stdin、stdout和stderr,0、1、2分别是stdin、stdout、stderr的预留描述符编号。
0 —— stdin(标准输入)
1 —— stdout(标准输出)
2 —— stderr(标准错误)
2)重定向
可以将某个文件描述符的内容重定向到另一个文件中。
可以将输出文本重定向或保存到文件中,如下:
$ echo "This is a sample text 1" > temp.txt
这种方法通过截断文件到方式,将输出文本存储到文件temp.txt中。在把echo命令的输出写入文本之前,temp.txt中的内容会先被清空。
若要不清空原有文件,并将内容追加到目标文件中,可将>替换成>>,如下:
$ echo "This is a sample text 2" >> temp.txt
查看文件内容:
$ cat temp.txt
This is a sample text 1
This is a sample text 2
当命令输出错误信息时,stderr信息就会被打印出来。下面的命令会将stderr文本打印到屏幕上,而不是文件中,由于stdout没有输出,因此没有内容:
$ ls + > out.txt
ls: cannot access +: No such file or directory
下面的命令,将stderr重定向到ou t.txt中:
$ ls + 2> out.txt
$ cat out.txt
ls: cannot access +: No such file or directory
也可以将stderr单独重定向到一个文件,将stdout重定向到另一个文件:
$ cmd 2>stderr.txt 1>stdout.txt
也可以将stderr转换成stdout,使得stderr和stdout都被重定向到同一个文件中,如下:
$ cmd 2>&1 stdout.txt
或者:
$ cmd &> stdout.txt
如果不想让终端输出stderr的信息,可以将stderr重定向到/dev/null,如下:
$ cmd 2> /dev/null
3)tee命令
若要在终端打印stdout,同时将它重定向到一个文件中,可以使用tee,如下:
cmd |tee FILE1 FILE2
在下面的代码中,tee命令接收到来自stdin的数据,它将stdout的一份副本写入到文件out.txt,同时将另一份副本作为后续命令的stdin。命令cat -n将从stdin中接收到的每一行数据前加上行好并写入stdout:
$ echo a1 > a1
$ cp a1 a2
$ cp a2 a3
$ chmod 000 a1
$ cat a* |tee out.txt|cat -n
cat: a1: Permission denied
1 a1
2 a1
查看out.txt的内容:
$ cat out.txt
a1
a1
默认情况下,tee命令会将文件覆盖,-a选项用于追加内容,如下:
$ cat a* |tee -a out.txt|cat -n
4)将文件重定向到命令
可以使用stdin从文件读取数据,如下:
$ cmd < file
5)将脚本内部的文本块进行重定向
例如,向log文件中写入头部数据:
#!/bin/bash
cat<<EOF>log.txt
LOG FILE HEADER
This is a test log file
Function:System statistics
EOF
6)自定义文件描述符
<操作符用于从文件读取数据到stdin。>操作符用于截断模式的文件写入,即数据在目标文件内容被截断后写入。>>操作符用于追加模式的文件写入,即数据被添加到文件的现有内容中,且该目标文件中的原有内容不丢失。
可以使用exec命令创建自定义文件描述符。
创建一个文件描述符进行文件读取,如下:
$ echo this is a test line > input.txt
$ exec 3 < input.txt
$ cat <&3
this is a test line
如果要在此读取,就不能继续使用文件描述符3了,而是需要用exec重新分配文件描述符来进行二次读取。
创建一个文件描述符用于截断写入,如下:
$ exec 4>output.txt
$ echo newline >&4
$ cat output.txt
newline
创建一个文件描述符用于追加写入,如下:
$ exec 5>>output.txt
$ echo appended line >&5
$ cat input.txt
new line
appended line
成功与不成功的命令
当一个命令发生错误并退出时,它会返回一个非0的退出状态;而当命令成功完成后,它会返回数字0。退出状态可以从特殊变量$?中获得。在命令执行之后立即运行echo $?,就可以打印出退出状态。
3、数组
1)定义数组
定义数组的方法如下:
array_var=(1 2 3 4 5 6)
这些值将会存储在以0为起始索引的连续位置上。
也可以将数组定义成一组“索引-值”,如下:
array_var[0]="1"
2)打印数组
打印特定索引的数组元素,如下:
$ echo ${array_var[0]}
1
$ index=5
$ echo ${array_var[$index]}
6
打印数组中的所有值,如下:
$ echo ${array_var[*]}
1 2 3 4 5 6
也可以这样使用,如下:
$ echo ${array_var[@]}
1 2 3 4 5 6
打印数组长度,如下:
$ echo ${#array_var[@]}
6
3)关联数组
在关联数组中,可以用任意文本作为数组索引。
定义数组时,使用声明语句将一个变量名声明为关联数组。如下:
$ declare -A ass_array
声明之后,可以用两种方法将元素添加到关联数组中。
利用内嵌“索引-值”列表的方法,提供一个“索引-值”列表,如下:
$ ass_array=([index1]=val1 [index2]=val2)
使用独立的“索引-值”进行赋值,如下:
$ ass_array[index1]=val1
$ ass_array[index2]=val2
每个数组元素都有一个索引用于查找,普通数组和关联数组具有不通的索引类型。可以通过以下方法获取数组的索引列表:
$ echo ${!array_var[*]}
也可以使用:
$ echo ${!array_var[@}
对于普通数组,这个方法同样可行。
4、别名
别名是一种便捷方式,以省去用户输入一长串命令序列的麻烦。
使用alias命令创建别名。如下:
$ alias new_command='command sequence'
alias命令的作用只是暂时的,一旦终端关闭,所有设置的别名就都失效了。为了使别名的设置一直保持有效,可以将它放入~/.bashrc文件中。当一个新的shell进程生成时,都会执行~/.bashrc中的命令。
$ echo 'alias cmd="command sequence"' >> ~/.bashrc
如果要删除别名,将其对应的语句从~/.bashrc中删除,或者使用unalias命令。也可以使用alias example=,这会取消名example的别名。
创建别名时,如果已经有同名的别名存在,那么原有的别名设置将被新的设置取代。
当要忽略当前定时的别名时,可以使用字符\对命令进行转义,从而执行原本的命令,而不是这些命令的别名。如下:
$ \command
通过在命令前加上\来忽略可能存在的别名设置,总是一个不错的安全实践。因为攻击者可能已经将一些别有用心的命令利用别名伪装成了特权命令,借此来倒去用户输入的重要信息。
5、获取终端信息
tput和stty是两款终端处理工具。
1)获取终端的行数和列数:
tput cols
tput lines
2)打印当前终端名:
tput longname
3)将光标移动到(100,100)处:
tput cup 100 100
4)设置终端背景颜色:
tput setb n)
其中,n可以在0到7之间取值。
5)设置文本前景色:
tput setf n)
其中,n可以在0到7之间取值。
6)设置文本样式为粗体:
tput bold
7)设置下划线的起止:
tput smul
tput rmul
8)删除从当前光标位置到行尾的所有内容:
tput ed)
9)输入密码时,不应该显示输入内容,可以通过stty来实现,如下:
#!/bin/bash
#Filename: password.sh
echo -e "Enter password: "
stty -echo
read password
stty echo
echo
echo Password read
选项-echo禁止将输出发送到终端,而选项echo则允许发送输出到终端。
6、获取、设置日期和延时
在类Unix系统中,日期被存储成一个整数,其大小为自世界标准时间(UTC)1970年1月1日0时0分0秒起所流逝的秒数。这种计时方式称为纪元时或Unix时间。
1)读取日期
$ date
Sun Mar 4 12:35:23 CST 2018
2)打印纪元时
$ date +%s
1520138123
将任意的日期格式转换成纪元时:
$ date --date "Sun Mar 4 12:35:23 CST 2018" +%s
1520138123
选项--date用于提供日期串作为输入。
3)格式化日期
用格式串结合+作为date命令的参数,可以打印对应格式的日期。如下:
$ date "+%d %B %Y"
04 March 2018
4)设置日期和时间
date -s "格式化的日期字符串"
5)检查脚本执行时长
可使用如下代码:
#!/bin/bash
# Filename: time_take.sh
start=$(date +%s)
commands;
statements;
end=$(date +%s)
difference=$(( end - start ))
echo Time taken to execute commands is $difference seconds.
另一种方法则是使用time<scriptpath>来得到执行脚本所花费的时间。
6)日期格式
星期 %a(例如:Sun) %A(例如:Sunday)
月份 %b(例如:Mar) %B(例如:March) %m(例如:03) %h(例如:Mar)
日 %d(例如:31)
固定格式日期 %D(例如:03/04/18)
年份 %y(例如:18) %Y(例如:2018)
小时 %I(例如:01) %H(例如:13)
分钟 %M(例如:06)
秒 %S(例如:01)
纳秒 %N(例如:892982208)
Unix纪元时(以秒为单位) %s(例如:1520140188)
7)延时
延时通过sleep命令实现,单位为秒。
如下脚本:
#!/bin/bash
# Filename: sleep.sh
echo -n Count:
tput sc
count=0;
while true;
do
if [ $count -lt 40 ];
then
let count++;
sleep 1;
tput rc
tput ed
echo -n $count
else exit 0;
fi
done
在上面的例子中,变量count初始化为0,随后每循环一次便加1,echo语句打印出count的值。用tput sc存储光标位置,在每次循环中,通过命令tput rc恢复之前存储的光标位置,在终端中打印出新的count值。tput ed清除从当前光标位置到行尾之间的所有内容,使得旧的count值被清除并写入新值。循环的一秒钟延时是通过sleep命令实现的。