简单的Shell脚本

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
简介:

shell脚本的基本结构以及如何执行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
[root@localhost script] # cat 1.sh 
#!/bin/bash
#The first shell script
#Writen by Justin 2015-01-07
date
echo  "Hello World"
[root@localhost script] # ./1.sh
- bash : . /1 .sh: Permission denied
[root@localhost script] # sh 1.sh 
Wed Jan  7 05:15:38 CST 2015
Hello World
[root@localhost script] # chmod +x 1.sh 
[root@localhost script] # ./1.sh
Wed Jan  7 05:15:50 CST 2015
Hello World
[root@localhost script] # sh -x 1.sh 
date
Wed Jan  7 07:29:29 CST 2015
echo  'Hello World'
Hello World
[root@localhost script] #

以上为一个简单的shell脚本和执行,Shell脚本通常都是以.sh 为后缀名,不是说不带.sh就不是脚本,“#! /bin/bash” 它代表的意思是,该文件使用的是bash语法,其中#表示该行是注释,叹号“!”告诉shell运行叹号之后的命令并用文件的其余部分作为输入,也就是运行/bin/bash并让/bin/bash去执行shell程序的内容。后面跟一些该脚本的相关注释内容以及作者和创建日期或者版本等等,这些注释并非必须的,可以省略掉,但是不建议省略。因为随着你工作时间的增加,你写的shell脚本也会越来越多,如果有一天你回头查看你写的某个脚本时,很有可能忘记该脚本是用来干什么的以及什么时候写的,所以写上注释是有必要的。Shell脚本的执行很简单,直接”sh filename “ 即可,也可以加一个执行权限,直接使用’./filename’ 执行这个脚本。

另外使用sh命令去执行一个shell脚本的时候是可以加-x选项来查看这个脚本执行过程,也可以在脚本里写上set -x或者set -xv,这样只执行脚本时候就会显示执行的每条命令,这样有利于我们调试这个脚本哪里出了问题。

使用-n可以检查是否有错误

1
2
3
[root@localhost src] # sh -n install-tomcat.sh 
install -tomcat.sh: line 72: syntax error: unexpected end of  file
[root@localhost src] #

syntax error: unexpected end of file:

如果是在windows环境下编写的shell脚本上传到linux下需要把dos文件转换成unix文件格式,否则会出现报错:syntax error: unexpected end of file:

dos格式文件传输到unix系统时,会在每行的结尾多一个^M,而linux下的是没有的

1
2
3
4
5
6
7
8
[root@localhost ~] # cat -A linux.txt
linux$
windows$
[root@localhost ~] # cat -A windows.txt
windows^M$
linux                      最后一行行末没有换行符;
[root@localhost ~]
[root@localhost ~] #

vim windows.txt末尾提示[noeol] 120L, 2532C 信息,'noeol' 就是 'no end-of-line', 即“没有行末结束符”,vim windows.txt,不做任何修改直接 :wq保存退出换行符已经追加上去,

如果提示 "dos.txt" [dos] 120L, 2532C 字样,表示是一个[dos]格式文件,如果是MAC系统的,会显示[MAC],因为文件格式的原因有时会导致我们的unix程序,或者shell程序出现错误,那么需要把这些dos文件格式转换成unix格式,方法是
 
    vi   dos.txt         
     :set fileformat=unix
 
    :w                  
    这样文件就转换成unix格式文件了,或者使用dos2unix

     yum -y install dos2unix

    dos2unix filename.sh

如果还是报错syntax error: unexpected end of file检查语法

出现中文乱码的问题

此问题是因执行定时任务时没有去获取系统的环境变量,导致了中文乱码。在shell脚本开始的时候加下命令:export LANG="en_US.UTF-8"

1
2
#!/bin/sh
export  LANG= "en_US.UTF-8"

crontab 里脚本不执行

手动执行脚本正常,加入到crontab里后脚本不执行,这是因为crontab没有读取环境变量,脚本中的部分命令不是用的绝对路径无法找到,可以在脚本开头读取环境变量或者所有命令使用绝对路径

1
2
[ -f ~/.bash_profile ] && . ~/.bash_profile
[ -f  /etc/profile ] &&  .  /etc/profile


shell脚本中的变量

$? 是显示最后命令的退出状态,0表示没有错误,其他表示有错误


$$ Shell本身的PID(ProcessID)

$! Shell最后运行的后台Process的PID

$- 使用Set命令设定的Flag一览

$# 是传给脚本的参数个数

$0 是脚本本身的名字

$1 是传递给该shell脚本的第一个参数

$2 是传递给该shell脚本的第二个参数

$$ 是脚本运行的当前进程ID号

