乐趣区

关于linux:Linux基础Shell脚本

四、Shell 脚本

[TOC]

编写自定义脚本的时候,能够在用户的家目录下的创立一个 bin 目录,而后将自定义脚本文件退出到刚创立的 bin 目录中,而后咱们就能够间接在以后用户下执行脚本文件了,因为在用户的家目录下的 用户环境变量文件(.bash_profile) 中曾经执行 $HOME/bin 了。所以能够间接应用!

2.shell 根本运算

因为原生的 bash 是不反对数学运算的,所以能够应用 expr看来实现数学运算,应用根本形式:应用反引号将 expr 表达式包裹起来;

==留神==:条件表达式要放在方括号之间,并且要有空格,例如: [$a==$b] 是谬误的,必须写成 [
$a == $b ]。

vi a.sh

#!/bin/bash
# 定义一个变量名叫 val, 要计算的两个字符之间要有空格进行分隔
val=`expr 2 + 2`
# 调用自定义变量进行输入的时候应用 $ 符号
echo "两数字之和:$val"

(1). 算数运算符

运算符 阐明 举例
+ 加法 expr $a + $b 后果为 30。
减法 expr $a - $b 后果为 -10。
* 乘法 expr $a \* $b 后果为 200。
/ 除法 expr $b / $a 后果为 2。
% 取余 expr $b % $a 后果为 0。
= 赋值 a=$b 将把变量 b 的值赋给 a。
== 相等。用于比拟两个数字,雷同则返回 true。 [$a == $b] 返回 false。
!= 不相等。用于比拟两个数字,不雷同则返回 true。 [$a != $b] 返回 true。

(2). 关系运算符

运算符 阐明 举例
-eq 检测两个数是否相等,相等返回 true。 [$a -eq $b] 返回 false。
-ne 检测两个数是否不相等,不相等返回 true。 [$a -ne $b] 返回 true。
-gt 检测右边的数是否大于左边的,如果是,则返回 true。 [$a -gt $b] 返回 false。
-lt 检测右边的数是否小于左边的,如果是,则返回 true。 [$a -lt $b] 返回 true。
-ge 检测右边的数是否大于等于左边的,如果是,则返回 true。 [$a -ge $b] 返回 false。
-le 检测右边的数是否小于等于左边的,如果是,则返回 true。 [$a -le $b] 返回 true。

(3). 布尔运算符

运算符 阐明 举例
! 非运算,表达式为 true 则返回 false,否则返回 true。 [! false] 返回 true。
-o 或运算,有一个表达式为 true 则返回 true。 [$a -lt 20 -o \$b -gt 100] 返回 true。
-a 与运算,两个表达式都为 true 才返回 true。 [$a -lt 20 -a ​ \$b -gt 100] 返回 false。

(4). 逻辑运算符

运算符 阐明 举例
&& 逻辑的 AND [[$Misplaced &a -lt 100 && $b -gt 100]] 返回 false
\ \ 逻辑的 OR [[$a -lt 100 \ \ $b -gt 100 ]] 返回 true

(5). 字符串运算符

运算符 阐明 举例
= 检测两个字符串是否相等,相等返回 true。 [$a = $b] 返回 false。
!= 检测两个字符串是否相等,不相等返回 true。 [$a != $b] 返回 true。
-z 检测字符串长度是否为 0,为 0 返回 true。 [-z $a] 返回 false。
-n 检测字符串长度是否不为 0,不为 0 返回 true。 [-n “$a”] 返回 true。
$ 检测字符串是否为空,不为空返回 true。 [$a] 返回 true。

3.shell 流程管制

(1),if/then/elif/else/fi 构造

# 编写脚本
$ vi equals.sh
# 指定脚本解析器的类型是 bash
#!/bin/bash

a=10
b=20

if [$a != $b]
then
  echo "$a != $b : a 不等于 b"
else
  echo "$a == $b : a 等于 b"
fi

# 2,编写实现脚本之后,须要对脚本文件赋予可执行的权限
$ chmod 755 equals.sh
# 3,执行脚本
$ sh equals.sh

(2).case/esac 构造

# 1,编写脚本内容
$ vi case.sh 

#!/bin/bash
# 打印提示信息
echo "当初是早上吗?请输出你的抉择:"
# 示意须要用户输出一个变量
read yes_or_no
# 依据用户输出的变量,进行 case 的判断,而后 in 之后的每个选项指定命令最初都应用 ;; 示意完结
case "$yes_or_no" in
yes|y|Yes|YES) # 示意输出以以下选项的
  echo "Good Morning!";;
