bash [OPTION] FILENAME
例:bash -x ./test.sh
-n :检查脚本语法错误
注意:不是命令语法错误
-x: 调试运行
for循环
语法1:
for 变量名 in 循环列表; do
循环体
done
List的生成方式:
1、整数序列:{start..end},例如:{1..10}
2、整数序列:seq 开始数步长结束数,例如:$(seq 1 2 10)
3、命令的执行结果:例如,$(ls/var) 或 `ls /var`
语法2:
for ((变量初始值赋值;循环条件;变量的修正表达式)); do
循环体
done
例:1+2+3+…+10
(1) sum=0
for i in {1,10};do
let sum+=$i
done
echo $sum
(2) sum=0
for i in `seq 1 10`;do
let sum+=$i
done
echo $sum
(3) sum=0
for ((i=1;i<=10;i++));do
let sum+=$i
done
echo $sum
例: 2+4+6+…+10
(1) for i in `seq 2 210`;do
let sum+=$i
done
echo $sum
(2) for ((i=2;i<=10;i=$i+2));do
letsum+=$i
done
echo$sum
用for遍历
例:
显示当前系统上所有默认shell为bash的用户的用户名、UID以及此类所有用户的UID之和
找出默认shell为bash的用户名,uid
#!/bin/bash
grep '/bin/bash$' /etc/passwd|cut -d':' -f1,3
uid=`grep '/bin/bash$' /etc/passwd|cut -d':' -f3`
#计算uid之和
for i in $uid;do
letsum_id+=$i
done
echo $sum_id
练习:写一个脚本
(1) 接受一个以上文件路径作为参数;
(2) 显示每个文件拥有的行数;
(3) 总结说明本次共为几个文件统计了其行数;
#!/bin/bash
#
echo"Files: $@."
forfile in $@; do
lines=$(wc -l $file | cut -d' ' -f1)
echo "$file has $lines line(s)."
done
echo"Total files: $#."
练习:写一个脚本
(1) 传递两个以上字符串当作用户名;
(2) 创建这些用户;且密码同用户名;
(3) 总结说明共创建了几个用户;
#/bin/bash
#
foruser in $@; do
useradd$user
echo$user | passwd --stdin $user &> /dev/null
done
echo"Total users: $#."
算术运算:
如何定义整型变量?
(1)let Var_Name=INTEGER_VALUE
letnum1=3
(2)declare -i Var_Name=INTEGER_VALUE
declare-i num2=6
注意:即使没有定义变量类型为整型,字符型的整数依然可以参与算术运算,是因为bash会进行隐式的数据类型转换
bash的算术运算:
(1)let: let Var_Name=ARITHMATIC_EXPRESSION
例:let A=1;let B=2;let C=$A+$B ==>C=3
A=1 ; B=2 ; C=$A+$B==>C=1+2
(2)$[]: Var_Name=$[ARITHMATIC_EXPRESSION]
例:C=$[$A+$B]
(3) $(()):Var_Name=$((ARITHMATIC_EXPRESSION))
例: C=$(($A+$B))
(4) expr命令:Var_Name=$(exprEXPRESSION)
Var_Name=$(expr ARGU1 operator $ARGU2)
例:C=`expr$A+$B`
算术运算:+;-;*;/;%:取模,取余数;**:幂运算
条件测试:
bash命令执行结束后有退出状态码:
0-255:
0:成功执行
1-255:执行失败
可以用 exit 数字 [例:exit 0] 设置脚本退出时的状态码
整数测试:
双目操作符:需要两个操作数;
$A -gt $B: 大于
-ge:大于等于
-lt:小于
-le:小于等于
-ne:不等于
-eq:等于
字符串测试:例$str1, $str2
双目:
"$str1" >"$str2": 测试str1是否大于str2,是则为真,不是则为假;
<:是否小于;
==: 是否等于;
!=: 是否不等于;
单目:
-z "$str1": 测试str1字符串是否为空,空则为真,不空则为假;
-n "$str1": 测试str1字符串是否不空,不空则为真,空则为假;
模式匹配测试:
"$str1"=~PATTERN:
如果str1字符串能够被后面的模式所匹配则为真,否则为假;
练习:写一个脚本,完成如下功能
(1) 传递一个磁盘设备文件路径给脚本,判断此设备是否存在;
(2) 如果存在,则显示此设备上的所有分区信息;
#!/bin/bash
#
if! [[ "$1" =~ ^/dev/[sh]d[a-z]$ ]]; then
echo "Not a device file."
exit 1
fi
iffdisk -l /dev/[hs]d[a-z] | grep "^Disk" | grep "$1"&> /dev/null; then
fdisk -l $1
else
echo "No such disk."
fi
文件测试:$file
存在性及类型测试:
-e $file: 文件是否存在,存在为真,否则为假;
-a $file: 同上;
-f $file: 文件是否存在且为普通文件,是则为真,不是则假;
-d $file:路径是否存在且为目录文件;
-h|-L $file: 路径是否为符号链接文件;
-b $file: 路径是否存在且为块设备文件;
-c $file: 路径是否存在且为字符设备文件;
-S $file: 路径是否存在且为套接字文件;
-p $file: 路径是否存在且为管道文件;
文件权限判断:
-r $file: 文件是否存在且对当前用户可读;
-w $file: 文件是否存在且对当前用户可写;
-x $file: 文件是否存在且对当前用户可执行;
-u $file: 文件是否存在且拥有SUID权限;
-g $file: 文件是否存在且拥有SGID权限;
-k $file:文件是否存在且且拥有sticky权限;
-O $file: 当前用户是否路径指向的文件的属主;
-G $file:当前用户是否属于文件的属组;
文件时间戳相关:
-N $file:文件自从最近一次的读访问之后,是否又被修改过;
$f1 -nt $f2: f1是否新于f2;
$f1 -ot $f2: f1是否旧于f2;
$f1 -ef $f2: f1和f2是否为同一个文件的两个硬链接;
组合条件测试:
组合测试条件1:组合测试表达式
或:-o, [ COND_EXPR1 -o COND_EXPR2 ]
与:-a
非:!
例:["$hname" == "localhost" -o "$hname" =="(none)" ]
组合测试条件2:组合测试语句
与:&&
或:||
非:!
例:["$hname" == "localhost" ] || [ "$hname" =="(none)" ]
if语句的语法:
(1)单分支:
if 判断条件; then
分支
fi
(2)双分支
if 判断条件; then
分支1
else
分支2
fi
(3)多分支
if 判断条件1;then
…
elif 判断条件2;then
…
elif 判断条件3;then
…
else
…
fi
注意:if语句只有在判断两个值比较时,才用[ ];
若是if`id root &>/dev/null` ;then
echo root
fi
不用加[ ]
例 if [ $A -lt $B ];then
echo $A
elif [ $str1 == $str2];then
echo $str1
fi
case语句的语法:
case变量引用in
PATTERN1)
分支1
;;
PATTERN2)
分支2
;;
...
*) *表示都不匹配时
分支n
;;
esac
注意:
PATTERN可使用通配符:
*: 任意长度任意字符;
?: 任意单个字符;
[]:指定范围内的任意单个字符;
a|b|c:或者
例:
练习:写一个脚本
(1) 能接受四个参数:start,stop, restart, status
start: 输出“starting脚本名 finished.”
...
(2) 其它任意参数,均报错退出;
#!/bin/bash
#
svc=$(basename$0)
lockfile=/tmp/${svc}
case$1 in
start)
if [ -e $lockfile ]; then
echo "$svc is running"
exit 0
else
echo "starting $svcfinished."
touch $lockfile && exit 0
fi
;;
stop)
echo "stopping $svc finished."
;;
restart)
echo "restarting $svc finished."
;;
status)
echo "running or stopped."
;;
*)
echo "Usage: $svc{start|stop|restart|status}"
exit 1
;;
esac
交互式编程 read :
read -p "注释信息" 变量名
例: read -p "Input one Number :" num1
read -t # :指定超时时间
例: read -t 5 "Input one Number :" num1 [若5s内用户没输入,则继续向下进行,而num1为空]
-a 数组;-A 关联数组
练习:写一个脚本,提示用户输入一个用户名;
在用户存在时,判断其UID是否同GID;
同,则显示“Good guy”,不同,则显示“Bad guy”;
#!/bin/bash
#
read-p "Plz enter a user name: " name
!id $name &> /dev/null && echo "No this user."&& exit 1
if[ `id -u $name` == `id -g $name` ]; then
echo "Good guy"
else
echo "Bad guy"
fi
while循环语句的语法:
whileCONDITION; do
循环体
循环控制变量的修正表达式
done
其中,“CONDITION”,当条件测试结果为“真”,则进入循环;为“假”时,终止循环;循环控制变量要用于CONDITION中,以实现循环条件构建;
示例:求100以内所在正整数之和;
#!/bin/bash
#
declare-i i=1
while[ $i -le 100 ]; do
let sum+=$i
let i++
done
echo"$sum"
示例:打印九九乘法法;
#!/bin/bash
#
declare-i i=1
while[ $i -le 9 ]; do
let j=1
while [ $j -le $i ]; do
echo -n -e"${j}X${i}=$[$i*$j]\t"
let j++
done
echo
let i++
done
示例:写一个脚本,判断给定的用户是否登录了当前系统;
(1) 如果登录了,则显示用户登录,脚本终止;
(2) 每3秒钟,查看一次用户是否登录;
#!/bin/bash
#
read-p "Plz enter a user name: " name
while! who | grep "^$name\>" &> /dev/null; do
sleep 3
done
echo"$name is here."
示例:写一个脚本,显示用户选定要查看的信息;
cpu)display cpu info
mem)display memory info
disk)display disk info
quit)quit
非此四项选择,则提示错误,并要求用户重新选择,只到其给出正确的选择为止;
#!/bin/bash
#
cat<< EOF
cpu) display cpu info
mem) display memory info
disk) display disk info
quit) quit
EOF
read-p "your option: " option
while[ "$option" != 'cpu' -a "$option" != 'mem' -a"$option" != 'disk' -a "$option" != 'quit' ]; do
echo "Wrong option."
read -p "your option again: "option
done
case$option in
cpu)
lscpu ;;
mem)
free ;;
disk)
fdisk -l /dev/[sh]d[a-z] ;;
quit)
exit 0
esac
while循环的特殊用法:
(a)用于遍历指定文件的每一行;
while read变量名; do
循环体
done < /PATH/TO/SOMEFILE
变量用于保存一行数据;直到遇到文件尾,数据终止,循环退出;
示例:找出其UID为偶数的用户,显示用户名和其UID;
#!/bin/bash
#
[$UID -ne 0 ] && echo "Only root can excute this script."&& exit 1
while read line; do
userid=$(echo $line | cut -d: -f3)
if [$[$userid%2] -eq 0 ]; then
echo$line | cut -d: -f1,3
fi
done < /etc/passwd
示例:将给定的偶数行内字符统统修改为大写之后输出;
#!/bin/bash
#
declare-i linenumber=1
read-p "Plz enter a file path: " file
while read line; do
if [$[$linenumber%2] -eq 0 ]; then
echo -n"$linenumber "
echo$line |tr 'a-z' 'A-Z'
fi
letlinenumber++
done < $file
(b) 死循环
while true; do
循环体
done
循环控制:
break [N]:结束循环;
退出当前循环;N表示循环嵌套层次;
continue [N]:提前结束本轮循环,而直接进入下一轮循环条件判断
示例:求100以内所有偶数之和;
#!/bin/bash
#
declare-i evensum=0
declare-i i=0
while[ $i -le 100 ]; do
let i++
if [ $[$i%2] -eq 1 ]; then
continue
fi
let evensum+=$i
done
echo"$evensum"
示例:判断centos是否登录,如果登录则显示之;否则,每3秒查看centos是否登录;
#!/bin/bash
#
user='centos'
whiletrue; do
if who | grep "^$user\>"&> /dev/null; then
break
fi
sleep 3
done
echo"$user is here."
until循环语句的语法:
until CONDITION; do
循环体
控制变量修正语句
done
CONDTION条件为“假”时,执行循环;为“真”时退出循环;与while相反
示例:100以内所有正整数之和:
#!/bin/bash
#
declare-i i=1
declare-i sum=0
until[ $i -gt 100 ]; do
let sum+=$i
let i++
done
echo"$sum"
函数function的语法:
语法结构:
(1) function 函数名 {
函数体
}
(2) 函数名() {
函数体
}
调用函数:函数名 例: checkUser centos [checkUser 为函数名centos 为传入的参数]
函数的返回值:
函数执行过程结果向调用者的回馈;
(a) 显式使用echo或printf命令;
(b) 函数中某些命令运行结果产生的输出;
函数退出状态码:
默认其退出码取决于函数体最后执行的那个语句的退出状态码;
自定义退出状态码:
return[N]
0-255
0:成功
1-255: 失败
注意:函数运行时,一旦遇到return语句即会退出整个函数的运行;
函数可接受参数:
调用函数时,可以传递参数给函数,传递方法类似向脚本传递参数的方法,例如
函数名 arg1 arg2 arg3...
在函数中可使用$1,$2, ...来调用这些参数;
可在函数中使用特殊变量:$#,$@, $*
练习:写一个脚本
(1) 用函数实现返回一个用户的UID和SHELL;用户名通过参数传递而来
(2) 提示用户输入一个用户名或输入“quit”退出;
当输入的是用户名,则调用函数显示用户信息;
当用户输入quit,则退出脚本;
进一步地:显示键入的用户相关信息后,再次提醒输出用户名或quit
#!/bin/bash
checkUser(){
userid=`id -u $1`
usershell=`grep"^$1" /etc/passwd |cut -d: -f7`
echo"$1 uid :$userid "
echo"$1 shell :$usershell"
}
whiletrue ;do
read-p "input username: " name
if! [ -z $name ];then
if`id $name&>/dev/null`;then
checkUser $name
elif[ $name == 'q' ];then
exit0
else
echo"Input or quit"
fi
else
echo"Input or quit"
fi
done
数组语法:
数组:变量阵列,通过同一个名字进行存取操作;
连续的多个独立的内存空间(元素),每个内存空间相当于一个变量;
数组元素:数组名[索引]
索引:从0开始编号
声明数组:
declare-a Array_Name
bash的数组支持稀疏格式;
数组元素赋值:
(1) 一次只赋值一个元素
a_name[index]=value
weekday[0]="Sunday"
weekday[1]="Monday"
(2) 一次赋值全部元素
weekday=("Sunday""Monday" "Tuesday")
(3) 指定索引进行赋值
weekdays=([0]="Sunday"[3]="Thu" [6]="Sat")
(4)read -a a_name
引用数组元素:${array_name[index]}
获取数组长度:${#array[*]}, ${#array[@]}
即数组中元素的个数;
练习:写一个脚本,生成10个随机数,保存至数据组中;而后显示数组索引为偶数的元素的值;
#!/bin/bash
#
for((i=0;i<10;i++)); do
rand[$i]=$RANDOM
echo ${rand[$i]}
done
echo"========================"
fori in `seq 0 2 9`; do
echo ${rand[$i]}
done
练习:写一个脚本
定义一个数组,数组元素为/var/log目录下所有以.log结尾的文件的名字;显示每个文件的行数;
#!/bin/bash
#
declare-a files
files=(/var/log/*.log)
fori in `seq 0 $[${#files[*]}-1]`; do
wc -l ${files[$i]}
done
数组切片:从数组中挑选指定的某个或某些元素:
${array[@]:offset:number}
offset: 偏移的元素的个数;
number:要取出的元素的个数;
${array[@]:offset}
取出偏移量之后剩余所有的元素;
${array[@]}
从数组中删除元素:
unsetarray[index]
bash从4.0版本起支持关联数组:
数组索引可为自定的字符串;
定义方法:declare -A array_name
练习:写一个脚本,能从所有同学中随机挑选一个同学回答问题;
进一步地:可接受一个参数,做为要挑选的同学的个数;
bash的字符串处理:
字符串切片:${var:offset:length}
取出字符串的最后几个字符:${var: -length}
注意:-length之前有空白字符;
基于模式取子串:
${var#*word}:自左而右,查找var变量中存储的字符串中第一次出现的由word所指明的字符,删除此字符及其左侧的所有内容;
${var##*word}:自左而右,查找var变量中存储的字符串中最后一次出现的由word所指明的字符,删除此字符及其左侧的所有内容;
${var%word*}:自右而左,查找var变量中存储的字符串中第一次出现的由word所指明的字符,删除此字符及其右侧的所有内容;
${var%%word*}:自右而左,查找var变量中存储的字符串中最后一次出现的由word所指明的字符,删除此字符及其右侧的所有内容;
示例:
url="http://www.magedu.com:80"
取端口:echo ${url##*:}
取协议:echo ${url%%:*}
查找替换:
${var/pattern/replacement}:查找var变量存储的字符中第一次由pattern匹配到的内容,并替换为replacement;
${var//pattern/replacement}:查找var变量存储的字符中所有能够由pattern匹配到的内容,并替换为replacement;
${var/#pattern/replacement}:查找var变量存储的字符中最开始处能够由pattern匹配到的内容,并替换为replacement;
${var/%pattern/replacement}:查找var变量存储的字符中最后位置能够由pattern匹配到的内容,并替换为replacement;
查找删除:
${var/pattern}:查找var变量存储的字符中第一次由pattern匹配到的内容,并删除;
${var//pattern}:查找var变量存储的字符中所有能够由pattern匹配到的内容,并删除;
${var/#pattern}:查找var变量存储的字符中最开始处能够由pattern匹配到的内容,并删除;
${var/%pattern}:查找var变量存储的字符中最后位置能够由pattern匹配到的内容,并删除;
字符串大小写转换:
${var^^}:把var变量中的所有小写字母,统统替换为大写;
${var,,}:把var变量中的所有大写字母,统统替换为小写;
变量赋值:
${var:-word}:如果变量var为空或未声明,则返回word所表示的字符串;否则,则返回var变量的值;
${var:=word}:如果变量var为空或未声明,则返回word所表示的字符串,并且把word赋值为var变量;否则,则返回var变量的值;
${var:?error}:如果变量var为空或未声明,则返回error为错误信息;否则,则返回var变量的值;
${var:+word}:如果变量var为空或未声明,忽略;否则,则返回word;
转载于:https://blog.51cto.com/xxlixin1993/1654682