Bonky Zhu
If someone is able to show me that what I think or do is not right, I will happily change, for I seek the truth, by which no one was ever truly harmed. It is the person who continues in his self-deception and ignorance who is harmed.

Linux Shell脚本

执行方式之间的差异

  • sh script./script 是创建一个子 shell,然后在子 shell 里面执行脚本。(所以脚本里面的变量不会在本环境生效,想起了我以前写了脚本快速进入 anaconda 环境,在脚本里面执行 source /路径/activate 结果还是没法进入环境,其实进入的是子 shell)
  • source 在当前 shell 里面执行脚本。

shell 脚本的追踪和 debug

$ sh [-nvx] scripts.sh 
选项与参数: 
-n :不要执行 script,仅查询语法的问题; 
-v :再执行 sccript 前,先将 scripts 的内容输出到屏幕上; 
-x :将使用到的 script 内容显示到屏幕上,这是很有用的参数!


一般来说,首先先用 -n 检查是否有语法问题,然后执行有问题再用 -x 或者 -v 进行 debug

几个简单的脚本

创建一个有输入的脚本

重点是使用 read 指令,如果你想要加上提示文字请使用 -p 选项。下面 echo 的 -e 选项作用是允许转义字符(发现 macOS 的不需要直接支持)。

#!/bin/bash
read -p "Please Input Your First Name: " firstname
read -p "Please Input Your Last Name: " lastname
echo -e "\nYour Name Is $firstname $lastname"


根据日期不同创建文件名

#!/bin/bash
date=`date +"%m-%d-%Y"`
touch "Backup_$date.txt"


date 指令的使用

基本也最常用使用方法就是这样:

date +


其中格式串中的特殊字符的含义如下,用的最多的是 %Y (年) , %m(月) , %d(天) ,%H(小时),%M(分钟),%S (秒),其实只要记住大小写也很好记住的:

%H 小时(以00-23来表示)。
%I 小时(以01-12来表示)。 
%K 小时(以0-23来表示)。 
%l 小时(以0-12来表示)。 
%M 分钟(以00-59来表示)。 
%P AM或PM。 
%r 时间(含时分秒,小时以12小时AM/PM来表示)。 
%s 总秒数。起算时间为1970-01-01 00:00:00 UTC。 
%S 秒(以本地的惯用法来表示)。 
%T 时间(含时分秒,小时以24小时制来表示)。 
%X 时间(以本地的惯用法来表示)。 
%Z 市区。 
%a 星期的缩写。 
%A 星期的完整名称。 
%b 月份英文名的缩写。 
%B 月份的完整英文名称。 
%c 日期与时间。只输入date指令也会显示同样的结果。 
%d 日期(以01-31来表示)。 
%D 日期(含年月日)。 
%j 该年中的第几天。 
%m 月份(以01-12来表示)。 
%U 该年中的周数。 
%w 该周的天数,0代表周日,1代表周一,异词类推。 
%x 日期(以本地的惯用法来表示)。 
%y 年份(以00-99来表示)。 
%Y 年份(以四位数来表示)。 
%n 在显示时,插入新的一行。 
%t 在显示时,插入tab。 


当然还可以使用 -d 选项指定特定的时间,下面这几种格式都是 OK 的:

date -d "1 day ago" +"%Y-%m-%d"
date -d "+1 day" +%Y%m%d   #显示前一天的日期 
date -d "Dec 5, 2009 12:00:37 AM 2 year ago" +"%Y-%m-%d %H:%M.%S"
date -d "2009-12-12" +"%Y/%m/%d %H:%M.%S"


条件表达式

if...elif...else...fi

基本格式如下,注意 if 和 elif 后面需要接 then,然后就是最后需要有代表 if 结束的 fi :

if [ 条件判断式一 ]; then
    当条件判断式一成立时,可以进行的指令工作内容;
elif [ 条件判断式二 ]; then
    当条件判断式二成立时,可以进行的指令工作内容;