[nN]*) # 示意你输出以 n 结尾的都能够提醒你下午好
  echo "Good Afternoon!";;
*) # 输出其余字符的时候,提醒输出有误
  echo "对不起,输出有误!"
  exit 1;;
# case 语句的完结
esac
exit 0

# 2,编写实现脚本之后,须要对脚本文件赋予可执行的权限
$ chmod 755 case.sh
# 3,执行脚本
$ sh case.sh

(3).for/do/done 构造

1). 案例需要

请应用 for 循环在指定目录下 ”/root/test” 目录下创立是个文件。

test-1.sh
test-2.sh
test-3.sh
test-4.sh
test-5.sh
test-6.sh
test-7.sh
test-8.sh
test-9.sh
test-10.sh
2),shell 脚本
#!/bin/sh

# 先判断以后指定的文件夹是否存在
if [! -d /root/forTest]
 then mkdir -p /root/forTest
fi
# 循环 1 -10 之间的所有数
for num in {1..10}
 # 在循环内执行以下命令
 do
  touch /root/forTest/test-${num}.sh    #因为创立文件到指定目录,所以创立时最好用全门路;done

(4),while/do/done 构造

1), 案例需要

需要 1:验证用户输出的明码,直到用户输出正确为止;

需要 2:验证用户输出的明码,用户输十次还是不正确则退出;

2),shell 脚本

shell 脚本 1

#!/bin/bash

echo "请输出明码:"
# 获取用户输出的字符
read try
while ["$try" != "111111"];
do
  echo "输出谬误,请从新输出:"
  read try
done

shell 脚本 2

#!/bin/bash

echo "请输出明码:"
read try
# 定义计数器,控制变量
counter=1
# 此时的 "-a" 示意 "and","-lt" 示意 "小于"
while ["$try" != "111111" -a "$counter" -lt 3];
do
  echo "输出谬误,请从新输出:"
  # 其中 $(()) 中的 Shell 变量取值将转换成整数,另外 $[] 示意同样的含意
  counter=$(($counter+1))
  # 在循环中再次读入用户输出的变量
  read try
done
# 当循环完结之后,弹出提示信息
echo "3 次机会用尽!!"

(5).break 与 continue

break 跳出,continue 跳过。

break[n]能够指定跳出几层循环,continue 跳过本次循环步,没跳出整个循环。

(6),Shell 中对字符串的解决

