centos shell脚本编程1 正则 shell脚本结构 read命令 date命令的用法 shell中的逻辑判断 if 判断文件、目录属性 shell数组简单用法 $( ) 和${ } 和$(( )) 与 sh -n sh -x sh -v 第三十五节课

时间:2021-07-03 11:20:02

centos   shell脚本编程1 正则  shell脚本结构  read命令  date命令的用法  shell中的逻辑判断  if 判断文件、目录属性  shell数组简单用法 $( ) 和${ } 和$(( )) 与  sh -n  sh -x  sh -v 第三十五节课

Shebang/Sha-bang
在计算机科学中, Shebang (也称为Hashbang)是一个由井号和叹号构成的字符串行(#!), 其出现在文本文件的第一行的前两个字符.
在文件中存在Shebang的情况下, 类Unix操作系统的程序载入器会分析Shebang后的内容, 将这些内容作为解释器指令, 并调用该指令, 并将载有Shebang的文件路径作为该解释器的参数. 例如, 以指令#!/bin/sh开头的文件在执行时会实际调用/bin/sh程序.)

#!用于帮助内核找到相应的解释器,只有被直接执行的文件中才有必要加入#!

Shebang指明了执行这个脚本文件的解释程序。
1. 如果脚本文件中没有#!这一行,那么它执行时会默认用当前Shell去解释这个脚本(即:$SHELL环境变量)。
2. 如果#!之后的解释程序是一个可执行文件,那么执行这个脚本时,它就会把文件名及其参数一起作为参数传给那个解释程序去执行。
3. 如果#!指定的解释程序没有可执行权限,则会报错“bad interpreter: Permission denied”。
如果#!指定的解释程序不是一个可执行文件,那么指定的解释程序会被忽略,转而交给当前的SHELL去执行这个脚本。
4. 如果#!指定的解释程序不存在,那么会报错“bad interpreter: No such file or directory”。
注意:#!之后的解释程序,需要写其绝对路径(如:#!/bin/bash),它是不会自动到$PATH中寻找解释器的。
5. 当然,如果你使用”bash test.sh”这样的命令来执行脚本,那么#!这一行将会被忽略掉,解释器当然是用命令行中显式指定的bash。

注意:脚本写到一行最好用分号;

if ls /tmp/sdfsdf >/dev/null 2>&1 ;then echo ok;else echo notok; fi

每个结构语句后面都可以加分号表示语句的结束,但是分多行来书写不会加任何分号更加美观

#!/bin/bash
cd /etc/ ;
for a in `ls /etc/` ;
do
if [ -d /etc/$a ] ;
then
ls -d /etc/$a ;
fi ;
done ;

对比

#!/bin/bash
cd /etc/
for a in `ls /etc/`
do
if [ -d /etc/$a ]
then
ls -d /etc/$a
fi
done

上半节课
正则
shell脚本结构
date命令的用法

下半节课
shell中的逻辑判断
if 判断文件、目录属性
shell数组简单用法
sh -n sh -x  sh -v 
$( ) 和${ } 和$(( ))

内容概要:
1. shell脚本是什么
它是一种脚本语言,并非编程语言
可以使用一些逻辑判断、循环等语法
可以自定义子函数
是系统命令的集合
shell脚本可以实现自动化运维,大大增加我们的工作效率
正则
.:任意一个字符
*:结合星号前面的符号,a* 0个或多个a
+:grep不可以直接,加大E 或者egrep 或者脱义符号 grep 'a\+' 一个或多个a
?:0个或多个a
.*:贪婪匹配
():匹配,然后替换
(ab){2,4}: 出现2到4次ab
[^:]+:非冒号开头的一个或多个
([^:]+)(:.*:)([^:]+) 匹配多段
sed -r 's/([^:]+)(:.*:)([^:]+) /\3\2\1/' /etc/passwd
awk -F ':' '{print $7}' /etc/passwd

2. shell脚本结构以及执行方法以及命名规则
开头行指定bash路径: #! /bin/bash
以#开头的行作为解释说明
脚本的名字以.sh结尾,用于区分这是一个shell脚本
执行方式有两种:chmod +x 1.sh; ./1.sh 如果没有执行权限可以 bash 1.sh
bash -x 1.sh 可以查看脚本执行过程
shell的变量和函数命名不能有横杠,只能是大小写字母、 下划线、 数字  例如  check-service(){} 错误 check_service(){} 正确

3. 学会date命令的用法
命令格式:date [参数]... [+格式]
选择参数:
-d<字符串>  显示字符串所指的日期与时间。字符串前后必须加上双引号。
-s<字符串>  根据字符串来设置系统日期与时间。字符串前后必须加上双引号。

注意:如果有空格需要用双引号引起来:date +"%Y%m%d %H%M"  不用双引号date +%Y%m%d-%H%M
CST:中国时区,北京时间
EST :东部标准时间
date +%Y-%m-%d, date +%y-%m-%d 年月日
大Y 四位年 小y两位年 小m月 大M分钟 小d天 小h月 大H小时 大S秒 小s时间戳(大Y 四位年 小m月 小d天 大H小时 大M分钟 大S秒 )
date +%H:%M:%S = date +%T 时间
date +%Y-%m-%d = date +%F 年月日
date +"%Y-%m-%d %H:%M:%S" 2016-01-20 10:36:47 双引号引起来
date +%w 星期几
date +%W 第几周
date +%s 时间戳
date -d @1434248742 反推时间戳,时间戳是1434248742
date -d "+1day" +"%Y-%m-%d"一天后
date -d "-1day" +"%Y-%m-%d"一天前
date -d "-1month" +"%Y-%m-%d"一月前
date -d "-1min" +"%Y-%m-%d %H:%M:%S"一分钟前
date -d "-1year" +"%Y-%m-%d" 一年前
date -d "-1weak" +"%Y-%m-%d" 一个星期前
两部分
第一部分-d "-1weak"
第二部分+"%Y-%m-%d"

4. shell脚本中的变量
当脚本中使用某个字符串较频繁并且字符串长度很长时就应该使用变量代替
使用条件语句时,常常使用变量 if [ $a -gt 1 ]; then ... ; fi
引用某个命令的结果时,用变量替代 n=`wc -l 1.txt`或者 n=$(wc -l 1.txt)
写和用户交互的脚本时,变量也是必不可少的 read -p "Input a number: " n; echo $n 如果没写这个n,可以直接使用全局变量$REPLY
内置变量 $0, $1, $2… $0表示脚本本身,$1 第一个参数,$2 第二个 ....
$#:表示参数个数或最后一个参数
$* 所有参数列表。如"$*"用「"」括起来的情况、以"$1 $2 … $n"的形式输出所有参数。
$@ :表示以空格分隔的所有输入参数或者所有函数参数所有参数列表。如"$@"用「"」括起来的情况、以"$1" "$2" … "$n" 的形式输出所有参数。
$$:当前shell进程的pid
$!:后台运行的最后一个进程的pid
$- 使用Set命令设定的Flag一览

数学运算a=1;b=2; c=$(($a+$b))或者$[$a+$b]
变量的作用域和属性 :readonly 、local

5. shell中的逻辑判断
格式1:if 条件 ; then 语句; fi
格式2:if 条件; then 语句; else 语句; fi
格式3:if …; then … ;elif …; then …; else …; fi
格式4:if 命令;then 语句;fi
逻辑判断表达式:if [ $a -gt $b ]; if [ $a -lt 5 ]; if [ $b -eq 10 ]等 -gt (>); -lt(<); -ge(>=); -le(<=);-eq(==); -ne(!=) 注意到处都是空格 方括号两边也有空格 if [ 1 == 1 ] then xx fi
可以使用 && || 结合多个条件 -a也表示&&,-o也表示||
冒号表示真 if : then xx fi

用两个括号可以避免使用lt,gt
if (($a<3)); then echo ok;else echo nook;fi
if (($a>3)); then echo ok;else echo nook;fi

字符串比较用(==)和(!=) ,不能用-eq和 -ne
if [ "s" == "e" ]; then echo 1 ;fi
if [ "s" != "e" ]; then echo 1 ;fi

if:
格式:注意[]两边都有空格 逻辑操作符两边也要留空格
#!/bin/bash
if [ 1 == 2 ]
then
echo true;
elif [ 1 == 3 ] ;then
echo false;
else
echo error;
fi

> gt
< lt
>= ge
<= le
== eq
!= ne

if 命令 
判断的基础
1、有返回值 每个命令都有返回值
2、有正确的返回值 0是真 非0是假 echo $?

注意,if 命令不能加中括号  ,if [ 命令 ]是错的

例子

if  ping -c  192.168.1.106 >/dev/null >& ; then echo ; else echo ; fi

if sed -i 's/i/gg/g' sed.txt
if /bin/true ;
grep -q 避免输出

if [$a -lt 10] && [$a -gt 1]; then echo ok;fi
或者
if [$a -lt 10 -a $a -gt 1]; then echo ok;fi

if [$a -lt 10] || [$a -gt 1]; then echo ok;fi
或者
if [$a -lt 10 -o $a -gt 1]; then echo ok;fi

exit返回码 exit 1 ,那么echo $? 就会显示1

6. if 判断文件、目录属性
[ -f file ]判断是否是普通文件,且存在 [ ! -f file ]
[ -d file ] 判断是否是目录,且存在 [ ! -d file ]
[ -e file ] 判断文件或目录是否存在 [ ! -e file ]
[ -r file ] 判断文件是否可读 [ ! -r file ]
[ -w file ] 判断文件是否可写 [ ! -w file ]
[ -x file ] 判断文件是否可执行 [ ! -x file ]
[ -z $a ] 判断变量是否为空 [ ! -z  ]

if 简化写法
[ -f 2.sh ] && echo "2.sh exits"

[ ! -f 2.sh ] || echo "2.sh exits"

示例1

read -p  xx  n  ,n只是一个变量名,你可以用x,y,z
#!/bin/bash
read -p "input a number" n
n2=`echo $n|grep -E '[^0-9]'|wc -l`

if [ $n2 == 1 ]
then
echo " not a nubmer"
exit 1
fi

n1=$[$n%2]

if [ $n1 == 0 ]
then
echo "even"
else
echo "odd"
fi

示例2
#根据命令的返回值 echo $? echo $n|grep -q '[^0-9]' 0是真 非0是假  (if netstat -lnp |grep -Eq ':80'   精确匹配 有返回值是0 无返回值是假)
#!/bin/bash
read -p "input a number" n
#n2=`echo $n|grep -E '[^0-9]'|wc -l`
if echo $n|grep -q '[^0-9]'
then
echo " not a nubmer"
exit 1
fi

n1=$[$n%2]

if [ $n1 == 0 ]
then
echo "even"
else
echo "odd"
fi

示例2
#根据命令的返回值 echo $? echo $n|grep -q '[^0-9]' 0是真 非0是假
#!/bin/bash
read -p "input a number" n
#n2=`echo $n|grep -E '[^0-9]'|wc -l`
if echo $n|grep -q '[^0-9]'
then
echo " not a nubmer"
exit 1
fi

n1=$[$n%2]

if [ $n1 == 0 ]
then
echo "even"
else
echo "odd"
fi

示例3
[root@steven ~]# if ls /tmp/sdfsdf >/dev/null 2>&1 ;then echo ok;else echo notok; fi
notok
[root@steven ~]# if [ $? == 0 ] ;then echo ok;else echo notok; fi
ok

示例4
exec表示后面的命令的输出都重定向到/tmp/$d.log
#!/bin/bash
d=`date +%F`
exec >/tmp/$d.log 2>&1
echo "begin at `date`"
ls /tmp/sdfsdfcd
cd /sdfsdf
echo "end at `date`"


扩展阅读

http://www.jb51.net/article/56553.htm

附 表:
[ -a FILE ]  如果 FILE 存在则为真。  
[ -b FILE ]  如果 FILE 存在且是一个块特殊文件则为真。  
[ -c FILE ]  如果 FILE 存在且是一个字特殊文件则为真。  
[ -d FILE ]  如果 FILE 存在且是一个目录则为真。  
[ -e FILE ]  如果 FILE 存在则为真。  
[ -f FILE ]  如果 FILE 存在且是一个普通文件则为真。  
[ -g FILE ] 如果 FILE 存在且已经设置了SGID则为真。 [ -h FILE ]  如果 FILE 存在且是一个符号连接则为真。  
[ -k FILE ]  如果 FILE 存在且已经设置了粘制位则为真。  
[ -p FILE ]  如果 FILE 存在且是一个名字管道(F如果O)则为真。  
[ -r FILE ]  如果 FILE 存在且是可读的则为真。  
[ -s FILE ]  如果 FILE 存在且大小不为0则为真。  
[ -t FD ]  如果文件描述符 FD 打开且指向一个终端则为真。  
[ -u FILE ]  如果 FILE 存在且设置了SUID (set user ID)则为真。  
[ -w FILE ]  如果 FILE 如果 FILE 存在且是可写的则为真。  
[ -x FILE ]  如果 FILE 存在且是可执行的则为真。  
[ -O FILE ]  如果 FILE 存在且属有效用户ID则为真。  
[ -G FILE ]  如果 FILE 存在且属有效用户组则为真。  
[ -L FILE ]  如果 FILE 存在且是一个符号连接则为真。  
[ -N FILE ]  如果 FILE 存在 and has been mod如果ied since it was last read则为真。  
[ -S FILE ]  如果 FILE 存在且是一个套接字则为真。  
[ FILE1 -nt FILE2 ]  如果 FILE1 has been changed more recently than FILE2, or 如果 FILE1 exists and FILE2 does not则为真。  
[ FILE1 -ot FILE2 ]  如果 FILE1 比 FILE2 要老, 或者 FILE2 存在且 FILE1 不存在则为真。  
[ FILE1 -ef FILE2 ]  如果 FILE1 和 FILE2 指向相同的设备和节点号则为真。  
[ -o OPTIONNAME ]  如果 shell选项 “OPTIONNAME” 开启则为真。  
[ -z STRING ]  “STRING” 的长度为零则为真。  
[ -n STRING ] or [ STRING ]  “STRING” 的长度为非零 non-zero则为真。  
[ STRING1 == STRING2 ]  如果2个字符串相同。 “=” may be used instead of “==” for strict POSIX compliance则为真。  
[ STRING1 != STRING2 ]  如果字符串不相等则为真。
[ STRING1 < STRING2 ]  如果 “STRING1” sorts before “STRING2” lexicographically in the current locale则为真。  
[ STRING1 > STRING2 ]  如果 “STRING1” sorts after “STRING2” lexicographically in the current locale则为真。  
[ ARG1 OP ARG2 ] “OP” is one of -eq, -ne, -lt, -le, -gt or -ge. These arithmetic binary operators return true if “ARG1” is equal to, not equal to, less than, less than or equal to, greater than, or greater than or equal to “ARG2”, respectively. “ARG1” and “ARG2” are integers.


sh -x 调试shell脚本

sh -x .sh
+ '[' -eq ']'
+ eco
.sh: line : eco: command not found

sh -n  检查shell脚本语法

sh -n .sh

sh -v  显示整个shell脚本并执行

sh -v .sh
#!/bin/bash
if [ -eq ]
then
eco
else
echo
fi
.sh: line : eco: command not found

有时候使用-v选项会更加直观看到shell脚本的语法错误

sh -c 启动一个子进程,执行字符串里的命令,字符串后面的参数作为字符串里命令的参数

sh -c 'ls /tmp'

sh -c 'ls ' /tmp

$( ) 和${ } 和$(( )) 与差在哪  http://blog.chinaunix.net/uid-12380499-id-105461.html

$( ) 引用结果

${ }变量累加

$(( )) 整数运算

a=sss
b=${a}123
echo $b
sss123
c=$(wc -l /etc/passwd|awk '{print $1}')
echo $c
38
y=$((5+c))
echo $y
43

n=$(wc -l 1.txt)


shell数组简单用法

http://www.apelearn.com/bbs/thread-7235-1-1.html

1.数组定义, 一对括号表示是数组,数组元素用“空格”符号分割开。

# a=(1 2 3 4 5)
# echo $a
1

2. 数组读取
# echo ${#a[@]} //获取数组的元素个数
# echo ${a[2]} //读取数组中的某一个元素,数标从0开始,a[0]是第一个元素
# echo ${a[*]} //这样会打印整个数组的元素,或者使用 echo ${a[@]}

3. 数组赋值
# a[1]=100

# echo ${a[*]} 
1 100 3 4 5

# a[5]=100 
# echo ${a[*]}
1 100 3 4 5 100

直接通过 数组名[下标] 就可以对其进行引用赋值,如果下标不存在,自动添加新一个数组元素

4. 数组的删除
# a=(1 2 3 4 5)
# unset a
# echo ${a[*]}

# a=(1 2 3 4 5)
# unset a[1] 
# echo ${a[*]} 
1 3 4 5
# echo ${#a[*]}
4

5. 数组分片
# a=(`seq 1 5`)
# echo ${a[@]:0:3}
1 2 3
# echo ${a[@]:1:4}
2 3 4 5

6. 数组替换,可以echo替换,也可以直接赋值
# a=(1 2 3 4 5) 
# echo ${a[@]/3/100}
1 2 100 4 5
# echo ${a[@]}
1 2 3 4 5
# a=(${a[@]/3/100}) 
# echo ${a[@]} 
1 2 100 4 5


SHELL变量名必须遵循下面规律

1、首个字母必须为字母a-zA-Z

2、中间不能有空格,可以用下划线_

3、不能使用标点符号

4、不能用bash关键字(可用help命令查看是否保留关键字)


read命令

read命令 接受标准输入或其他文件描述符的输入,得到输入后,read命令将数据放入一个标准变量中

利用read读取文件时,每次调用read命令都会读取文件中的“一行”文本。当文件没有可读的行时,read命令以非零状态退出

示例

while read myline
do
echo "line: $myline"
done < /tmp/myfile.txt

cat /tmp/myfile.txt | while read myline
do
echo "line: $myline"
done

/usr/local/inotify/bin/inotifywait -mrq --timefmt '%d/%m/%y %H:%M' --format '%T %w%f%e' -e close_write,delete,create,attrib,move $srcdir | while read file
do
for ip in $dstip
do
rsync -avH --port= --progress --delete --exclude-from=$excludedir $srcdir $rsyncuser@$ip::$dstdir --password-file=$rsyncpassdir
echo " ${file} was rsynced" >> /tmp/rsync.log >&
done

read -p "input a number" n  或read  n  #监听终端的标准输入

当然也可以用for循环和sed -n ""p 读取文件的每行文本

  

read -p "Enter [start|stop] to start or stop redis-server:" -e -i start option

-e :使用-i 选项的默认值填充到option变量

-i start :[-i text],提示默认值是start


shell变量
http://blog.csdn.net/zhuying_linux/article/details/6633022

在shell中有3种变量:系统变量,环境变量和用户变量,其中系统变量在对参数判断和命令返回值判断时会使用,环境变量主要是在程序运行时需要
readonly

local

declare

另外,如果在赋值后不希望改变变量,使其类似于常数,则可以使用readonly命令将其设为只读。
//先赋值,再设置只读
#a=good
#readonly a
//或者直接在赋值时设置只读
#readonly a=good
此时若用unset命令重置变量a或者对a重新赋值,则会产生错误。
#unset a
-bash: a: readonly variable
#a=Good
-bash: a: readonly variable

另外,shell的变量默认是全局作用的,如果需要在一定范围内生效,则需要加上local限制。例如local a将设置a为局部变量。
Tar_Cd()
{
    local FileName=$1
    local DirName=$2
}

数字和数组的声明
默认的赋值是对字符串赋值,例如执行如下脚本,就会发现这个脚本并没有使用5和6相加输出“11”,而是输出“5+6”。
#!/bin/bash
a=5
b=6
c=$a+$b
echo $c

如果要对数字或数组进行声明,则要declare命令,例如上例改成如下形式,即可正常进行加减。

#!/bin/bash
declare -i a=5
declare -i b=6
declare -i c=$a+$b
echo $c

declare支持的声明类型如下。使用“- 类型”开启,“+ 类型”关闭。
(1)i:定义整数integer
(2)a:定义数组array
(3)f:定义函数function
(4)r:定义为只读readonly
(5)x:定义为通过环境输出变量

例如对声明数组变量的方法如下。
#!/bin/bash
declare -a arr=(a b c )


linux下时间戳转换 date
http://www.apelearn.com/bbs/forum.php?mod=viewthread&tid=8092&fromuid=4587

shell中获取时间戳的方式为:date -d "$currentTime" +%s

$ date -d @1337743485671 "+%c"
Sun 28 May 44361 12:41:11 PM CST

如果要将一个日期转为时间戳,方式如下:

1、得到当前时间

currentTime=`date "+%Y-%m-%d %H:%M:%S"`

2、将日期转为时间戳

currentTimeStamp=`date -d "$currentTime" +%s `
echo $currentTimeStamp

3.字符串转换为时间戳可以这样做:

date -d "2010-10-18 00:00:00" +%s

输出形如:

1287331200

其中,-d参数表示显示指定的字符串所表示的时间,+%s表示输出时间戳。

4.而时间戳转换为字符串可以这样做:

date -d @1287331200

输出形如:

Mon Oct 18 00:00:00 CST 2010


Shell脚本8种字符串截取方法总结
http://www.jb51.net/article/56563.htm

这篇文章主要介绍了Shell脚本8种字符串截取方法总结,每个方法给出了实例代码和操作结果,一目了然,需要的朋友可以参考下

echo ${var *  *  * }
#    匹配左边第一   留右  从左边起
## 匹配左边最后     留右  从左边起
%   匹配右边第一    留左  从右边起
%% 匹配右边最后     留左  从右边起

星号表示要删除的字符
var#  * //  删除左边字符,保留右边字符。
var## * /
var%   /  *
var%%  /  *

Linux 的字符串截取很有用。有八种方法。
假设有变量 var=http://www.aaa.com/123.htm

1. # 号截取,删除左边字符,保留右边字符。

var=http://www.aaa.com/123.htm
echo ${var#*//}

其中 var 是变量名,# 号是运算符,*// 表示从左边开始删除第一个 // 号及左边的所有字符
即删除 http://
结果是 :www.aaa.com/123.htm

2. ## 号截取,删除左边字符,保留右边字符。

var=http://www.aaa.com/123.htm
echo ${var##*/}

##*/ 表示从左边开始删除最后(最右边)一个 / 号及左边的所有字符
即删除 http://www.aaa.com/
结果是 123.htm

3. %号截取,删除右边字符,保留左边字符

var=http://www.aaa.com/123.htm
echo ${var%/*}

%/* 表示从右边开始,删除第一个 / 号及右边的字符
结果是:http://www.aaa.com

4. %% 号截取,删除右边字符,保留左边字符

var=http://www.aaa.com/123.htm
echo ${var%%/*}

%%/* 表示从右边开始,删除最后(最左边)一个 / 号及右边的字符
结果是:http:

5. 保留/打印从左边第几个字符开始,及字符的个数

var=http://www.aaa.com/123.htm
echo ${var::}

其中的 0 表示左边第一个字符开始打印,5 表示字符的总个数。
结果是:http:

6. 从左边第几个字符开始,一直到结束。

var=http://www.aaa.com/123.htm
echo ${var:}

其中的 7 表示左边第8个字符开始,一直到结束。
结果是 :www.aaa.com/123.htm

7. 从右边第几个字符开始,及字符的个数

var=http://www.aaa.com/123.htm
echo ${var:-:}

其中的 0-7 表示右边算起第七个字符开始,3 表示字符的个数。
结果是:123

8. 从右边第几个字符开始,一直到结束。

var=http://www.aaa.com/123.htm
echo ${var:-}

表示从右边第七个字符开始,一直到结束。
结果是:123.htm
注:(左边的第一个字符是用 0 表示,右边的第一个字符用 0-1 表示)


花括号批量创建文件或目录

mkdir -p man/mm{1..6} 1..6 中间一定要是两点 !!!!!!
ll
total 24
drwxr-xr-x 2 root root 4096 Apr 11 08:41 mm1
drwxr-xr-x 2 root root 4096 Apr 11 08:41 mm2
drwxr-xr-x 2 root root 4096 Apr 11 08:41 mm3
drwxr-xr-x 2 root root 4096 Apr 11 08:41 mm4
drwxr-xr-x 2 root root 4096 Apr 11 08:41 mm5
drwxr-xr-x 2 root root 4096 Apr 11 08:41 mm6


括号包起来的命令会产生一个子shell,在这个子shell里面执行的操作不会影响到我们当前shell
(umask 077)

f