else
    当条件判断式一与二均不成立时,可以进行的指令工作内容;
fi


case...esac

case $变量名称 in
    "第一个变量内容")  <==每个变量内容建议用双引号括起来,关键词则为小括号 )
        程序段
        ;;  <==每个类别结尾使用两个连续的分号来处理!
    "第二个变量内容")
        程序段
        ;;
    *)  <==最后一个变量内容都会用 * 来代表所有其他值
        不包含第一个变量内容与第二个变量内容的其他程序执行段
        exit 1
        ;;
esac    <==最终的 case 结尾!『反过来写』思考一下!


函数

function fname() {
    程序段
}


那有人想问了,这个怎么传参呢。其实和 shell 脚本一样,通过 $1$2 这样的来传参,只不过和脚本不共用而已。

#!/bin/bash
function printinfo() {
    echo "我是复读机: $1"
}
read -p "输入你的消息:" info
printinfo $info


循环

不定循环 while, until

while 是满足条件则进行循环。

while [ condition ]  <==中括号内的状态就是判断式 
do            <==do 是循环的开始!  
    程序段落 
done          <==done 是循环的结束 


until 是满足条件则退出循环。

until [ condition ] 
do  
    程序段落 
done


固定循环 for

for 循环第一次循环 var 等于 con1,第二次循环 var 等于 con2,以此类推。。。

for var in con1 con2 con3 ... 
do  
    程序段 
done


for 循环和 cut 指令配合使用可以处理一个文本后,然后循环其变量:

users=$(cut -d ':' -f1 /etc/passwd)    # 撷取账号名称 
for username in ${users}               # 开始循环进行! do 
   id ${username} 
done 


假如我们想从 1 循环到 100 怎么做呢?首先一个办法是使用 seq 指令:

for var in $(seq 1 100)       # seq 为 sequence(连续) 的缩写之意 
do
  语句 
done 


其次,for 也提供类似高级语言的写法:

for (( 初始值; 限制值; 执行步阶 )) 
do  
  程序段 
done 


数学运算

简单的加减乘除

#!/bin/bash
read -p "Please Input First Num: " firstnum
read -p "Please Input Second Num: " secondnum
declare -i mul_ans=$firstnum*$secondnum
declare -i plus_ans=$firstnum+$secondnum
declare -i sub_ans=$firstnum-$secondnum
declare -i div_ans=$firstnum-$secondnum
echo  "Multiple Ans Is : $mul_ans"
echo  "Plus Ans Is : $plus_ans"
echo  "Sub Ans Is : $sub_ans"
echo  "Div Ans Is : $div_ans"


除了使用 declare 以外我们还可以使用 $((计算式)) 来进行运算(比较推荐这一种):

mul_ans= $(($firstnum*$secondnum))
plus_ans=$(($firstnum+$secondnum))
sub_ans= $(($firstnum-$secondnum))
div_ans= $(($firstnum-$secondnum))


如果你想要计算含有小数点的数据时,应该 bc 这个指令。

bc 指令

一般在 shell 里面使用一般来说利用 echo 将表达式通过管道传给 bc 即可(-l 选项是可以数学库的函数,一般来说带上没什么影响):

echo  | bc -l


重点是如何写表达式,如果是普通的加减乘除直接像下面这么写即可:

echo 'scale=2; (2.777 - 1.4744)/1' | bc


其中 scale=2 代表保留的小数点为2位。然后,bc 有一个最最最好用的东西就是他可以进行进制的转换:

$ echo "obase=2;ibase=16;FFFF" | bc
1111111111111111


obase 代表输出进制,ibase 代表输入进制。当然,还有一些常用的函数可供你使用:

函数名 作用
s(x) 计算 x 的正弦值,x 是弧度值。
c(x) 计算 x 的余弦值,x 是弧度值。
a(x) 计算 x 的反正切值,返回弧度值。
l(x) 计算 x 的自然对数。
e(x) 求 e 的 x 次方。
j(n, x) 贝塞尔函数,计算从 n 到 x 的阶数。