1), 字符串的截取
格局 阐明
${string: start :length} 从 string 字符串的右边第 start 个字符开始,向右截取 length 个字符。
${string: start} 从 string 字符串的右边第 start 个字符开始截取,直到最初。
${string: 0-start :length} 从 string 字符串的左边第 start 个字符开始,向右截取 length 个字符。
${string: 0-start} 从 string 字符串的左边第 start 个字符开始截取,直到最初。
${string#*chars} 从 string 字符串第一次呈现 chars 的地位开始,截取 chars 左边的所有字符。
${string##*chars} 从 string 字符串最初一次呈现 chars 的地位开始,截取 chars 左边的所有字符。
${string%*chars} 从 string 字符串第一次呈现 chars 的地位开始,截取 chars 右边的所有字符。
${string%%*chars} 从 string 字符串最初一次呈现 chars 的地位开始,截取 chars 右边的所有字符。
2), 获取字符串长度等操作
  • 示例代码 与 执行后果

    str="http://www.fengbohello.xin3e.com/blog/shell-truncating-string"
    echo "string : [${str}]"
    
    length=${#str}
    echo "length : [${length}]"
    
    ## 执行后果
    string : [http://www.fengbohello.xin3e.com/blog/shell-truncating-string]
    length : [61]

4.shell 参数 和 非凡变量

$0 就是你写的 shell 脚本自身的名字,​$1 是你给你写的 shell 脚本传的第一个参数,$2 是你给你写的 shell 脚本传的第二个参数.

其余罕用非凡变量

$#  # 相当于 C 语言 main 函数的 argc - 1,留神这里的 #前面不示意正文
$@  # 示意参数列表 "$1" "$2" ...,例如能够用在 for 循环中的 in 前面。$*  # 示意参数列表 "$1" "$2" ...,同上
$?  # 上一条命令的 Exit Status
$$  # 以后过程号
# 编写脚本文件
$ vi special.sh

#!/bin/bash
echo "shell 脚本自身的名字: $0"
echo "传给 shell 脚本的第一个参数: $1"
echo "传给 shell 脚本的第二个参数: $2"

# 赋予权限、执行脚本
$ chmod 755 special.sh
$ sh special.sh xxx01 xxx02

5.shell 的文件蕴含

. filename   # 留神点号 (.) 和文件名两头有一空格
或者
source filename

6. 脚本的执行

一个标准的 Shell 脚本在第一行会指出由哪个程序(解释器)来执行脚本中的内容,这一行内容在 Linux bash 的编程个别为:

\#!/bin/bash

或者

\#!/bin/sh

留神:

(1)在 Shell 中如果一行的第一个字母是 #, 则是正文,然而下面两个是写在第一行,所以不是脚本正文行,如果写在某个命令之后,则变成正文行。

(2)sh 为 bash 的软链接,大多数状况下,脚本的结尾应用“#!/bin/bash”和“#!/bin/sh”是没有区别的,但更标准的写法是在脚本的结尾应用“#!/bin/bash”。

(1), 应用 ”./ 脚本 ” 的形式运行

# 进入脚本所在的工作目录,应用 ./ 脚本形式执行
./test.sh

(2),”sh 脚本 ” 执行

# 应用脚本对应的 sh 或者 bash 来接着脚本执行
sh test.sh
bash test.sh

(3),shell 环境运行

# 在以后 shell 环境中执行,能够应用 ". 脚本" 或者 "source 脚本"
. test.sh
source test.sh

7. 定时工作(清理日志)

1. 应用命令:cat /etc/crontab 查看定时工作的工夫格局,
2. 应用命令:vi /etc/crontab 编辑你的定时工作,
3. 应用命令:service crond status 查看以后定时工作是否启动.
4. 应用命令:service crond start 来启动 crond 工作.
# 常见的定时工作
# 0 0 * * * sh /root/bin/demo02.sh
每分钟执行              */1 * * * *     command
每小时 0 分执行           0 * * * *       command
每天 0 点执行             0 0 * * *       command
每周日 0 点执行           0 0 * * 0       command
每月 1 号执行             0 0 1 * *       command
每年 1 月 1 日执行          0 0 1 1 *       command

(1), 定时清理日志

1), 找到对应的日志目录
## find 对应目录 -mtime + 天数 -name "文件名" -exec rm -rf {} \;

find /home/apache-tomcat-7.0.55-13-990*/webapps/ProxyServer2.0/WEB-INF/logs/ -mtime +10  -name "*.log.*" -exec rm -rf {} \;

find /app/datacheck/logs/ -mtime +10  -name "*.log.*"


## 阐明:find:Linux 查找命令,用户查找指定条件的文件
/home/apache-tomcat-7.0.55-13-990*/webapps/HollyProxyServer2.0/WEB-INF/logs/:须要进行清理的目标目录
-mtime:规范语句写法
+10:数字代表天数
-name "*.log.*":指标文件的类型,带有 log 的所有文件
-exec:固定写法
rm -rf:强制删除包含目录在内的文件
{} \;:固定写法,一对大括号 + 空格 +\+;
2), 设置定时工作
  • 创立 shell 脚本

    touch  /home/apache-tomcat-7.0.55-13-990*/webapps/ProxyServer2.0/WEB-INF/bin/auto-del-log.sh
     
    chmod u+x auto-del-log.sh
    
    vi auto-del-log.sh
    #!/bin/sh
    find /home/apache-tomcat-7.0.55-13-990*/webapps/ProxyServer2.0/WEB-INF/logs/ -mtime +10  -name "*.log.*" -exec rm -rf {} \;
  • 编辑定时工作

    # 进入定时工作编辑页面,按 i 底部呈现 INSERT,开始进行工作脚本编辑,将 auto-del-log.sh 执行脚本退出到零碎打算工作,到点主动执行:crontab e
     
    # 追加如下内容
    30 0 * * * /home/apache-tomcat-7.0.55-13-990*/webapps/ProxyServer2.0/WEB-INF/logs/ auto-del-log.sh
    设置每天凌晨 0:30 执行 auto-del-log.sh 文件进行数据清理工作。编辑实现按 Ctrl+c,输出:quit,之后再输出:w! 实现保留,ctrl+c,:quit 退出。

### (2), 依据文件大小清理日志

1), 编辑文件
vi xxx.sh
## 追加以下内容, 判断 /app/datacheck/logs/dev.log 文件的大小
#!/bin/bash
if [`/bin/ls -lt /app/datacheck/logs/dev.log | head -1 | /bin/awk '{print $5}'` -gt $((1024*1024*10)) ]
then
  echo > /app/datacheck/logs/dev.log