$* 是以一个单字符串显示所有向脚本传递的参数,与位置变量不同,参数可超过9个

$@:是传给脚本的所有参数的列表,即被扩展为"$1" "$2" "$3"等;

$*:是以一个单字符串显示所有向脚本传递的参数,与位置变量不同,参数可超过9个,即被扩展成"$1c$2c$3",其中c是IFS的第一个字符;


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[root@localhost script] # cat 2.sh 
#!/bin/bash
#This script,we will use variables.
#Write by Justin 2015-01-07
x=` date  +%H:%M:%S`
echo  "the script begin at $x"
echo  "The script end after 2 seconds"
sleep  2
y=` date  +%H:%M:%S`
echo  "The script end at $y" 
[root@localhost script] # sh 2.sh 
the script begin at 14:22:08
The script end after 2 seconds
The script end at 14:22:10
[root@localhost script] #

脚本中调用了命令date所以使用了反引号,在调用变量时需要加上符号$,这个和在shell中定义变量是一致的。

1
2
3
4
5
6
7
8
9
[root@localhost script] # cat 3.sh 
#!/bin/bash
a=1
b=2
sum =$[$a+$b]
echo  "sum is $sum"
[root@localhost script] # sh 3.sh 
sum  is 3
[root@localhost script] #

数学计算要用’[ ]’括起来并且外头要带一个’$’。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
[root@localhost script] # cat 4.sh 
#!/bin/bash
echo  "please input a number:"
read  x
echo  "please input another number:"
read  y
sum =$[$x+$y]
echo  "The sum of tow number is:$sum"
 
[root@localhost script] # sh 4.sh 
please input a number:
3
please input another number:
5
The  sum  of tow number is:8
[root@localhost script] # sh -x 4.sh 
echo  'please input a number:'
please input a number:
read  x
3   
echo  'please input another number:'
please input another number:
read  y
5
sum =8
echo  'The sum of tow number is:8'
The  sum  of tow number is:8
[root@SAMBA1 infa_shared] # echo `date +"%Y-%m-%d %H:%M:%S"` > read-only.txt 
[root@SAMBA1 infa_shared] # cat read-only.txt 
2015-11-25 15:56:56
[root@SAMBA1 infa_shared] # y=`date +"%Y-%m-%d %H:%M:%S"`
[root@SAMBA1 infa_shared] # echo $y
2015-11-25 15:58:27
[root@SAMBA1 infa_shared] #

Shell脚本可以和用户交互。这就用到了read命令了,它可以从标准输入获得变量的值,后跟变量名。”read x”表示x变量的值需要用户通过键盘输入得到。我们可以直接使用read -p 来代替echo的作用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[root@localhost script]# cat  5 .sh 
#!/bin/bash
read -p  "please input a number:"  x     ;x前有个空格
read -p  "please input another number:"  y
sum=$[$x+$y]
echo  "The sum of tow number is:$sum"
 
[root@localhost script]# sh -x  5 .sh 
+ read -p  'please input a number:'  x
please input a number: 3
+ read -p  'please input another number:'  y
please input another number: 5
+ sum= 8
+ echo  'The sum of tow number is:8'
The sum of tow number  is : 8
[root@localhost script]#

”/etc/init.d/iptables restart “ 前面的/etc/init.d/iptables 就是一个shell脚本,后面”restart”是了shell脚本的预设变量。上面例子我们可以通过设置预设变量

1
2
3
4
5
6
7
8
9
10
11
12
[root@localhost script] # cat 5.sh 
#!/bin/bash
sum =$[$1+$2]
echo  "The sum of tow number is:$sum"
echo  "$0"
[root@localhost script] # sh -x 5.sh 3 5
sum =8
echo  'The sum of tow number is:8'
The  sum  of tow number is:8
echo  '5.sh'
5.sh
[root@localhost script] #

$1和$2就是shell脚本的预设变量,其中$1的值就是在执行的时候输入的3,而$2的值就是执行的时候输入的5,一个shell脚本的预设变量是没有限制的,$0代表的是脚本本身的名字。

set -x与set +x指令

用于脚本调试。set是把它下面的命令打印到屏幕set -x 是开启 set +x是关闭 set -o是查看 (xtrace),set去追中一段代码的显示情况。

1
2
set  -x
/usr/local/bin/sendEmail  -s  "$SMTP_Server"  -xu  "$username"  -xp  "$password"  -f  "$from_email_address"  -t  "$to_email_address"  -u  "$message_subject_utf8"  -m  "$message_body_utf8"  -o message-content- type =text -o message-charset=utf-8


shell脚本中的逻辑判断

if判断语句:

1)不带else

格式:

