shell脚本的语法和使用

最近开始玩linux了,经常要搭建一些环境,比如lnmp,pptp vpn等,我发的教程大多是别人写好的,然后修修补补,到底还是要自己懂才行。今天就介绍下如何正确的写shell脚本

shell基础知识

1、指定解释器

1
#!/bin/bash       //bash脚本第一句都是这个,他会让系统指定以bash来解释这个脚本

2、注释

1
#                 //shell脚本注释符号

3、添加脚本可执行权限

当编辑好脚本时,如果要执行该脚本,还必须使其可执行。通过以下命令添加可执行权限:

1
chmod +x filename

4、执行脚本

然后,您可以通过输入以下命令来执行您的脚本:

1
./filename

shell变量及使用

Shell中的变量是都是弱变量,一般以数字与字符串为主。

1、变量定义

1)定义数值变量

1
a=123                       //赋值数字

shell中默认把变量值当作字符串,如果需要进行数学运算,需要用let等命令。

2)定义单引号字符串变量

1
str='this is a string'

单引号字符串的限制:
单引号里的任何字符都会原样输出,单引号字符串中的变量是无效的;
单引号字串中不能出现单引号(对单引号使用转义符后也不行)。

3)定义双引号字符串变量

1
2
your_name='xiaoming'
str="Hello, I know your are /"$your_name/"! /n"

双引号的优点:
双引号里可以有变量
双引号里可以出现转义字符

4)定义命令语句变量

1
HOME_LIST=$(ls /home)        //把命令的执行结果赋值给变量

利用$()将括号中的执行结果赋值给变量。$( )将多行合为一行,实际是合为一个字符串数组。

5)定义数组变量

1
2
3
4
for ((i=0; i<100; i++))
do
  array[$i]=$i
done

2、变量引用

如下:

1
2
3
num=2
echo "this is the $numnd"    //输出”this is the “ 
echo "this is the ${num}nd"  //输出”this is the 2nd”

${var}这种写法便于区分连续的变量,上面就是很好的示例,与$var在一般情况下区别不大。

3、系统变量

1
2
3
4
5
6
7
$0   这个程序的执行名字
$n  这个程序的第n个参数值,n=1...9
$*  这个程序的所有参数
$#   这个程序的参数个数
$$   这个程序的PID
$!   执行上一个背景指令的PID
$?   上一个指令的返回值,成功执行返回0,不成功返回非0

变量运算及运算符

1、变量数学运算

在shell中一般使用let进行数学运算,如

1
2
3
var=1
let "var+=1"
echo $var       #输出2

需要注意的是
1)let几乎支持所有的运算符,在网上看到一篇文章说“let不支持++、–和逗号、(、)”,但经我测试自加、自减、以及括号的优先级都得到了很好的支持
2)方幂运算应使用“**”
3)参数在表达式中直接访问,不必加$
4)一般情况下算数表达式可以不加双引号,但是若表达式中有bash中的关键字则需加上
5)let后的表达式只能进行整数运算

2、文件比较运算符

1
2
3
4
5
6
7
8
9
10
  
运算符                            描述                                     示例
-s filename                 如果 filename 存在且大小非零,则为真   [ -s /var/log/syslog ]
-e filename                 如果 filename 存在,则为真             [ -e /var/log/syslog ]
-d filename                 如果 filename 为目录,则为真           [ -d /tmp/mydir ]
-f filename                 如果 filename 为常规文件,则为真       [ -f /usr/bin/grep ]
-L filename                 如果 filename 为符号链接,则为真       [ -L /usr/bin/grep ]
-r filename                 如果 filename 可读,则为真             [ -r /var/log/syslog ]
-w filename                 如果 filename 可写,则为真             [ -w /var/mytmp.txt ]
-x filename                 如果 filename 可执行,则为真           [ -L /usr/bin/grep ]

3、字符串比较运算符