fi
2), 编辑定时工作
crontab -e
## 追加以下内容: 每个六个小时执行一次
0 */6 * * * sh /root/bin/clean_log.sh

(3), 将大的日志文件进行拆分

#!/bin/bash 
current_date=`date -d "-1 day" "+%Y%m%d"` 
split -b 65535000 -d -a 4 /home/alvin/nohup.out   /home/alvin/log/log_${current_date}_ 
cat /dev/null > nohup.out


# 1, 先获取以后工夫的前一天
date -d "-1 day" 示意获取前一天的日期,就是说咱们明天操作的话是切割昨天的日志。+%Y%m%d 是具体的日期格局,也就是年月日格局,比方:20181005。# 2, 而后对日志文件进行切分
其中,65535000 是 60M,也就是日志文件按 60M 大小进行切割,可自定义大小。-d -a 4 示意文件后缀是 4 位。咱们将文件切割后要按秩序进行编号,比方 0000,0001,0002……这个 4 就代表编号的位数。# 3, 将依据指定大小拆分之后的日志输入的文件中
再之后的./log/log${current_date}就是切割后日志文件的前缀,外面就带入了以后日期。所以,最终的输入格局相似于:log_20181005_0001。# 4, 最初将原始文件中内容置空

8, 循环开启端口

#!/bin/sh
Count=0;
cat ./xxx | while read line
do
    Count=`expr $Count + 1`;
    echo "Test($Count),$line...";
    firewall-cmd --add-port="${line}"/tcp --permanent;
    sleep 0.1;
done;
echo "从新加载规定:"
firewall-cmd --reload;
echo "以后已开启的端口有:"
firewall-cmd --list-port;

9, 合并清理日志文件脚本

  • 生成测试文件脚本
#!/bin/sh

# 循环生成文件,因为创立文件到指定目录,所以创立时最好用全门路;for num in {1..99}
 # 在循环内执行以下命令
 do
  touch /root/aaa/test-${num}.sh    
 done
  • 理论合并清理日志文件脚本

1,工夫格式化

  • -d “day ago” 获取前一天;不加的话就间接取当天

2,判断文件夹是否存在

3,挪动文件的时候,防止出现,挪动文件过多,导致报错(mv argument list too long 谬误)

#!/bin/sh
# 获取前一天的时候生成
TODAY_DATE=`date -d "day ago" +"%Y%m%d"`;
if [-d /root/aaa/dir-1]
 then mkdir -p /root/aaa/dir-1/bak-${TODAY_DATE};
find /root/aaa/dir-1 -type f -name '*.sh' -not -path "/root/aaa/dir-1/bak-${TODAY_DATE}*"  -exec mv {} /root/aaa/dir-1/bak-${TODAY_DATE} \;
fi

if [-d /root/aaa/dir-2]
 then mkdir -p /root/aaa/dir-2/bak-${TODAY_DATE};
find /root/aaa/dir-2 -type f -name '*.sh' -not -path "/root/aaa/dir-2/bak-${TODAY_DATE}*"  -exec mv {} /root/aaa/dir-2/bak-${TODAY_DATE} \;
fi

if [-d /root/aaa/dir-3]
 then mkdir -p /root/aaa/dir-3/bak-${TODAY_DATE};
find /root/aaa/dir-3 -type f -name '*.sh' -not -path "/root/aaa/dir-3/bak-${TODAY_DATE}*"  -exec mv {} /root/aaa/dir-3/bak-${TODAY_DATE} \;
fi

# 定时工作: 每天 0 点之执行 crontab -e
# 0 0 * * * sh /root/bin/demo02.sh
  • 补充脚本阐明
#!/bin/sh
# 获取以后工夫
TODAY_DATE=`date +"%Y%m%d"`;
if [-d /home/zazt/yuanxun/ATOSLog]
 then mkdir -p /home/zazt/yuanxun/ATOSLog/bak-${TODAY_DATE};
find /home/zazt/yuanxun/ATOSLog -maxdepth 1 -type f -name '*.json' -not -path "/home/zazt/yuanxun/ATOSLog/bak-${TODAY_DATE}*"  -exec mv {} /home/zazt/yuanxun/ATOSLog/bak-${TODAY_DATE} \;
fi

if [-d /home/zazt/yuanxun20002/ATOSLog]
 then mkdir -p /home/zazt/yuanxun20002/ATOSLog/bak-${TODAY_DATE};