if 判断语句; then

command

fi

1
2
3
4
5
6
7
8
9
10
[root@localhost script] # cat if1.sh 
#! /bin/bash
read  -p  "please input your score:"  a
if  ((a<60)); then
     echo  "You didn't pass the exam.you score is $a"
fi
[root@localhost script] # sh if1.sh 
please input your score:33
You didn't pass the exam.you score is 33
[root@localhost script] #

上面出现了 ((a<60))这样的形式,这是shell脚本中判断数值大小特有的格式,用一个小括号或者不用都会报错,请记住这个格式,在判断数值大小除了可以用”(( ))”的形式外,还可以使用”[ ]”。但是就不能使用>, < , = ,!=这样的符号了,要使用 -lt (小于),-gt (大于),-le (小于等于),-ge (大于等于),-eq (等于),-ne (不等于),这种类型在while循环中使用多

2)带有else

格式:

if 判断语句 ; then

command

else

command

fi

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[root@localhost script] # cat if1.sh 
#! /bin/bash
read  -p  "please input your score:"  a
if  ((a<60)); then
     echo  "You didn't pass the exam.you score is $a"
else
     echo  "GOOD! You passed the exam,you score is $a."
fi
[root@localhost script] # sh if1.sh 
please input your score:67
GOOD! You passed the exam,you score is 67.
[root@localhost script] # sh if1.sh 
please input your score:33
You didn't pass the exam.you score is 33
[root@localhost script] #
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
linux-gnv2: /opt/FlashServer/flashserver  # cat restart.sh 
#!/bin/bash
x=` ps  -ef| grep  'flashserver' | grep  - v  grep | grep  - v  nohup | awk  -F  " "  '{print $2}' `
y=` ps  -ef| grep  'flashserver' | grep  - v  grep | grep  - v  nohup | wc  -l`
if  [ $y - ge  1 ]
then
         sudo  kill  -9 $x
         sleep  2
else
         echo  "no service exist"
fi
nohup  . /flashserver  > /dev/null  &
y=` ps  -ef| grep  'flashserver' | grep  - v  grep | grep  - v  nohup | wc  -l`
sleep  1
if  [ $y - ge  1 ]
then
         echo  "start service success"
else
         echo  "start service fail"
fi
linux-gnv2: /opt/FlashServer/flashserver  #

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
[root@QuoteService Release] # cat RestartQuoteServer.sh 
#!/bin/bash
x=`pgrep QuotePlatform`
y=` ps  -ef| grep  'QuotePlatform' | grep  - v  grep | wc  -l`
z= "/usr/local/QuoteService/make/Release"
if  [ $y - ge  1 ]; then
    kill  -9 $x
    sleep  2
    yy=` ps  -ef| grep  'QuotePlatform' | grep  - v  grep | wc  -l`    #pgrep QuotePlatform是变量,每次应用都需要重新定义变量,不重新定义就是第一次赋予的值
    if  [ $yy - le  0 ]; then
       echo  "QuotePlatform server is not running!" 
       cd  $z
       nohup  . /QuotePlatform  > /dev/null  &
       sleep  3
       yyy=` ps  -ef| grep  'QuotePlatform' | grep  - v  grep | wc  -l`
       if  [ $yyy - ge  1 ]; then
         echo  "QuotePlatform service successfully started!"
         else
         echo  "QuotePlatform service startup failed! "
       fi
    else
      echo  "Kill QuotePlatform service is failed!"
    fi
else
   echo  "QuotePlatform server is not running!"
   cd  $z
   nohup  . /QuotePlatform  > /dev/null  &
   sleep  3
   yyyy=` ps  -ef| grep  'QuotePlatform' | grep  - v  grep | wc  -l`
   if  [ $yyyy - ge  1 ]; then
     echo  "QuotePlatform service successfully started!"
     else
      echo  "QuotePlatform service startup failed! "
    fi
fi

多判断条件:同时满足三个文件大小

1
2
3
4
5
6
7
8
9
10
11
12
13
[root@localhost src] # cat test.sh 
#!/bin/bash
nagiossize=` du  -k nagios-plugins-1.4.16. tar .gz| awk  '{print $1}' `
nrpesize=` du  -k nrpe-2.15. tar .gz| awk  '{print $1}' `
zabbixsize=` du  -k zabbix-2.2.2. tar .gz| awk  '{print $1}' `
if  [ $zabbixsize - ge  14200 ] && [ $nagiossize - ge  2000 ] && [ $nrpesize - ge  400 ]; then
     echo  "download is successful"
else
     echo  "download is failed"
fi
[root@localhost src] # sh test.sh 
download is successful
[root@localhost src] #