1
2
3
4
5
运算符                            描述                                  示例
-z string                   如果 string 长度为零,则为真          [ -z "$myvar" ]
-n string                   如果 string 长度非零,则为真          [ -n "$myvar" ]
string1 = string2           如果 string1 与 string2 相同,则为真  [ "$myvar" = "one two three" ]
string1 != string2          如果 string1 与 string2 不同,则为真  [ "$myvar" != "one two three" ]

4、算术比较运算符

1
2
3
4
5
6
7
运算符                         描述                                      示例
num1 -eq num2                 等于                                 [ 3 -eq $mynum ]
num1 -ne num2                 不等于                               [ 3 -ne $mynum ]
num1 -lt num2                 小于                                 [ 3 -lt $mynum ]
num1 -le num2                 小于或等于                            [ 3 -le $mynum ]
num1 -gt num2                 大于                                 [ 3 -gt $mynum ]
num1 -ge num2                 大于或等于                            [ 3 -ge $mynum ]

5、逻辑判断符

1)[ ]与[[ ]]
两者都可以用来进行条件判断,[ ]及[[ ]]中都可以使用上面介绍的比较运算符。
但是[ ]中的与或只能使用-a及-o,而[[ ]]中的与或可以使用&&及||,并且[[ ]]支持更高级的正则匹配。

2)&&连接两条命令
在使用&&连接两条命令时,它的含义是只有前面一条命令成功执行了,后面的命令才会执行。