find /home/zazt/yuanxun20002/ATOSLog -maxdepth 1 -type f -name '*.json' -not -path "/home/zazt/yuanxun20002/ATOSLog/bak-${TODAY_DATE}*"  -exec mv {} /home/zazt/yuanxun20002/ATOSLog/bak-${TODAY_DATE} \;
fi


if [-d /home/zazt/card/ATOSLog]
 then mkdir -p /home/zazt/card/ATOSLog/bak-${TODAY_DATE};
find /home/zazt/card/ATOSLog -maxdepth 1 -type f -name '*.json' -not -path "/home/zazt/card/ATOSLog/bak-${TODAY_DATE}*"  -exec mv {} /home/zazt/card/ATOSLog/bak-${TODAY_DATE} \;
fi


if [-d /home/zazt/review/ATOSLog]
 then mkdir -p /home/zazt/review/ATOSLog/bak-${TODAY_DATE};
find /home/zazt/review/ATOSLog -maxdepth 1 -type f -name '*.json' -not -path "/home/zazt/review/ATOSLog/bak-${TODAY_DATE}*"  -exec mv {} /home/zazt/review/ATOSLog/bak-${TODAY_DATE} \;
fi


if [-d /home/zazt/BackgroundCheck/ATOSLog]
 then mkdir -p /home/zazt/BackgroundCheck/ATOSLog/bak-${TODAY_DATE};
find /home/zazt/BackgroundCheck/ATOSLog -maxdepth 1 -type f -name '*.json' -not -path "/home/zazt/BackgroundCheck/ATOSLog/bak-${TODAY_DATE}*"  -exec mv {} /home/zazt/BackgroundCheck/ATOSLog/bak-${TODAY_DATE} \;
fi
#!/bin/sh
# 获取以后工夫
MONTH_DATE=`date +"%Y%m"`;
if [-d /home/zazt/yuanxun/ATOSLog]
 then mkdir -p /home/zazt/yuanxun/ATOSLog/month-bak-${MONTH_DATE};
 mv /home/zazt/yuanxun/ATOSLog/bak-* /home/zazt/yuanxun/ATOSLog/month-bak-${MONTH_DATE};
fi

if [-d /home/zazt/yuanxun20002/ATOSLog]
 then mkdir -p /home/zazt/yuanxun20002/ATOSLog/month-bak-${MONTH_DATE};
 mv /home/zazt/yuanxun20002/ATOSLog/bak-* /home/zazt/yuanxun20002/ATOSLog/month-bak-${MONTH_DATE};
fi

if [-d /home/zazt/card/ATOSLog]
 then mkdir -p /home/zazt/card/ATOSLog/month-bak-${MONTH_DATE};
 mv /home/zazt/card/ATOSLog/bak-* /home/zazt/card/ATOSLog/month-bak-${MONTH_DATE};
fi

if [-d /home/zazt/review/ATOSLog]
 then mkdir -p /home/zazt/review/ATOSLog/month-bak-${MONTH_DATE};
 mv /home/zazt/review/ATOSLog/bak-* /home/zazt/review/ATOSLog/month-bak-${MONTH_DATE};
fi

if [-d /home/zazt/BackgroundCheck/ATOSLog]
 then mkdir -p /home/zazt/BackgroundCheck/ATOSLog/month-bak-${MONTH_DATE};
 mv /home/zazt/BackgroundCheck/ATOSLog/bak-* /home/zazt/BackgroundCheck/ATOSLog/month-bak-${MONTH_DATE};
fi
  • 补充工夫解决参考脚本
#!/bin/sh
 
echo "打印明天之前间断的十个工夫戳"
TODAY_DATE=`date`
date_range=$(seq 10)  # 数组
#echo ${date_range[@]}
for i in ${date_range[@]}
do 
    INSTANCE_DATE=`date -d "${TODAY_DATE} $i day ago" +"%Y%m%d"`;
    echo ${INSTANCE_DATE}
done
 
echo "打印指定工夫之前的十天"
date_var=20170909
for i in $(seq 20)
do 
    current=`date -d "${date_var} $i day ago" +"%Y%m%d"`
    echo ${current}
done
 
#TODAY_DATE=`date +%Y%m%d`
#INSTANCE_DATE=`date -d "${TODAY_DATE} 2 day ago"`;
#INSTANCE_DATE_NEXT=`date -d "${INSTANCE_DATE} 1 day" +"%Y%m%d"`;
退出移动版