3)带有elif

格式:

if [ expression 1 ]

then

   Statement(s) to be executed if expression 1 is true

elif [ expression 2 ]

then

   Statement(s) to be executed if expression 2 is true

elif [ expression 3 ]

then

   Statement(s) to be executed if expression 3 is true

else

   Statement(s) to be executed if no expression is true

fi

哪一个 expression 的值为 true,就执行哪个 expression 后面的语句;如果都为 false,那么不执行任何语句。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
[root@localhost script] # cat if1.sh 
#! /bin/bash
read  -p  "please input your score:"  a
if  ((a<60)); then
     echo  "You didn't pass the exam.you score is $a"
elif  ((a>60)) && ((a<85)); then
     echo  "GOOD! You passed the exam,you score is $a."
else
     echo  "Very Good! You score is $a,it's very hight!"
fi
[root@localhost script] # sh if1.sh 
please input your score:33
You didn't pass the exam.you score is 33
[root@localhost script] # sh if1.sh 
please input your score:77
GOOD! You passed the exam,you score is 77.
[root@localhost script] # sh if1.sh 
please input your score:99
Very Good! You score is 99,it's very hight!
[root@localhost script] #

逻辑运算符&& 表示“并且”, || 表示“或者”。
判断文件(夹)是否存在

注意:单引号和双引号的区别。单引号告诉shell忽略所有特殊字符,而双引号忽略大多数,但不包括$、\、`,即双引号保有变量的内容,但单引号内仅能是 一般字符 ,而不会有特殊符号。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#!/bin/bash  
myPath= "/usr/local/src/dir/test"
myFile= "/usr/local/src/access.log" 
#这里的-d参数判断$myPath是否存在,注意[]前后空格
if  [ -d   "$myPath"  ]; then
  cd  "$myPath"
  tar  -cvf myPath. tar  *
else
    mkdir  -p  /usr/local/src/dir/test
    echo  "$myPath is created"
    touch  /usr/local/src/dir/test/1
    touch  /usr/local/src/dir/test/2
    touch  /usr/local/src/dir/test/3
fi
1
2
3
4
5
6
7
8
9
10
#!/bin/bash  
myPath= "/usr/local/src/dir/test"
myFile= "/usr/local/src/access.log"
#这里的-f参数判断$myPath是否存在  
if  [ ! -f   "$myFile"  ]; then
    touch  "$myFile"
    echo  "test"  "$myFile"
else
    cat  "$myFile"
fi
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#!/bin/bash  
#这里的-x参数判断$myPath是否存在并且有可执行权限  
if  [ ! -x   '/usr/local/src/'  ]; then
    chmod  +x  /usr/local/src/
    ll -d  /usr/local/src/
else
    cd  "$myPath"  &&  tar  -cvf  tar . tar  /usr/local/src/ *
    tar  -tvf  tar . tar
fi
 
#!/bin/bash
MyFile=` command  - v  ntpdate`
if  [ ! -x  "$MyFile"  ]; then
yum -y  install  ntp
cat  >>  /var/spool/cron/root  << EOF
0 * /12  * * *   /usr/sbin/ntpdate  cn.pool.ntp.org
EOF
/sbin/hwclock  --systohc
/etc/init .d /crond  restart
else
cat  >>  /var/spool/cron/root  << EOF
0 * /12  * * *   /usr/sbin/ntpdate  cn.pool.ntp.org
EOF
/sbin/hwclock  --systohc
/etc/init .d /crond  restart
fi

1
2
3
4
5
6
7
#!/bin/bash 
#两个变量判断是否相等
if  "$var1"  ==  "$var2"  ];  then
echo  '$var1 eq $var2'
else
echo  '$var1 not eq $var2'
fi

其他参数:

[ -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 存在且大小不为o则为真。

[ -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则为真。


判断文件是否存在特定字符串grep -q 

1
2
3
4
5
6
7
[root@finchina ~] # cat a.txt 
nihao
nihaooo
hello
[root@finchina ~] # if grep -q hellooooo a.txt ;then echo 'hello is exist';else echo 'hello is not exist';fi
hello is not exist
[root@finchina ~] #

case判断语句

格式:

case 变量 in

value1)

command

;;

value2)

command

;;

value3)

command

;;

*)

command

;;

esac

上面的结构中,不限制value的个数,*则代表除了上面的value外的其他值,case行尾必须为单词“in”,每一个模式必须以右括号“)”结束。双分号“;;”表示命令序列结束。匹配模式中可是使用方括号表示一个连续的范围,如[0-9];使用竖杠符号“|”表示或。最后的“*)”表示默认模式,当使用前面的各种模式均无法匹配该变量时,将执行“*)”后    的命令序列。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
[root@localhost script] # cat case.sh 
#!/bin/bash
read  -p  "please input:"  n
case  $n  in
[0-9]*)
echo  "You input a number:$n"
;;
[a-z]*|[A-Z]*)
echo  "You input a letter:$n"
;;
*)
echo  "You input is not a digital or not characters:$n"
;;
esac
[root@localhost script] # sh case.sh 
please input:111112222
You input a number:111112222
[root@localhost script] # sh case.sh 
please input:afdfdaf
You input a letter:afdfdaf
[root@localhost script] # sh case.sh 
please input:**(*
You input is not a digital or not characters:**(*
[root@localhost script] #

case脚本常用于编写系统服务的启动脚本,例如/etc/init.d/iptables中就用到了


read命令:

read命令接收标准输入(键盘)的输入,或其他文件描述符的输入(后面在说)。得到输入后,read命令将数据放入一个标准变量中。

语法

read (选项) (参数)

主要参数:

-t  等待时间,eg、read -t 5 -p "please enter your name:" name

1
read  -t 5 -p  "please enter your name:"  name

-p 允许在read命令行中直接指定一个提示

1
2
read  -p  "Enter your name:"  name
echo  "hello $name, welcome to my program"

-n 计算输入字符数  

1
read  -n1 -p  "Do you want to continue [Y/N]?"  answer

指示read命令只要接受到一个字符就退出。只要按下一个字符进行回答,read命令立即接受输入并将其传给变量。无需按回车键

1
2
3
4
5
6
7
8
9
10
11
#!/bin/bash
read  -n1 -p  "Do you want to continue [Y/N]?"  answer
case  $answer  in
Y | y)
       echo  "fine ,continue" ;;
N | n)
       echo  "ok,good bye" ;;
*)
      echo  "error choice" ;;
esac
exit  0

-s 选项能够使read命令中输入的数据不显示在监视器上(实际上,数据是显示的,只是 read命令将文本颜色设置成与背景相同的颜色)。

1
read   -s  -p  "Enter your password:"  pass


shell脚本中的循环

for循环

结构 :

for 变量名 in 循环的条件; do

command

done

for ((初始语句;执行条件;增量 ));do

command

done

执行顺序:1、初始语句  2、执行条件是否符合?  3、循环体  4、增加增量 

初始化语句只在循环开始前执行一次,每次执行循环体时要先判断是否符合条件,如果循环条件还会true,则执行循环体,在执行迭代语句。

所以对于for循环,循环条件总比循环体多执行一次。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
[root@localhost script] # cat for.sh 
#!/bin/bash
#for i in `seq 0 5`    #这里是反引号
#for i  in {0..5}
for  in  0 1 2 3 4 5
do
echo  $i
done
[root@localhost script] # sh for.sh 
0
1
2
3
4
5
[root@localhost script] # cat for.sh 
#!/bin/bash
for  in  ` tail  -1  /etc/passwd `
do
x=` echo  $i| awk  -F ':'  '{print $1"\t"$7}' `
echo  $x
done
[root@localhost script] # sh for.sh 
tcpdump  /sbin/nologin
[root@localhost script] # cat for.sh   #禁止udp对外发包
#!/bin/bash
dns=` grep  -i nameserver  /etc/resolv .conf| awk  '{print $NF}' `
for  in  $dns
do
iptables -A OUTPUT -p udp -d $i --dport 53 -j ACCEPT
done
iptables -A OUTPUT -p udp -j DROP
service iptables save
[root@Zabbix ~] # sh for.sh 
iptables: Saving firewall rules to  /etc/sysconfig/iptables :[  OK  ]
[root@Zabbix ~] #

seq 0 5 表示从0到5的一个序列,seq默认从0开始,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[root@localhost script] # cat for.sh 
#!/bin/bash
for  ((i=1;i<=19;i++))
do
  if ((i%3==0)); then
echo  $i
  fi
done
[root@localhost script] # sh for.sh 
3
6
9
12
15
18
[root@localhost script] #

for语法循环有点像C语法,但记得双括号

循环控制break/continue

    break和continue都是用来控制循环结构的,主要是停止循环。

    break用于完全结束一个循环,跳出循环体,不再执行后面循环

    continue只是终止本次循环,接着还执行后面的循环。只是跳过当次循环中剩下的语句,但是会继续执行下一次循环。

1
2
3
4
5
6
7
8
9
10
11
[root@localhost src] # cat for.sh 
#!/bin/bash
for  ((i=7000;i<8101;i++))
do 
    if  [ $i - ge  7101 ] && [ $i - le  7999 ]; then
       continue
    else
        echo  $i 
    fi
done
[root@localhost src] #

这里执行的结果是输出7000-7100、8000-8100,当循环到7101时候continue会终止这次7101的循环,后面一次类推,如果continue换成break的话只输出7000-7100,当循环到7101时候if判断为真,执行break跳出当前循环,如果这里for的外面还有一个循环体系,要跳出该循环体系的话使用break 2;一次类推break 3 ....

while循环

格式:

while 条件; do

command

done

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
[root@localhost script] # cat while.sh 
#!/bin/bash
min=1
max=20
#while (($min<=$max))
while  [ $min - le  $max ]    ;[后面、]前面有个空格
do 
echo  $min
min=$[$min+1]
done 
[root@localhost script] # sh while.sh 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
[root@localhost script] #


.循环控制语句 
# break 命令不执行当前循环体内break下面的语句从当前循环退出. 
# continue 命令是程序在本循体内忽略下面的语句,从循环头开始执行

while 逐行读取文件While read LINE

while循环中执行效率最高,最常用的方法:

While read LINE

do

echo $LINE

done  < $FILENAME

}

1
2
3
4
5
6
7
8
9
10
11
12
13
[root@token1 git] # cat while.sh
#!/bin/bash
ls  -l  /app/git | grep  - v  total| awk  '{print $NF}'  /tmp/git .txt
while  read  line
do
   cd  /app/git/ $line
   git add .
   git commit -am  "add new repo $line"  
   git remote  rm  origin
   git remote add origin git@192.168.100.243:$line.git
   git push origin master
done  /tmp/git .txt
[root@token1 git] #

或者

1
2
3
4
5
6
7
8
9
10
11
12
13
[root@token1 git] # cat while.sh
#!/bin/bash
ls  -l  /app/git | grep  - v  total| awk  '{print $NF}'  /tmp/git .txt
cat  /tmp/git .txt| while  read  line
do
   cd  /app/git/ $line
   git add .
   git commit -am  "add new repo $line"  
   git remote  rm  origin
   git remote add origin git@192.168.100.243:$line.git
   git push origin master
done
[root@token1 git] #

对循环重定向的输入可适用于循环中的所有需要从标准输入读取数据的命令;

对循环重定向的输出可适用于循环中的所有需要向标准输出写入数据的命令;

当在循环内部显式地使用输入或输出重定向,内部重定向覆盖外部重定向。

read通过输入重定向,把file的第一行所有的内容赋值给变量line,循环体内的命令一般包含对变量line的处理;然后循环处理file的第二行、第三行。。。一直到file的最后一行。还记得while根据其后的命令退出状态来判断是否执行循环体吗?是的,read命令也有退出状态,当它从文件file中读到内容时,退出状态为0,循环继续惊醒;当read从文件中读完最后一行后,下次便没有内容可读了,此时read的退出状态为非0,所以循环才会退出。


Tips:如果脚本你是直接在windows下通过记事本类工具编写传到linux上给了执行权限后使用./来执行发现无法执行,而通过sh就可以

1
2
3
4
5
6
7
8
[root@localhost src] # chmod +x linux_nagios_client.sh 
[root@localhost src] # ./linux_nagios_client.sh 
- bash : . /linux_nagios_client .sh:  /bin/bash ^M: bad interpreter: No such  file  or directory
[root@localhost src] # sh linux_nagios_client.sh 
Loaded plugins: fastestmirror, product- id , subscription-manager
This system is not registered to Red Hat Subscription Management. You can use subscription-manager to register.
Loading mirror speeds from cached hostfile
Setting up Group Process

出现上面错误的原因之一是脚本文件是DOS格式的, 即每一行的行尾以\r\n来标识, 使用vim编辑器打开脚本, 运行:

:set ff?

可以看到DOS或UNIX的字样. 使用set ff=unix把它强制为unix格式的, 然后存盘退出, 即可.

切换用户并执行命令

1
2
3
4
5
6
7
8
#!/bin/bash
su  - oracle << EOF
salplus / as sysdba
startup
exit
EOF
#su - oracle -c "salplus / as sysdba && startup"
lsnrctl start


添加脚本运行的日志

  • 有时候脚本运行我们希望看到运行的记录情况,这时候我们可以在脚本里定义将运行的结果写入到日志里。

在脚本开头定义日志信息

1
2
3
4
5
#!/bin/bash
LOGFILE= "/var/log/sendEmail.log"
:> "$LOGFILE"
exec  1>> "$LOGFILE"
exec  2>&1

注意:添加以上行后所有标准输出信息都重定向到/var/log/sendEmail.log文件中,界面中无法看到输出信息,如果有交互的操作也无法看到提示信息,此时应避免这种方式

exec 1:0(stdin,标准输入)、1(stdout,标准输出)、2(stderr,标准错误输出)


冒号在shell中表示空指令

: >file   清空文件file的内容


设定缺省值(:=): 1.1 未定义时, 生成缺省值,: ${VAR:=DEFAULT} 当变量VAR没有声明或者为NULL时,将VAR设置为默认值DEFAULT。如果不在前面加上:命令,那么就会把${VAR:=DEFAULT}本身当做一个命令来执行,报错是肯定的。

                1.2 空值时, 有冒号就生成缺省值;

                1.3 有值时, 不覆盖.

缺省值(:-):      2.1 未定义时, 原变量str不会变; 返回值var可覆盖;

                 2.2 空值时, 变量str不会变; 

                  2.3 有值时, 不覆盖.

覆盖缺省值(:+): 3.1 未定义时, 原变量str, 返回值var不会变; 

                 3.2 空值时, 原变量str不会变;

                 3.3 有值时, 覆盖变量str的缺省值

注意:

1. =, -有值时, 原变量不变, 只有:+才能覆盖;

2. +, -无值时, 原变量str仍为空.


  • 如果需要把信息打印屏幕同时写入日志可以使用mkfifo来实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#!/bin/bash
export  LANG= "en_US.UTF-8"
error_log= '/tmp/error_log.log'
info_log= '/tmp/info_log.log'
:> "$error_log"
:> "$info_log"
rm  -rf  /tmp/info .fifo
rm  -rf  /tmp/error .fifo
#exec 1>>"$info_log"
#exec 2>>"$error_log"
mkfifo  /tmp/info .fifo
mkfifo  /tmp/error .fifo
cat  /tmp/info .fifo |  tee  -a  "${info_log}"  &
exec  1> /tmp/info .fifo
cat  /tmp/error .fifo |  tee  -a  "${error_log}"  &
exec  2> /tmp/error .fifo
date  "+%Y-%m-%d %H:%M:%S"  > $error_log
date  "+%Y-%m-%d %H:%M:%S"  > $info_log
#####################################

mkfifo info.fifo

mkfifo error.fifo

#创建管道文件

cat info.fifo | tee -a info.log &

exec 1>info.fifo   

#把执行过程输出到info文件中

cat error.fifo | tee -a error.log &

exec 2>error.fifo 

#把报错输出到error文件中


printf "\015"  #结束从管道文件中获取信息



自定义函数

当我们要在脚本中多次使用相同一组命令时,可以将这组命令定义成一个函数来调用。

注意:所有函数在使用前必须定义。这意味着必须将函数放在脚本开始部分,直至shell解释器首次发现它时,才可以使用。调用函数仅使用其函数名即可。

shell中函数的定义格式如下:

1
2
3
4
5
6
7
函数名(){               #function 函数名(){   可在函数名前加上关键字function
     command1
     command2
     ...
     commandN
     return  value ]
}

例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[root@localhost ~] # cat function.sh 
#!/bin/bash
function  test (){
                 echo  "This is your first shell function!"
                }
echo  "Function begin..."
test
echo  "Function begin..."
[root@localhost ~] # sh function.sh 
Function begin...
This is your first shell  function !
Function begin...
[root@localhost ~] # echo $?
0
[root@localhost ~] #

函数返回值,可以显示增加return语句;如果不加,则将最后一条命令运行结果作为返回值(一般为0,如果执行失败则返回错误代码)。 return后跟数值(0-255)。

在Shell中,调用函数时可以向其传递参数。在函数体内部,通过 $n 的形式来获取参数的值,例如,$1表示第一个参数,$2表示第二个参数...当n>=10时,需要使用${n}来获取参数。

例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[root@localhost ~] # cat function.sh 
#!/bin/bash
function  test (){
                 echo  "This first number is $1"
                 echo  "This second number is $2"
                 echo  "This third number is $3"
                 return  $(($1+$2+$3))
                }
test  1 2 3
echo  "The sum of Three numbers is $? !"
[root@localhost ~] # sh function.sh 
This first number is 1
This second number is 2
This third number is 3
The  sum  of Three numbers is 6 !
[root@localhost ~] #

shell中的函数可以带参数调用,各个输入参数直接用空格分隔。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[root@localhost ~] # cat function.sh 
#!/bin/bash
function  test (){
                 echo  "This first number is $1"
                 echo  "This second number is $2"
                 echo  "This third number is $3"
                 return  $(($1+$2+$3))
                }
total=$( test  4 5 6)
echo  -e  "$total\n return is $? !"
[root@localhost ~] # sh function.sh 
This first number is 4
This second number is 5
This third number is 6
  return  is 15 !
[root@localhost ~] #

定义函数可以与系统命令相同,shell搜索命令时候,首先会在当前的shell文件定义好的地方查找,找到直接执行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
[root@localhost ~] # cat function.sh 
#!/bin/bash
echo  "$(uname)"
function  uname (){
                 echo  "This first number is $1"
                 echo  "This second number is $2"
                 echo  "This third number is $3"
                 return  $(($1+$2+$3))
                }
total=$( uname  4 5 6)
echo  -e  "$total\n return is $? !"
[root@localhost ~] # sh function.sh 
Linux
This first number is 4
This second number is 5
This third number is 6
  return  is 15 !
[root@localhost ~] #

在函数调用之前定义变量,该变量是全局变量,如果定义的变量只对某个函数定义,可以在函数中定义:local 变量=值 ,这时变量就是内部变量,它的修改,不会影响函数外部相同变量的值 。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[root@localhost ~] # cat function.sh 
#!/bin/bash
num=100
function  num1(){
                 local  num=10
                 echo  "$num"
                }
function  num2(){
                 echo  "$num"
                }
num1
num2
[root@localhost ~] # sh function.sh
10
100
[root@localhost ~] #


shell切换用户执行命令,执行完切换当前用户

1
2
3
4
5
6
7
8
9
10
11
12
[root@GIT gitosis-admin] # vim create_repo.sh 
 
#!/bin/bash
echo  -e  "Please input repo name \033[031m(Format:dirnam.git)\033[0m:"
read  name
su  git << EOF
cd  /home/gitrepository/ ;
mkdir  $name;
cd  $name;
git init --bare;
exit ;
EOF

[root@GIT gitosis-admin]# vim create_repo.sh 

#!/bin/bash

echo -e "Please input repo name \033[031m(Format:dirnam.git)\033[0m:"

read name

su git << EOF

cd /home/gitrepository/;

mkdir $name;

cd $name;

git init --bare;

exit;

EOF




本文转自 justin_peng 51CTO博客,原文链接:http://blog.51cto.com/ityunwei2017/1606146,如需转载请自行联系原作者
相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
相关文章
|
2天前
|
前端开发 Shell 应用服务中间件
shell实战脚本1-前端打包脚本
```markdown 这是一个Bash脚本,用于自动化前端应用的更新流程:切换到`performance-appraisal-web`目录,检出`release`分支,拉取最新代码,使用自定义镜像源安装npm包,构建无登录模式,清理Nginx静态目录,复制构建结果,打包dist目录为`dist.zip`,清除旧zip,并将zip文件复制到临时目录。最后,列出临时目录内容并记录日志到`faban.log`。 ```
29 18
|
10天前
|
JavaScript 前端开发 Shell
Shell 脚本编程保姆级教程(上)
Shell 脚本编程保姆级教程(上)
|
5天前
|
Shell Linux C语言
|
7天前
|
Shell 网络安全
shell脚本 配饰ssh
【7月更文挑战第15天】
10 4
|
11天前
|
网络协议 Shell Linux
Shell脚本配置Centos静态ip地址
这是一个用于在CentOS上设置静态IP的Shell脚本摘要: - 脚本交互式获取用户输入的IP地址、子网掩码、网关和DNS。 - 使用`sed`命令动态更新`/etc/sysconfig/network-scripts/ifcfg-ENS33`配置文件。 - 修改`BOOTPROTO`为`static`,并设置IP、NETMASK、GATEWAY和DNS1字段。 - 用`systemctl restart network`重启网络服务。 - 提示用户新配置的静态IP信息。
|
17天前
|
Shell Linux
Linux Shell 脚本入门教程:开启你的自动化之旅
Shell是一种计算机程序,它充当了用户与操作系统之间的接口。在Linux系统中,Shell允许用户通过命令行界面(CLI)来控制计算机。Shell脚本则是一种使用Shell语言编写的脚本,它可以自动执行一系列的命令,帮助用户实现任务自动化,提高工作效率。
|
23天前
|
Shell
蓝易云 - 简单shell脚本的编写教程
以上就是编写一个基本Shell脚本的步骤。当然,Shell脚本可以做的远不止这些,你可以使用变量,控制结构(如if语句和循环),以及各种Shell命令和功能来编写更复杂的脚本。
22 1
|
10天前
|
Shell
Shell 脚本编程保姆级教程(下)
Shell 脚本编程保姆级教程(下)
|
1月前
|
监控 Shell Linux
Linux的Shell脚本详解
Linux的Shell脚本详解
|
1月前
|
Shell
shell脚本
shell脚本
21 2