1
rm $TEMPDIR/* && echo "Files successfully removed"

只有rm命令成功执行以后,才会执行echo命令,可以用这种方式简化IF语句。

3)||连接两条命令
在使用||连接两条命令时,它的含义为只有第一条命令执行失败才执行第二条命令,例如:

1
rm $TEMPDIR/* || echo "File were not removed"

if、for、while、case流程控制语句

1、if语句

1
2
3
4
5
6
if [ "22" -lt "33" ]   
then       
   echo "22 less than 33"
else                                //else判断
   echo "no"
fi                                  //if语句的结束

2、for语句

1
2
3
4
5
6
7
8
9
for filename in $(ls)
do
  cat $filename
done
 
for((i=0; i<10; i++))
do
  echo $i
done

对于字符串数组,for in格式对字符串数组按空格分割,并循环显示出来。

3、while语句

1
2
3
4
5
6
7
8
x=1
sum=0
while [ $x -le 10 ]   //注意[ ] 两边的空格
do
  let sum=sum+$x    //shell中算术计算使用let
  let x=x+1
done
echo $sum

4、case语句

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
echo "enter a number"
read ans                              //读取一个变量read
case $ans in
1)
  echo   "you numer is $ans"
    ;;                                //注意符号是两个 ;
2)
  echo "you number is 2"
  ;;
[3-9])
  echo "you number is $ans"
  ;;
*)                                   //*通配符
  echo "others"
esac

shell中的常用命令

1、Unix命令

虽然在shell脚本中可以使用任意的unix命令,但是还是由一些相对更常用的命令。这些命令通常是用来进行文件和文字操作的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
echo "some text":                  将文字内容打印在屏幕上
ls:                                文件列表
wc –l filewc -w filewc -c file:    计算文件行数计算文件中的单词数计算文件中的字符数
cp sourcefile destfile:            文件拷贝
mv oldname newname :               重命名文件或移动文件
rm file:                           删除文件
grep 'pattern' file:               在文件内搜索字符串比如:grep 'searchstring' file.txt
cut -b colnum file:                指定欲显示的文件内容范围,并将它们输出到标准输出设备比如:输出每行第5个到第9个字符
cat file.txt:                      输出文件内容到标准输出设备(屏幕)上
file somefile:                     得到文件类型
read var:                          提示用户输入,并将输入赋值给变量
sort file.txt:                     对file.txt文件中的行进行排序
uniq:                              删除文本文件中出现的行列比如: sort file.txt | uniq
expr:                              进行数学运算Example: add 2 and 3expr 2 "+" 3
find:                              搜索文件比如:根据文件名搜索find . -name filename -print
tee:                               将数据输出到标准输出设备(屏幕) 和文件比如:somecommand | tee outfile
basename file:                     返回不包含路径的文件名比如: basename /bin/tux将返回 tux
dirname file:                      返回文件所在路径比如:dirname /bin/tux将返回 /bin
head file:                         打印文本文件开头几行
tail file :                        打印文本文件末尾几行

2、文本替换工具sed

sed是一个基本的查找替换程序。可以从标准输入(比如命令管道)读入文本,并将结果输出到标准输出(屏幕)。该命令采用正则表达式(见参考)进行搜索。不要和shell中的通配符相混淆。

比如:将linuxfocus 替换为 LinuxFocus :

1
cat text.file | sed 's/linuxfocus/LinuxFocus/' > newtext.file

3、文本分析工具awk

awk: awk 用来从文本文件中提取字段。缺省地,字段分割符是空格,可以使用-F指定其他分割符。
NF 表示字段的数量,$0表示一整行,NR表示记录的数量

1
2
3
awk [-F  field-separator]  'commands'  input-file(s)
其中,commands 是真正awk命令,[-F域分隔符]是可选的。 input-file(s) 是待处理的文件。
在awk中,文件的每一行中,由域分隔符分开的每一项称为一个域。通常,在不指名-F域分隔符的情况下,默认的域分隔符是空格。
1
2
3
4
5
#cat /etc/passwd |awk  -F ':'  '{print $1}'  
root
daemon
bin
sys

这种是awk+action的示例,每行都会执行action{print $1},-F指定域分隔符为’:’。

4、管道, 重定向和 backtick

这些不是系统命令,但是他们真的很重要。

1)管道 (|) 将一个命令的输出作为另外一个命令的输入。

1
grep "hello" file.txt | wc -l

在file.txt中搜索包含有”hello”的行并计算其行数。
在这里grep命令的输出作为wc命令的输入。当然您可以使用多个命令。

2)重定向:将命令的结果输出到文件,而不是标准输出(屏幕)。

1
2
> 写入文件并覆盖旧文件
>> 加到文件的尾部,保留旧文件内容。

3)反短斜线
使用反短斜线可以将一个命令的输出作为另外一个命令的一个命令行参数。

1
find . -mtime -1 -type f -print

用来查找过去24小时(-mtime –2则表示过去48小时)内修改过的文件。如果您想将所有查找到的文件打一个包,则可以使用以下脚本:

1
2
3
#!/bin/sh
# The ticks are backticks (`) not normal quotes ('):
tar -zcvf lastmod.tar.gz `find . -mtime -1 -type f -print`

shell脚本实例

下面是一个更为有用的脚本showrpm,其功能是打印一些RPM包的统计信息:

1
2
3
4
5
6
7
8
9
10
11
12
#!/bin/sh
# list a content summary of a number of RPM packages
# USAGE: showrpm rpmfile1 rpmfile2 ...
# EXAMPLE: showrpm /cdrom/RedHat/RPMS/*.rpm
for rpmpackage in $*; do
 if [ -r "$rpmpackage" ];then
  echo "=============== $rpmpackage =============="
  rpm -qi -p $rpmpackage
 else
  echo "ERROR: cannot read file $rpmpackage"
 fi
done

shell脚本调试

最简单的调试命令当然是使用echo命令。您可以使用echo在任何怀疑出错的地方打印任何变量值。这也是绝大多数的shell程序员要花费80%的时间来调试程序的原因。Shell程序的好处在于不需要重新编译,插入一个echo命令也不需要多少时间。

shell也有一个真实的调试模式。如果在脚本”strangescript” 中有错误,您可以这样来进行调试:

1
sh -x strangescript

这将执行该脚本并显示所有变量的值。

shell还有一个不需要执行脚本只是检查语法的模式。可以这样使用:

1
sh -n your_script

这将返回所有语法错误。



最近开始玩linux了,经常要搭建一些环境,比如lnmp,pptp vpn等,我发的教程大多是别人写好的,然后修修补补,到底还是要自己懂才行。今天就介绍下如何正确的写shell脚本

shell基础知识

1、指定解释器

1
#!/bin/bash       //bash脚本第一句都是这个,他会让系统指定以bash来解释这个脚本

2、注释

1
#                 //shell脚本注释符号

3、添加脚本可执行权限

当编辑好脚本时,如果要执行该脚本,还必须使其可执行。通过以下命令添加可执行权限:

1
chmod +x filename

4、执行脚本

然后,您可以通过输入以下命令来执行您的脚本:

1
./filename

shell变量及使用

Shell中的变量是都是弱变量,一般以数字与字符串为主。

1、变量定义

1)定义数值变量

1
a=123                       //赋值数字

shell中默认把变量值当作字符串,如果需要进行数学运算,需要用let等命令。

2)定义单引号字符串变量

1
str='this is a string'

单引号字符串的限制:
单引号里的任何字符都会原样输出,单引号字符串中的变量是无效的;
单引号字串中不能出现单引号(对单引号使用转义符后也不行)。

3)定义双引号字符串变量

1
2
your_name='xiaoming'
str="Hello, I know your are /"$your_name/"! /n"

双引号的优点:
双引号里可以有变量
双引号里可以出现转义字符

4)定义命令语句变量

1
HOME_LIST=$(ls /home)        //把命令的执行结果赋值给变量

利用$()将括号中的执行结果赋值给变量。$( )将多行合为一行,实际是合为一个字符串数组。

5)定义数组变量

1
2
3
4
for ((i=0; i<100; i++))
do
  array[$i]=$i
done

2、变量引用

如下:

1
2
3
num=2
echo "this is the $numnd"    //输出”this is the “ 
echo "this is the ${num}nd"  //输出”this is the 2nd”

${var}这种写法便于区分连续的变量,上面就是很好的示例,与$var在一般情况下区别不大。

3、系统变量

1
2
3
4
5
6
7
$0   这个程序的执行名字
$n  这个程序的第n个参数值,n=1...9
$*  这个程序的所有参数
$#   这个程序的参数个数
$$   这个程序的PID
$!   执行上一个背景指令的PID
$?   上一个指令的返回值,成功执行返回0,不成功返回非0

变量运算及运算符

1、变量数学运算

在shell中一般使用let进行数学运算,如

1
2
3
var=1
let "var+=1"
echo $var       #输出2

需要注意的是
1)let几乎支持所有的运算符,在网上看到一篇文章说“let不支持++、–和逗号、(、)”,但经我测试自加、自减、以及括号的优先级都得到了很好的支持
2)方幂运算应使用“**”
3)参数在表达式中直接访问,不必加$
4)一般情况下算数表达式可以不加双引号,但是若表达式中有bash中的关键字则需加上
5)let后的表达式只能进行整数运算

2、文件比较运算符

1
2
3
4
5
6
7
8
9
10
  
运算符                            描述                                     示例
-s filename                 如果 filename 存在且大小非零,则为真   [ -s /var/log/syslog ]
-e filename                 如果 filename 存在,则为真             [ -e /var/log/syslog ]
-d filename                 如果 filename 为目录,则为真           [ -d /tmp/mydir ]
-f filename                 如果 filename 为常规文件,则为真       [ -f /usr/bin/grep ]
-L filename                 如果 filename 为符号链接,则为真       [ -L /usr/bin/grep ]
-r filename                 如果 filename 可读,则为真             [ -r /var/log/syslog ]
-w filename                 如果 filename 可写,则为真             [ -w /var/mytmp.txt ]
-x filename                 如果 filename 可执行,则为真           [ -L /usr/bin/grep ]

3、字符串比较运算符

1
2
3
4
5
运算符                            描述                                  示例
-z string                   如果 string 长度为零,则为真          [ -z "$myvar" ]
-n string                   如果 string 长度非零,则为真          [ -n "$myvar" ]
string1 = string2           如果 string1 与 string2 相同,则为真  [ "$myvar" = "one two three" ]
string1 != string2          如果 string1 与 string2 不同,则为真  [ "$myvar" != "one two three" ]

4、算术比较运算符

1
2
3
4
5
6
7
运算符                         描述                                      示例
num1 -eq num2                 等于                                 [ 3 -eq $mynum ]
num1 -ne num2                 不等于                               [ 3 -ne $mynum ]
num1 -lt num2                 小于                                 [ 3 -lt $mynum ]
num1 -le num2                 小于或等于                            [ 3 -le $mynum ]
num1 -gt num2                 大于                                 [ 3 -gt $mynum ]
num1 -ge num2                 大于或等于                            [ 3 -ge $mynum ]

5、逻辑判断符

1)[ ]与[[ ]]
两者都可以用来进行条件判断,[ ]及[[ ]]中都可以使用上面介绍的比较运算符。
但是[ ]中的与或只能使用-a及-o,而[[ ]]中的与或可以使用&&及||,并且[[ ]]支持更高级的正则匹配。

2)&&连接两条命令
在使用&&连接两条命令时,它的含义是只有前面一条命令成功执行了,后面的命令才会执行。

1
rm $TEMPDIR/* && echo "Files successfully removed"

只有rm命令成功执行以后,才会执行echo命令,可以用这种方式简化IF语句。

3)||连接两条命令
在使用||连接两条命令时,它的含义为只有第一条命令执行失败才执行第二条命令,例如:

1
rm $TEMPDIR/* || echo "File were not removed"

if、for、while、case流程控制语句

1、if语句

1
2
3
4
5
6
if [ "22" -lt "33" ]   
then       
   echo "22 less than 33"
else                                //else判断
   echo "no"
fi                                  //if语句的结束

2、for语句

1
2
3
4
5
6
7
8
9
for filename in $(ls)
do
  cat $filename
done
 
for((i=0; i<10; i++))
do
  echo $i
done

对于字符串数组,for in格式对字符串数组按空格分割,并循环显示出来。

3、while语句

1
2
3
4
5
6
7
8
x=1
sum=0
while [ $x -le 10 ]   //注意[ ] 两边的空格
do
  let sum=sum+$x    //shell中算术计算使用let
  let x=x+1
done
echo $sum

4、case语句

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
echo "enter a number"
read ans                              //读取一个变量read
case $ans in
1)
  echo   "you numer is $ans"
    ;;                                //注意符号是两个 ;
2)
  echo "you number is 2"
  ;;
[3-9])
  echo "you number is $ans"
  ;;
*)                                   //*通配符
  echo "others"
esac

shell中的常用命令

1、Unix命令

虽然在shell脚本中可以使用任意的unix命令,但是还是由一些相对更常用的命令。这些命令通常是用来进行文件和文字操作的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
echo "some text":                  将文字内容打印在屏幕上
ls:                                文件列表
wc –l filewc -w filewc -c file:    计算文件行数计算文件中的单词数计算文件中的字符数
cp sourcefile destfile:            文件拷贝
mv oldname newname :               重命名文件或移动文件
rm file:                           删除文件
grep 'pattern' file:               在文件内搜索字符串比如:grep 'searchstring' file.txt
cut -b colnum file:                指定欲显示的文件内容范围,并将它们输出到标准输出设备比如:输出每行第5个到第9个字符
cat file.txt:                      输出文件内容到标准输出设备(屏幕)上
file somefile:                     得到文件类型
read var:                          提示用户输入,并将输入赋值给变量
sort file.txt:                     对file.txt文件中的行进行排序
uniq:                              删除文本文件中出现的行列比如: sort file.txt | uniq
expr:                              进行数学运算Example: add 2 and 3expr 2 "+" 3
find:                              搜索文件比如:根据文件名搜索find . -name filename -print
tee:                               将数据输出到标准输出设备(屏幕) 和文件比如:somecommand | tee outfile
basename file:                     返回不包含路径的文件名比如: basename /bin/tux将返回 tux
dirname file:                      返回文件所在路径比如:dirname /bin/tux将返回 /bin
head file:                         打印文本文件开头几行
tail file :                        打印文本文件末尾几行

2、文本替换工具sed

sed是一个基本的查找替换程序。可以从标准输入(比如命令管道)读入文本,并将结果输出到标准输出(屏幕)。该命令采用正则表达式(见参考)进行搜索。不要和shell中的通配符相混淆。

比如:将linuxfocus 替换为 LinuxFocus :

1
cat text.file | sed 's/linuxfocus/LinuxFocus/' > newtext.file

3、文本分析工具awk

awk: awk 用来从文本文件中提取字段。缺省地,字段分割符是空格,可以使用-F指定其他分割符。
NF 表示字段的数量,$0表示一整行,NR表示记录的数量

1
2
3
awk [-F  field-separator]  'commands'  input-file(s)
其中,commands 是真正awk命令,[-F域分隔符]是可选的。 input-file(s) 是待处理的文件。
在awk中,文件的每一行中,由域分隔符分开的每一项称为一个域。通常,在不指名-F域分隔符的情况下,默认的域分隔符是空格。
1
2
3
4
5
#cat /etc/passwd |awk  -F ':'  '{print $1}'  
root
daemon
bin
sys

这种是awk+action的示例,每行都会执行action{print $1},-F指定域分隔符为’:’。

4、管道, 重定向和 backtick

这些不是系统命令,但是他们真的很重要。

1)管道 (|) 将一个命令的输出作为另外一个命令的输入。

1
grep "hello" file.txt | wc -l

在file.txt中搜索包含有”hello”的行并计算其行数。
在这里grep命令的输出作为wc命令的输入。当然您可以使用多个命令。

2)重定向:将命令的结果输出到文件,而不是标准输出(屏幕)。

1
2
> 写入文件并覆盖旧文件
>> 加到文件的尾部,保留旧文件内容。

3)反短斜线
使用反短斜线可以将一个命令的输出作为另外一个命令的一个命令行参数。

1
find . -mtime -1 -type f -print

用来查找过去24小时(-mtime –2则表示过去48小时)内修改过的文件。如果您想将所有查找到的文件打一个包,则可以使用以下脚本:

1
2
3
#!/bin/sh
# The ticks are backticks (`) not normal quotes ('):
tar -zcvf lastmod.tar.gz `find . -mtime -1 -type f -print`

shell脚本实例

下面是一个更为有用的脚本showrpm,其功能是打印一些RPM包的统计信息:

1
2
3
4
5
6
7
8
9
10
11
12
#!/bin/sh
# list a content summary of a number of RPM packages
# USAGE: showrpm rpmfile1 rpmfile2 ...
# EXAMPLE: showrpm /cdrom/RedHat/RPMS/*.rpm
for rpmpackage in $*; do
 if [ -r "$rpmpackage" ];then
  echo "=============== $rpmpackage =============="
  rpm -qi -p $rpmpackage
 else
  echo "ERROR: cannot read file $rpmpackage"
 fi
done

shell脚本调试

最简单的调试命令当然是使用echo命令。您可以使用echo在任何怀疑出错的地方打印任何变量值。这也是绝大多数的shell程序员要花费80%的时间来调试程序的原因。Shell程序的好处在于不需要重新编译,插入一个echo命令也不需要多少时间。

shell也有一个真实的调试模式。如果在脚本”strangescript” 中有错误,您可以这样来进行调试:

1
sh -x strangescript

这将执行该脚本并显示所有变量的值。

shell还有一个不需要执行脚本只是检查语法的模式。可以这样使用:

1
sh -n your_script

这将返回所有语法错误。



发表评论