初学者编写bash脚本教程html
bash是命令语言解释器。普遍用于各类gun/unix系统上的默认命令解释器。全程叫作“Bourne-Again SHell”mysql
shell是一个宏处理器,容许执行交互式或非交互式的命令。linux
脚本容许自动执行,不然会一个接一个命令交互执行。程序员
shell容许你经过命令与计算机交互,从而检索或存储数据、处理信息和其它各类简单甚至很是复杂的任务。sql
因此:你天天都会执行date cal pwd lsdocker
script就是把上边的4个命令放在一个文本里,而后一块儿执行。shell
bash是许多GNU/Linux系统上的默认解释器编程
查看默认解释器执行命令:echo $SHELL
ubuntu
[root@localhost ~]# echo $SHELL /bin/bash
#!
shebang,定义脚本的shell解释器,写在脚本的第一行如:数组
#!/bin/bash
经过which bash
查找bash可执行二进制文件的完整路径。
要执行脚本,就要给脚本加执行的权限:chmod +x filename
经过file 命令查看文件的类型 file filename
[root@localhost ~]# file a.sh a.sh: Bourne-Again shell script, ASCII text executable [root@localhost ~]# file epel-7.repo epel-7.repo: ASCII text
一、指定解释器执行脚本
[root@localhost ~]# /bin/bash date.sh Sat Jul 14 09:01:39 UTC 2018
二、脚本文件头定义shebang,文件加执行权限
[root@localhost ~]# cat date.sh #!/bin/bash date [root@localhost ~]# chmod +x date.sh [root@localhost ~]# ./date.sh Sat Jul 14 09:02:41 UTC 2018
在linux中绝对路径就是从根/
开始,如:/usr/local/httpd
就是路径开头没有根/
,如:cd abc/test/
cd
不加任何参数,切换到当前用户home目录。
cd ~
都是切换到当前用户home目录。
cd 加路径
切换到目标路径,如:cd /usr/local
[root@localhost ~]# echo '#!/bin/bash' > hello.sh [root@localhost ~]# echo 'echo "hello world"' >> hello.sh [root@localhost ~]# cat hello.sh #!/bin/bash echo "hello world" [root@localhost ~]# chmod +x hello.sh [root@localhost ~]# ./hello.sh hello world
vi backup.sh #建立脚本 # !/bin/bash tar czf /tmp/root.tar /root/abc chmod +x backup.sh #加执行权限 ./backup.sh #执行脚本 [root@localhost]# ls /tmp/root.tar #验证执行结果 /tmp/root.tar
双引号"" 容许用过$符号引用其余变量值
单引号'' 禁止引用其余变量值,$视为普通字符
反撇号`` 将命令执行的记过输出给变量;如:
sed -i s/"B"/"b"/g `grep -rl "B" --exclude="*.sql" ceshi/*` 将grep获得的文件传输给sed使用
设置变量的做用范围
格式:
export 变量名
export 变量名=变量值
清除变量名:
unset 变量名
自定义变量 [root@ceshi ~]# export a #自定义变量a [root@ceshi ~]# export b=222 #自定义变量b [root@ceshi ~]# a=111 #给变量a赋值 [root@ceshi ~]# echo $a #打印变量a的值 111 [root@ceshi ~]# echo $b #打印变量b的值 222 清除变量: [root@ceshi ~]# unset a #清除变量a [root@ceshi ~]# unset b #清除变量b [root@ceshi ~]# echo $a #输出为空 [root@ceshi ~]# echo $b
全局配置文件:/etc/profile
用户配置文件:~/.bash_profile
set 命令能够查看全部的shell变量,其中包括环境变量
表示为:$n (n为1-9之间的数字)
#./test.sh one two three four five six
$0 表示脚本文件名自己
$1 表示one
$2 表示two
依次类推
$n 这个程式的第n个参数值,n=1..9
$@
跟$*相似,可是能够看成数组用变量是编程的本质。变量容许程序员在整个脚本中存储、修改和重用数据。
#!/bin/bash greeting="Welcome" user=$(whoami) day=$(date +%A) echo "$greeting back $user! Today is $day." echo "Your bash shell version is:$BASH_VERSION."
解释脚本:
第一部分:
shebang 指定shell解释器
greeting:定义一个变量,并赋值一个字符串值。
user:定义一个变量,赋值是经过一个命令替换技术来完成的。经过运行whoami获取当前的用户名,而后赋值给user。
day:定义一个变量,赋值同上;+%A表明只显示周几。
第二部分:
使用echo向终端打印信息,$greeting用来调用变量对应的值Welcome;$user一样用来调用变量对应的用户名;date也同样。
注意:不要使用大写命名私有变量,由于大写变量名都是内置变量。若是覆盖内置变量,可能致使功能失调。
[root@localhost ~]# a=5 #定义变量 [root@localhost ~]# b=6 [root@localhost ~]# echo $a #调用变量 5 [root@localhost ~]# echo $b 6 [root@localhost ~]# echo $[$a+$b] #变量算数 11
利用变量更新备份脚本,生成更有意义的文件名:
vi backup.sh
#!/bin/bash # this bash script is backup a general user's home directory to /tmp/ user=$(whoami) input=/home/$user output=/tmp/${user}_home$(date +%Y-%m-%d_%H%M%S).tar.gz tar czf $output $input echo "Backup of $input completed!! Details about the output backup file:" ls -l $output
脚本第五行:${parameter}叫参数展开,将花括号中的内容挨个和外面的内容结合。
[abc@localhost ~]$ ./backup.sh tar: Removing leading `/' from member names Backup of /home/abc completed!! Details about the output backup file: -rw-rw-r-- 1 abc abc 741 Jul 14 13:59 /tmp/abc_home2018-07-14_135933.tar.gz
一般在GNU/Linux命令行上执行的命令要么产生输出,要么要求输入,要么抛出错误消息。这是shell脚本的基本概念,也是使用GNU/Linux命令行的基本概念。
每次执行命令时,可能会发生三种可能的结果。第一个场景是命令将产生一个预期的输出,其次,该命令将生成一个错误,最后,您的命令可能根本不会产生任何输出。
[abc@localhost ~]$ ls backup.sh backup.sh
重定向标识符:“>”
重定向会将 标准输出 重定向到指定目标。
[abc@localhost ~]$ touch foobar [abc@localhost ~]$ ls foobar barfoo ls: cannot access barfoo: No such file or directory foobar [abc@localhost ~]$ ls foobar barfoo > stdout.txt ls: cannot access barfoo: No such file or directory [abc@localhost ~]$ cat stdout.txt foobar
重定向将 标准错误 重定向到指定目标。
[abc@localhost ~]$ ls foobar barfoo 2> stderr.txt foobar [abc@localhost ~]$ cat stderr.txt ls: cannot access barfoo: No such file or directory
重定向将 标准输出和标准错误 一块重定向到指定目标。
[abc@localhost ~]$ ls foobar barfoo &> stdoutandstderr.txt [abc@localhost ~]$ cat stdoutandstderr.txt ls: cannot access barfoo: No such file or directory foobar
追加剧定向输出,不覆盖文件,直接追加到文件结尾。
[abc@localhost ~]$ ls test.sh ls: cannot access test.sh: No such file or directory
重定向将 标准错误 重定向到指定目标。
[abc@localhost ~]$ ls foobar barfoo 2> stderr.txt foobar [abc@localhost ~]$ cat stderr.txt ls: cannot access barfoo: No such file or directory
一般终端输入来自键盘,因此输入的任何内容都称为stdin(标准输入)。
接受来自文件的命令输入。使用“<”符号接受文件输入。
咱们先用cat 命令,经过标准输入而后,重定向到file.txt:
[abc@localhost ~]$ cat > file.txt hello world. welcome backup. [abc@localhost ~]$ cat file.txt hello world. welcome backup.
咱们再用cat命令,经过< 符号输入file.txt的内容:
[abc@localhost ~]$ cat < file.txt hello world. welcome backup.
[root@localhost log]# cat >aaa.txt<<EOF > hello world > EOF [root@localhost log]# cat aaa.txt hello world
[root@localhost log]# cat >>aaa.txt<<EOF > 123456789 > HELLO WORLD > EOF [root@localhost log]# cat aaa.txt hello world 123456789 HELLO WORLD
从键盘或文件中获取标准输入:read命令
语法:read 变量名
[abc@localhost ~]$ read a abc [abc@localhost ~]$ echo $a abc [abc@localhost ~]$ echo "the word you entered is: $a" the word you entered is: abc [abc@localhost ~]$ read a1 a2 #同时输入两个字段 aaa bbb [abc@localhost ~]$ echo $a1 aaa [abc@localhost ~]$ echo $a2 bbb
详细见:https://blog.51cto.com/506554897/2114407
以前的backup脚本执行时有个错误:
tar: Removing leading "/" from member names
消息告诉咱们,绝对路径已被删除,从而提取了压缩文件,而不是覆盖任何现有文件。并无形成任何影响。
所以我能够利用重定向消除这种没必要要的信息。经过重定向stderr到/dev/null,/dev/null做为数据接收器,它丢弃任何重定向到它的数据,能够用man null查看详细。
咱们将脚本修改以下:
[abc@localhost ~]$ vi backup.sh #!/bin/bash # this bash script is backup a general user's home directory to /tmp/ user=$(whoami) input=/home/$user output=/tmp/${user}_home$(date +%Y-%m-%d_%H%M%S).tar.gz tar czf $output $input 2> /dev/null echo "Backup of $input completed!! Details about the output backup file:" ls -l $output
[abc@localhost ~]$ ./backup.sh Backup of /home/abc completed!! Details about the output backup file: -rw-rw-r-- 1 abc abc 893 Jul 14 14:59 /tmp/abc_home2018-07-14_145919.tar.gz
执行后没有了报错信息。
##名词
函数容许程序员组织和重用代码,从而提升了整个脚本的效率、执行速度和可读性。
当注意到脚本包含两行相同的代码时,能够考虑使用一个函数。
函数是将不一样命令的组号组合成当个命令的方法。若是所需的输出或计算由多个命令组成,而且在整个脚本执行过程当中须要屡次,这可能很是有用。函数是使用函数关键字定义,后面是用花括号括起来的函数体。如:user_details {}
定义函数方法:
function 函数名{ 函数体(命令) } 函数名() { 函数体(命令) } 函数名() { 函数体(命令) }
[abc@localhost ~]$ vi function.sh #!/bin/bash function user_details { echo "User Name: $(whoami)" echo "User Home: $HOME" } user_details
详细描述:
function是函数关键字,user_details是函数名。
花括号里边的是函数体,包括两个echo命令,在两个echo以前有缩进,缩进为1个tab,4个空格。缩进能够择本身的缩进方式。
最后的user_details 是在调用函数。
注意:函数定义必须在函数调用以前进行,不然脚本将返回stderr:command not found错误。
#!/bin/bash # this bash script is backup a general user's home directory to /tmp/ user=$(whoami) input=/home/$user output=/tmp/${user}_home$(date +%Y-%m-%d_%H%M%S).tar.gz function total_files { find $1 -type f | wc -l } function total_directory { find $1 -type d | wc -l } tar czf $output $input 2> /dev/null echo -n "files to be included:" total_files $input echo -n "directory to be included:" total_directory $input echo "Backup of $input completed!!" echo "Details about the output backup file:" ls -l $output
代码片断:
定义tottal_files函数,功能利用function find() { [native code] }和wc命令,来统计 提供给函数调用的目录的文件数量。
定义total_diretory函数,功能利用function find() { [native code] }和wc命令,来统计 提供给函数调用的目录的目录数量。
执行脚本显示以下:
描述 | 数值比较 | 字符串比较 |
---|---|---|
少于 less than | -lt | < |
大于 greater than | -gt | > |
等于 equal | -eq | = |
不等于 not equal | -ne | != |
小于或等于 | -le | N/A |
大于或等于 | -ge | N/A |
字符串不是空 | N/A | -n |
字符串是空 | N/A | -z |
N/A:表明不可用/不适用
-n $1
:字符串$1不是空的
-z $1
:字符串$1 为空
当两个数值比较后,能够用echo $?
来检查返回值,来查看结果是True 返回0;结果是False返回为1。
[abc@localhost ~]$ a=1 [abc@localhost ~]$ b=2 [abc@localhost ~]$ [ $a -lt $b ] [abc@localhost ~]$ echo $? #正确返回0 0 [abc@localhost ~]$ [ $a -gt $b ] [abc@localhost ~]$ echo $? #不正确返回1 1 [abc@localhost ~]$ [ $a -eq $b ] [abc@localhost ~]$ echo $? 1
[abc@localhost ~]$ [ "apple" = "banana" ] [abc@localhost ~]$ echo $? 1 [abc@localhost ~]$ str1="apple" [abc@localhost ~]$ str3="apple" [abc@localhost ~]$ str2="banana" [abc@localhost ~]$ [ $str1 = $str2 ] [abc@localhost ~]$ echo $? 1 [abc@localhost ~]$ [ $str1 = $str3 ] [abc@localhost ~]$ echo $? 0
条件容许程序员根据某些条件或事件在shell脚本中实现决策。
咱们所指的条件语句是:if、then、else。
举个例子:if num1大于num2,then 就打印num1大于2,else 若是不大于,就打印其余信息。
[abc@localhost ~]$ vi num.sh #!/bin/bash num1=100 num2=200 if [ $num1 -gt $num2 ];then echo "$num1 greater than num2" else echo "$num1 less than or equal $num2" fi
注意:fi
是关闭if条件块的关键词;注意中括号两边的间距,没有空间,就不能执行。
[abc@localhost ~]$ chmod +x num.sh [abc@localhost ~]$ ./num.sh 100 less than or equal 200
查看当前目录下是否有mysql目录
[abc@localhost ~]$ vi mysql.sh #!/bin/bash directory="./mysql" if [ -d $directory ]; then echo "$directory is exist!!!" else echo "$directory is not exist!" fi
执行结果:
[abc@localhost ~]$ sh mysql.sh ./mysql is not exist! [abc@localhost ~]$ mkdir mysql [abc@localhost ~]$ sh mysql.sh ./mysql is exist!!!
[abc@localhost ~]$ cat nested.sh #!/bin/bash # 这是一个嵌套if else脚本,while循环让用户循环选择字段。 # 定义一个标志 choise=0 # 定义一个函数,打印用户可选择的序号 字段 function p1() { echo "1. BeiJing" echo "2. ShangHai" echo "3. ChangSha" echo "请选择你喜欢城市的序号[1-3]" } # 当choise为0的时候就循环 while [ $choise -eq 0 ]; do p1 read choise if [ $choise -eq 1 ]; then echo "你选择的是:Beijing" else if [ $choise -eq 2 ]; then echo "您的选择是: ShangHai" else if [ $choise -eq 3 ]; then echo "您的选择是:ChangSha" else echo “请输入合法的字段” choise=0 fi fi fi done
执行结果:
[abc@localhost ~]$ sh nested.sh 1. BeiJing 2. ShangHai 3. ChangSha 请选择你喜欢城市的序号[1-3] 1 你选择的是:Beijing [abc@localhost ~]$ sh nested.sh 1. BeiJing 2. ShangHai 3. ChangSha 请选择你喜欢城市的序号[1-3] 4 “请输入合法的字段” 1. BeiJing 2. ShangHai 3. ChangSha 请选择你喜欢城市的序号[1-3] 3 您的选择是:ChangSha [abc@localhost ~]$ sh nested.sh 1. BeiJing 2. ShangHai 3. ChangSha 请选择你喜欢城市的序号[1-3] 2 您的选择是: ShangHai
[abc@localhost ~]$ cat elif.sh #!/bin/bash # 经过用户输入两个数值,分别赋值给num一、num2,而后来比较大小 read num1 num2 if [ $num1 -eq $num2 ]; then echo "Both values are equal!" elif [ $num1 -gt $num2 ]; then echo "num1 values is greater then num2" else echo "num1 values is less then num2" fi
执行结果:
[abc@localhost ~]$ sh elif.sh 11 22 num1 values is less then num2 [abc@localhost ~]$ sh elif.sh 11 11 Both values are equal! [abc@localhost ~]$ sh elif.sh 6 2 num1 values is greater then num2
执行完tar备份以后,要比较源数据的文件数量和备份后tar包的文件的数量。
#!/bin/bash # this bash script is backup a general user's home directory to /tmp/ user=$(whoami) input=/home/$user output=/tmp/${user}_home$(date +%Y-%m-%d_%H%M%S).tar.gz function total_files { find $1 -type f | wc -l } function total_directory { find $1 -type d | wc -l } function total_archived_directory { tar -tzf $1 | grep /$ |wc -l } function total_archived_files { tar -tzf $1 | grep -v /$ | wc -l } tar czf $output $input 2> /dev/null src_files=$( total_files $input ) src_directory=$( total_directory $input ) arch_files=$( total_archived_files $output ) arch_directory=$( total_archived_directory $output ) echo "Files to be include: $src_files" echo "Directories to be include: $src_directory" echo "Files archived: $arch_files" echo "Directories archived: $arch_directory" if [ $src_files -eq $arch_files ];then echo "Backup of $input completed!!!" echo "Details about the output backup file:" ls -l $output else echo "Backup of $input Failed!!!" fi
指令 | 描述 |
---|---|
-b filename | 块特殊文件 |
-c filename | 特殊字符文件 |
-d directory name | 检查目录是否存在 |
-e filename | 检查文件是否存在 |
-f filename | 检查是否存在常规文件,而不是目录 |
-G filename | 检查文件是否存在,是否有效组ID |
-g filename | 若是文件存在并设置了组id,则为true。 |
-k filename | Sticky bit |
-L filename | 符号连接 Symbolic link |
-O filename | 若是文件存在并有效用户id,则为true。 |
-r filename | 检查文件是否可读 |
-S filename | 检查文件是否为socket |
-s filename | 检查文件大小是否为非零 |
-u filename | Check if file set-ser-id bit is set |
-w filename | 检查文件是否可写 |
-x filename | 检查文件是否可执行 |
[abc@localhost ~]$ vi file-test.sh #!/bin/bash # this is a test file exist ? yes/no file="./testtest.txt" if [ -e $file ]; then echo "File exists!" else echo "File does not exists" fi
脚本执行结果:
[abc@localhost ~]$ /bin/bash file-test.sh File does not exists [abc@localhost ~]$ touch testtest.txt [abc@localhost ~]$ /bin/bash file-test.sh File exists!
到目前为止,咱们的备份脚本看起来很棒。咱们能够计算结果压缩备份文件中包含的文件和目录的数量。此外,咱们的脚本还促进了一个健全检查,以确认全部文件已正确备份。缺点是咱们老是被迫备份当前用户的目录。若是脚本足够灵活,容许系统管理员仅经过将脚本指向主目录就能够备份所选系统用户的主目录,那就太好了。
当使用bash位置参数时,这是一个至关容易的任务。位置参数经过命令行参数分配,并可在脚本中访问,如$1, $2...$N
变量。在脚本执行期间,在程序名称以后提供的任何附加项都被视为参数,并在脚本执行期间可用。
$、$# 、$*
考虑如下示例:
[abc@localhost ~]$which bash > position.sh [abc@localhost ~]$ vi position.sh #!/usr/bin/bash echo $1 $2 $4 echo $# echo $* [abc@localhost ~]$ /bin/bash position.sh 1 2 3 4 1 2 4 4 1 2 3 4 [abc@localhost ~]$ /bin/bash position.sh one two three four one two four 4 one two three four
脚本详细:
#!/usr/bin/bash echo $1 $2 $4 echo $# echo $*
第二行表明打印:第一个、第二个、第四个 位置参数
第三行表明打印:$#提供参数的总和
第四行表明打印:$*提供的全部参数
使用位置参数知识,让咱们如今改进咱们的backup.sh
脚原本接受命令行中的参数。实现让用户决定哪一个目录将被备份。若是用户在脚本执行期间没有提交任何参数,默认状况下,该脚本将备份当前用户的主目录。新脚本以下:
#!/bin/bash # this bash script is used to backup a user's home directory if [ -z $1 ];then user=$(whoami) else if [ ! -d "/home/$1" ]; then echo "Requested $1 user home directory doesn't exist!" exit 1 fi user=$1 fi # define default backup directory and tar.gz files input=/home/$user output=/tmp/${user}_home_$(date +%Y-%m-%d_%H%M%S).tar.gz # count the number of source files function total_files { find $1 -type f | wc -l } # count the number of source directory function total_directory { find $1 -type f | wc -l } # count the number of archived files total_archived_files() { tar -tzf $1 | grep /$ | wc -l } # count the number of archived directory total_archived_directory() { tar -tzf $1 | grep -v /$ | wc -l } tar -czf $output $input 2> /dev/null src_files_num=$( total_files $input ) src_directory_num=$( total_directory $input) arch_files_num=$( total_archived_files $output) arch_directory_num=$( total_archived_directory $output) echo "files to be included: $src_files_num" echo "directory to be included: $src_directory_num" echo "files to be included of archived: $arch_files_num" echo "directory to be included of archived: $arch_directory_num" if [ $src_files_num -eq $arch_files_num ]; then echo "Backup of $input complete!!" echo "Deatils about the output backup file:" ls -l $output else echo "Backup of $input Failed!!!" fi
脚本执行结果:
#不加参数执行,默认备份当前用户主目录 [abc@localhost ~]$ ./backup.sh files to be included: 21 directory to be included: 21 files to be included of archived: 1 directory to be included of archived: 21 Backup of /home/abc Failed!!! #加参数指定用户abc,备份abc用户的主目录 [abc@localhost ~]$ ./backup.sh abc files to be included: 21 directory to be included: 21 files to be included of archived: 1 directory to be included of archived: 21 Backup of /home/abc Failed!!! #加参数指定用户aaa,aaa用户不存在,报错了。 [abc@localhost ~]$ ./backup.sh aaa Requested aaa user home directory doesn't exist!
到目前为止,本分脚本功能和可用性已经大大提升,经过位置参数咱们能够指定备份 某个用户的主目录,咱们能够轻松的备份任何用户目录。
当咱们须要天天备份多个用户目录时,就会变得重复、乏味、费时。
这个时候咱们能够用循环构造来迭代任何给定数量的任务。
有三种基本循环类型。
for循环用于迭代列表中任意数量的提供项的任何给定代码。让咱们从一个简单的for循环示例开始:
[abc@localhost ~]$ for i in 1 2 3; do echo $i; done 1 2 3
上边的for循环使用了echo 命令打印因此项目。使用分号容许在当行执行for循环。将上面的for循环变成bash脚本,以下:
[abc@localhost ~]$ vi loop.sh #!/bin/bash for i in 1 2 3; do echo $i done ~
for循环由四个shell保留字段组成:for、in、do、done。
所以,上述代码能够理解为:for
循环in
在列表中的每一项,而后将每一项临时赋值给变量i,而后do
用echo $i
打印到stdout,直到列表循环完才结束done
。
计算文本里边每行的字符数量
[abc@localhost ~]$ cat items.txt abc abcd abcdef a [abc@localhost ~]$ for i in $( cat items.txt ); do echo -n $i | wc -c; done 3 4 6 1
while循环在给定的条件下工做。它将继续执行封闭的代码。当条件为true的时候,do
执行代码,当条件变为false就终止done
。
[abc@localhost ~]$ vi while.sh #!/bin/bash counter=0 while [ $counter -lt 3 ]; do let counter+=1 echo $counter done
代码详解:
定义变量:counter
while定义条件:当变量counter小于3的时候,条件为true。在每次循环迭代期间,使用let将counter自加1,而后打印变量counter。
done:当条件变成false 结束循环
脚本执行结果:
[abc@localhost ~]$ sh while.sh 1 2 3
until循环和while循环彻底相反的操做。利用until、do、done,循环直到条件从false变成true就结束。
[abc@localhost ~]$ vi until.sh #!/bin/bash counter=6 until [ $counter -lt 3 ]; do let counter-=1 echo $counter done
代码详解:
定义变量:counter
until定义条件:当变量counter不小于3的时候,条件为false。在每次循环迭代期间,使用let将counter自加1,而后打印变量counter。
done:当条件变成true 结束循环
脚本执行结果:
[abc@localhost ~]$ sh until.sh 5 4 3 2
以前的备份脚本每次执行备份一个用户的目录。咱们能够经过位置参数给脚本提供多个用户,经过for循环去执行备份命令。以下:
#!/bin/bash # this bash script is used to backup one or more user's home directory function backup { if [ ! -d "/home/$1" ]; then echo "resquested $1 user home directory does't exist." exit 1 fi user=$1 input=/home/$user output=/tmp/${user}_home_$(date +%Y-%m-%d_%H%M%S).tar.gz function total_files { find $1 -type f | wc -l } function total_directories { find $1 -type d | wc -l } function total_arch_files { tar -xvf $1 | grep -v /$ | wc -l } function total_arch_directories { tar -xvf $1 | grep /$ | wc -l } tar -czf $output $input 2> /dev/null # count the number of src_files and src_directory src_files=$( total_files $input ) src_directories=$( total_directories $input ) arch_files=$( total_arch_files $output ) arch_directories=$( total_arch_directories $output ) echo "######## $user ########" echo "Src_files to be included: $src_files" echo "Src_directories to be included: $src_directories" echo "Arch_files to be included: $arch_files" echo "Arch_directories to be included: $arch_directories" if [ $src_files -eq $arch_files ]; then echo "--------------------------------------" echo "Backup user:<$user> home completed!!!" echo "Details about the output backup files:" ls -l $output else echo "Backup user:<$user> home Failed!!!" fi } for directory in $*; do backup $directory done;
该脚本最好用root用户去执行,由于普通用户备份别的用户home目录会存在权限问题。但该脚本并不能备份root的home目录。脚本执行结果:
[root@localhost ~]# sh loop_backup.sh aaa bbb ccc ######## aaa ######## Src_files to be included: 4 Src_directories to be included: 1 Arch_files to be included: 4 Arch_directories to be included: 1 -------------------------------------- Backup user:<aaa> home completed!!! Details about the output backup files: -rw-r--r-- 1 root root 519 Jul 17 04:11 /tmp/aaa_home_2018-07-17_041134.tar.gz ######## bbb ######## Src_files to be included: 5 Src_directories to be included: 2 Arch_files to be included: 5 Arch_directories to be included: 2 -------------------------------------- Backup user:<bbb> home completed!!! Details about the output backup files: -rw-r--r-- 1 root root 573 Jul 17 04:11 /tmp/bbb_home_2018-07-17_041134.tar.gz ######## ccc ######## Src_files to be included: 6 Src_directories to be included: 3 Arch_files to be included: 6 Arch_directories to be included: 3 -------------------------------------- Backup user:<ccc> home completed!!! Details about the output backup files: -rw-r--r-- 1 root root 606 Jul 17 04:11 /tmp/ccc_home_2018-07-17_041134.tar.gz
select表达式是bash的一种扩展应用,擅长于交互式场合。
语法:
select var in ... ; do break; done
例子:
[abc@localhost ~]$ vi select.sh #!/bin/bash PS3="请选择你最喜欢城市的序号:" select word in "北京" "上海" "杭州" "长沙"; do echo "您选择的是:$word" break done
执行结果:
[abc@localhost ~]$ sh select.sh 1) 北京 2) 上海 3) 杭州 4) 长沙 请选择你最喜欢城市的序号:1 您选择的是:北京
case语句为多选择语句。能够用case语句来匹配一个值与一个模式,若是匹配成功,执行相匹配的命令。
case语法:
case 值 in 模式1) command1 command2 ... ;; # 两个分号表明结束 模式2) command1 command2 ... ;; esac
如上:case的值后面必须是in,每一个模式必须以 右括号结束,取值能够是变量或常数。匹配某一模式后,就会执行模式下的全部命令 直到 ;; 。
取值一旦匹配了某个模式,就不会再去匹配其余模式。若是没有匹配到,就会用 * 捕获该值,再执行后面的命令。
case语句最后要有一个结束标记:esac 。
例子:
[abc@localhost ~]$ cat case.sh #!/bin/bash echo "请输入你喜欢的城市:" echo "1) 北京" echo "2) 上海" echo "3) 杭州" echo "4) 长沙" read num case $num in 1) echo "你选择的是北京" echo 1 ;; 2) echo "你选择的是上海" echo 2 ;; 3) echo “你选择的是杭州” ;; 4) echo "你选择的是长沙" ;; *) echo "输入错误!请选择[1-4]城市对应的序号!" ;; esac
执行结果:
[abc@localhost ~]$ sh case.sh 请输入你喜欢的城市: 1) 北京 2) 上海 3) 杭州 4) 长沙 1 你选择的是北京 1 [abc@localhost ~]$ sh case.sh 请输入你喜欢的城市: 1) 北京 2) 上海 3) 杭州 4) 长沙 3 “你选择的是杭州” [abc@localhost ~]$ sh case.sh 请输入你喜欢的城市: 1) 北京 2) 上海 3) 杭州 4) 长沙 5 输入错误!请选择[1-4]城市对应的序号!
[root@ceshi ~]# vi let.sh #!/bin/bash num1=2 num2=3 let result=num1+num2 echo $result 运行: [root@ceshi ~]# /bin/bash let.sh 5
自加操做 let num1++
自减操做 let num1--
简写形式:let no+=10let ; let no-=20
等同于:let no=no+10; let no=no-20
[root@ceshi ~]# vi fangkuohao.sh #!/bin/bash num1=2 num2=3 result=$[$num1+num2] echo $result 运行: [root@ceshi ~]# /bin/bash fangkuohao.sh 5
注:使用方法和let类似,在[]中可使用$前缀
[root@ceshi ~]# vi xiaokuohao.sh #!/bin/bash n1=2 n2=3 result=$((n1+n2)) echo $result
[root@ceshi ~]# expr 2 + 3 5 [root@ceshi ~]# num1=5 [root@ceshi ~]# r=$(expr $num1 + 5) [root@ceshi ~]# echo $num1 5 [root@ceshi ~]# echo $r 10
expr的经常使用运算符
它能够执行浮点运算和一些高级函数
[root@ceshi ~]# echo "1.25*3" | bc 3.75
设定小数精度(也就是小数点显示几位)
scale=2 表明小数点显示2位
[root@ceshi ~]# echo "scale=2;7/3" | bc 2.33
十进制转二进制: [root@ceshi ~]# a=192 [root@ceshi ~]# echo "obase=2;$a" |bc 11000000 二进制转十进制: [root@ceshi ~]# b=11000000 [root@ceshi ~]# echo "obase=10;ibase=2;$b"|bc 192
求2的三次方: [root@ceshi ~]# echo "2^3"|bc 8 求100的平方根 [root@ceshi ~]# echo "sqrt(100)"|bc 10
用于指定在接收到信号后将要采起的动做,常见的用途是在脚本程序被中断时完成清理工做、忽略ctrl+c。
当脚本在执行的时候,在终端驶入ctrl+c,trap会接收到消息,并执行相关trap命令。以下:
[abc@localhost ~]$ cat traptrap.sh #!/bin/bash # difine bash trap command trap bashtrap INT # clear screen command clear; # define trap function:bashtrap, bashstrap is executed when CTRL-C is pressed; bashtrap() { echo "bash trap detected "CTRL+C" when script is executed. " } # for loop from 1/10 to 10/10 for a in `seq 1 10`; do echo "$a/10 to exit" sleep 1; done echo "exit bash trap example!"
当脚本在执行的时候,在终端输入ctrl+c,trap到以后会执行bashtrap函数。执行结果以下:
[abc@localhost ~]$ sh traptrap.sh 1/10 to exit 2/10 to exit ^Cbash trap detected CTRL+C when script is executed. 3/10 to exit ^Cbash trap detected CTRL+C when script is executed. 4/10 to exit ^Cbash trap detected CTRL+C when script is executed. 5/10 to exit 6/10 to exit 7/10 to exit ^Cbash trap detected CTRL+C when script is executed. 8/10 to exit 9/10 to exit 10/10 to exit exit bash trap example!
详见:第四章 数组、关联数组和别名使用
https://blog.51cto.com/506554897/2114414
[abc@localhost ~]$ vi array.sh #!/bin/bash # 声明数组array,并赋值3个元素 array=( "Debian linux" "redhat linux" "ubuntu linux") # 得到数组array里边有多少个元素 elements=${#array[@]} # 经过for循环数组里边的元素的索引,并经过数组索引打印每一个元素 for (( i=0;i<$elements;i++)); do echo ${array[${i}]} done
执行结果:
[abc@localhost ~]$ sh array.sh Debian linux redhat linux ubuntu linux
[abc@localhost ~]$ cat read.sh #!/bin/bash # 这是一个读取文件每行内容到数组的脚本。 # 声明一个变量array declare -a array # 用stdin连接文件记录器10 exec 10<&0 # 将stdin替换为做为第一个参数提供的文件。 exec < $1 # 用while循环将文件每行内容做为一个元素写入数组 # count用做数组的下标 let count=0 while read line; do echo "line text is: $line" array[$count]=$line ((count++)) done # 打印数组的元素总个数,@和* 这里是一个意思 都是全部 echo "count the number of array elements: ${#array[@]}" # 打印数组的全部元素 echo "array content is: ${array[*]}" # 从filedescriptor 10恢复stdin; filedescriptor:文件记录器 # 关闭filedescriptor 10 exec 0<&10 10<&-
declare 声明变量和显示变量 详见:第二十八章 声明和显示shell变量:declare命令
执行结果:
[abc@localhost ~]$ cat a.txt welcome to china [abc@localhost ~]$ sh read.sh a.txt line text is: welcome line text is: to line text is: china count the number of array elements: 3 array content is: welcome to china [abc@localhost ~]$ cat b.txt 大母鸡 小母鸡 母鸡 母鸡 老母鸡 [abc@localhost ~]$ sh read.sh b.txt line text is: 大母鸡 小母鸡 line text is: 母鸡 母鸡 line text is: 老母鸡 count the number of array elements: 3 array content is: 大母鸡 小母鸡 母鸡 母鸡 老母鸡
\
反斜杠就是转义特殊符号。
转义元字符就是抑制元字符的特殊含义,所以元字符将被bash逐字逐句的阅读。
语法:\特殊字符
[abc@localhost ~]$ vi escape.sh #!/bin/bash var1="Bash Script" echo "$var1" echo "\$var1"
执行结果:
[abc@localhost ~]$ sh escape.sh Bash Script $var1
bash中的单引号 抑制每一个元字符的特殊含义,元字符将被逐字逐句读取。
即便单引号被反斜杠转义,也不可能在两个单引号中使用另外一单引号。
[abc@localhost ~]$ vi quotes.sh #!/bin/bash var2="Bash Script" echo "$var2" echo '$var2 "$var2"'
执行结果:
[abc@localhost ~]$ sh quotes.sh Bash Script $var2 "$var2"
双引号将抑制除了 `$ \ `` 之外的每一个元字符的特殊含义。
任何其余元字符都会被逐字逐句读取。
在双引号中可使用单引号。若是须要在双引号中使用双引号,可使用"\" 进行转义。
[abc@localhost ~]$ vi double-quote.sh #!/bin/bash var3="Hello world" echo "$var3" echo "这是一个打招呼的词语:$var3" echo "这是一个到招呼的词语: \"$var3\", time is `date +"%Y-%m-%d_%H:%M:%S"` "
执行结果以下:
[abc@localhost ~]$ sh double-quote.sh Hello world 这是一个打招呼的词语:Hello world 这是一个到招呼的词语: "Hello world", time is 2018-07-19_16:35:23
ANSI-C :ANSI C是由美国国家标准协会(ANSI)及国际标准化组织(ISO)推出的关于C语言的标准。
在ANSI-C 类型的引用中,用“\” 转义的字符将按照ANSI-C标准得到特殊的含义。
转义字符 | 描述 |
---|---|
\a | alert 警报 |
\b | backspace 退格键 |
\e | 一个转义字符 |
\f | form feed 换页符 |
\n | newline 换行 |
\r | carriage return 回车 |
\t | horizontal tab 水平制表符 |
\v | vertical tab 垂直制表符 |
\\ |
反斜杠 |
\' |
单引号 |
\nnn | 字符的八进制值(见[http:/www.asciitable.com/ASCII表]) |
\xnn | 字符的十六进制值(见[http:/www.asciitable.com/ASCII表]) |
ANSI-C 引用语法:$‘
[abc@localhost ~]$ vi ansic.sh #!/bin/bash echo $'http://www.abc.com\nmai\x40abc.com' [root@docker-2 ~]# vi a.sh #!/bin/bash echo $'http://www.baidu.com\nmail\x40baidu.com'
执行结果:
[abc@localhost ~]$ sh ansic.sh http://www.abc.com mai@abc.com [root@docker-2 ~]# sh a.sh http://www.baidu.com mail@baidu.com
本教程pdf下载地址:http://down.51cto.com/data/2451862 下载内容包含笔者29章节的 shell学习笔记 也可参考shell连接:http://tldp.org/LDP/abs/html/