比如,我们可以计算 pi 的值:

$ echo "scale=20;4*a(1)" | bc -l
3.14159265358979323844


强大的 expr 指令

四则运算(一般不推荐)

$ expr 3 + 5
8
$ expr 3 - 5
-2
$ expr 3 \* 5
15
$ expr 3 / 5
0


需要注意两点:

  • 运算符左右都有空格,如果没有空格表示是字符串连接
  • 使用乘号时,必须用反斜线屏蔽其特定含义。因为shell可能会误解显示星号的意义

字符串操作

  • expr length 字串,返回字符串长度
  • expr indexString1 String2,返回字符串2在字符串1的第一个位置(从1开始)
  • expr substr 内容 起始位置 终点位置,提取字符串的子串

判断工具

test 指令

$ test expression
常用的选项:
1. 文件操作:test -e filename
-e :该文件或者目录是否存在?
-f :该文件是否存在?
-d :该目录是否存在?

2. 权限检测:test -r filename
-r, -w, -x : 检查是否有对应权限。

3. 文件比较:test file1 -nt file2
-nt, -ot :判断 file1 是否比 file2 新(老)
-ef :判断是否为同一文件(主要判断硬链接)

4. 整数比较:test n1 -eq n2
-eq, -ne :相等或者不相等
-gt, -lt :大于或者小于
-ge, -le :大于等于或者小于等于

5. 判定字符串的数据:test -z string
-z :为空返回 true
-n :非空返回 true
test str1 == str2, test str1 != str2

6. 多重条件判定:test -r filename -a -x filename
-a, -o :与和并
! : 取反


有一个操作:

test -e /home && echo "exist" || echo "Not exist"


我们可以这么理解,假设上面语句通过逻辑符号分为3个部分,只有 (1) 成功执行才会执行 (2),只有 (1) 和 (2) 都没有成功执行才会执行 (3) 。测试一下可以看到:

➜  test -e / && echo "Success and Return $?" || echo "Failed and Return $?"
Success and Return 0
➜  test -e /sdfsa && echo "Success and Return $?" || echo "Failed and Return $?"
Failed and Return 1


[ ] 符号

➜  a=1
➜  [ $a = 5 ] && echo "YES"
➜  [ $a = 1 ] && echo "YES"
YES


通过这个我们可以写一个简单的判断脚本:

#!/bin/bash
read -p "Continue or Stop? (Y/N) " choice
if [ $choice == Y ]
then 
        echo "Now is Continue";sleep 10s
        echo "Finished"
else
        echo "Stopped";exit 0
fi


带参数的脚本

其实每个 shell 对参数都设定了一些默认名称。

/path/to/scriptname     opt1    opt2    opt3    opt4 
          $0           $1    $2    $3    $4


我写了个小小的测试,下面是我测试的脚本:

echo $0
echo $1
echo $2


测试结果如下,可以看出来 \$0 是文件名,\$1 以后的是你自己输入的变量:

➜  sh test_shell_default_var.sh hello hhh
test_shell_default_var.sh
hello
hhh


当然还有一些特殊的变量:

$# :代表后接的参数个数。
$@ :代表 ["$1" "$2" ...] 之意,每个变量是独立的(用双引号括起来)。
$* :代表 ["$1c$2c...."] ,其中 c 为分隔字符,默认为空格键。(一般来说和 $@ 没什么区别啦,直接用 $@ 就 OK)


对于上面的脚本,执行 echo $# , echo $@ 结果分别是 2hello hhh

同时,还有个非常好用的指令 shift,非常适合不定长的参数的处理,每次可以向前出队列一个参数。打个比方原来 $@1 2 3 4,经过一次 shift 结果 $@ 变成 2 3 4。我们可以用他来进行不定长参数的求和:

sum=0
until [ $# == 0 ]
do
sum=`expr $sum + $1`
shift
done
echo "sum is: $sum"

Share

You may also like...

发表评论