bash脚本编程
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了bash脚本编程相关的知识,希望对你有一定的参考价值。
bash脚本编程的结构:bash脚本编程语言:
脚本类语言
解释性语言
过程式编程语言
过程式编程语言的结构:
顺序执行结构:默认
从上到下,自左而右执行所有的语句(命令)
选择执行结构:
当条件满足或不满足时才会执行对应的语句(命令)
循环执行结构:
重复执行某段语句(命令)
bash脚本编程语言中也具备上述结构:
顺序执行结构:默认
选择执行结构:
根据给定的条件逻辑判断结构或根据某个可选取的取值范围,进而选择某个分支结构中的命令语句予以执行的方式;
if:
选择执行结构的标准,根据条件的逻辑判断结果选择执行的语句内容;
case:
选择执行结构的标准,根据符合某特定范围取值标准来选择执行的语句内容;
循环执行结构:
对于特定语句内容,重复执行0次,1次或多次;
for:以遍历列表的方式来进行循环;
while:根据给定条件的逻辑判断结果进行循环;逻辑判断结果为真,才循环;否则,停止循环;
until:根据给定条件的逻辑判断结果进行循环;逻辑判断结果为假,才循环;否则,停止循环;
select:死循环,即没有默认退出条件的循环;利用循环提供一个可选择的列表;
bash脚本的执行结构值if选择执行结构:
if语句:
if 命令; then 命令; [ elif 命令; then 命令; ]... [ else 命令; ] fi
1.if语句的单分支结构:
if 命令; then 命令; fi
注意:是否会执行then后面的命令,取决于if后面的命令的执行状态返回值;
1.如果其返回值为真,则执行then后面的命令;
2.如果其返回值为假,则不执行then后面的命令;
建议在脚本中的书写格式:
if CONDITION ;then
STATEMENT
...
fi
或者
if CONDITION
then
STATEMENT
...
fi
2.if语句的双分支结构:
if 命令; then 命令; [ else 命令; ] ;fi
注意:是否会执行then后面的命令,取决于if后面的命令的执行状态返回值;
1.如果其返回值为真,则执行then后面的命令;
2.如果其返回值为假,则不执行else后面的命令;
建议在脚本中的书写格式:
if CONDITION ;then
STATEMENT
...
else
STATEMENT
...
fi
或者
if CONDITION
then
STATEMENT
...
else
STATEMENT
...
fi
3.if语句的多分支结构:
if 命令; then 命令; [ elif 命令; then 命令; ]... [ else 命令; ] fi
注意:是否会执行then后面的命令,取决于if后面的命令的执行状态返回值或elif和面的命令的执行状态返回值;
1.首先判断if后面的命令的状态返回值是否为真,如果为真就执行then后面的语句;如果为假,就继续判断第一个elif后面的命令 的执行状态返回值;
2.第一个elif后面的命令的执行状态返回值为真,就执行第一个elif后面then后面的命令,否则,就继续判断第二个elif后面的命 令的执行状态返回值;
3.以此类推,会判断每个elif后面的命令执行状态返回值是否为真;如果所有的if和elif后面的命令执行状态返回值均为假,则执 行else后面的语句;
建议在脚本中的书写格式:
if CONDITION1 ;then
STATEMENT
...
elif CONDITION2 ; then
STATEMENT
...
elif CONDITION3 ; then
STATEMENT
...
...
else
STATEMENT
...
fi
或者
if CONDITION
then
STATEMENT
...
elif CONDITION2
then
STATEMENT
...
elif CONDITION3
then
STATEMENT
...
...
else
STATEMENT
...
fi
注意:if的多分支结构,使用场景不多,而且有些时候,可以使用嵌套的单分支或双分支if结构代替if多分支结构;
嵌套的if结构:
if CONDITION1 ;then
if CONDITION2 ;then
if CONDITION3 ; then
STATEMENT
...
else
STATEMENT
fi
else
STATEMENT
...
fi
else
STATEMENT
...
fi
示例:
1.判断某个用户的默认登录shell是否为/bin/bash;
#!/bin/bash # USERNAME=$(cut -d : -f 1 /etc/shadow | sort -R | head -1) USERSHELL=$(egrep "^$USERNAME\>" /etc/passwds | cut -d : -f 7) if [ "$USERSHELL" == "/bin/bash" ] ; then echo "${USERNAME}'s login shell is /bin/bash." fi unset USERNAME USERSHELL
2.判断某个用户的默认登录shell是否为/bin/bash;如果不是就显示它的登录shell;
#!/bin/bash # USERNAME=$(cut -d : -f 1 /etc/shadow | sort -R | head -1) USERSHELL=$(egrep "^$USERNAME\>" /etc/passwd | cut -d : -f 7) if [ "$USERSHELL" == "/bin/bash" ] ; then echo "${USERNAME}'s login shell is /bin/bash." else echo "${USERNAME}'s login shell is ${USERSHELL}." fi
bash脚本编程之用户交互使用:
位置参数变量:
$0:命令的本身,对于脚本而言,就是该脚本的路径;
$1,$2,...$N:脚本后面通过命令行给脚本传递的命令行参数;
N>9时,引用该位置变量时需要加{},即:${10}; $10,只会认为其是$1;
#!/bin/bash # #USERNAME=$(cut -d : -f 1 /etc/shadow | sort -R | head -1) USERSHELL=$(egrep "^$1\>" /etc/passwds | cut -d : -f 7) if [ "$USERSHELL" == "/bin/bash" ] ; then echo "${1}'s login shell is /bin/bash." fi
特殊变量:
[email protected]:给出的所有位置参数的列表,当使用双引号引用时,每个参数作为单独的字符串存在;
$*:给出的所有位置参数的列表,当使用双引号引用时,整个参数列表被当做一个字符串;
$#:表示除去$0之外,整个命令行中有多少个参数;
read命令:
Read a line from the standard input and split it into fields.
read [-ers] [-a 数组] [-p 提示符] [-t 超时] [-u 文件描述符] [名称 ...]
-a array:定义索引数组;
-p promt:给用户输出提示信息;
-t timeout:用户输入的超时时间;
name:变量或数组的名称;如果省略此内容,bash会将read读到的信息直接保存到内置的名为REPLY
变量中;
注意:
Linux哲学思想之一:尽量不与用户交互;
在使用read命令时,通常会使用-t选项来指定与用户交互的时间,一旦时间超过预定时间,脚本中后续的命令内容会自动被执行;因此,通常需要在后面判断通过read赋值的变量值是否为空,如果为空,我们可能需要为该变量提供默认值;
read -t 5 VAR1
[ -z $VAR1 ] && VAR1=value1
管理用户脚本:
脚本可以接受两个参数,第一个参数为-a或-d,第二个参数为用户名;若果第一个参数时-a,则创建其后面参数命令的用户;如果第一个参数为-d,则删除其后面参数命令的用户;
#!/bin/bash # if [ $# -ne 2 ] ; then echo "Make sure provide TWO arguments." exit 5 fi if [ $1 == '-a' ] ; then if ! id $2 &> /dev/null ; then useradd $2 &> /dev/null echo $2 | passwd --stdin $2 &> /dev/null echo "User $2 created succesfully and password changed to it's username." else echo "$2 exists already." fi elif [ $1 == '-d' ] ; then if id $2 &> /dev/null ; then userdel -r $2 &> /dev/null echo "User $2 delete finished." else echo "$2 does not exist yet." fi else echo "Usage: $(basename $0) -a USERNAME | -d USERNAME" exit 6 fi
改进版:
#!/bin/bash # if [ $# -ne 1 ] ; then echo "Make sure provide ONE argument." exit 5 fi if [ $1 == '-a' ] ; then read -p "Please input a username for creating: " USERNAME if ! id $USERNAME &> /dev/null ; then useradd $USERNAME &> /dev/null echo $USERNAME | passwd --stdin $USERNAME &> /dev/null echo "User $USERNAME created succesfully and password changed to it's username." else echo "$USERNAME exists already." fi elif [ $1 == '-d' ] ; then read -p "Please input a username for deleting: " USERNAME read -t 5 -p "confirm? Input 'yes' to continue: " CHOICE [ -z $CHOICE ] && CHOICE='no' if [ $CHOICE == 'yes' ] ; then if id $USERNAME &> /dev/null ; then userdel -r $USERNAME &> /dev/null echo "User $USERNAME delete finished." else echo "$USERNAME does not exist yet." fi else echo echo "$USERNAME is not deleted." fi else echo "Usage: $(basename $0) { -a | -d }" exit 6 fi
判断用户通过命令行给的一个参数是否为整数。
#!/bin/bash # if [ $# -ne 1 ] ; then echo "确定这是一个参数。" exit 4 fi if ! [[ $1 =~ [^[:digit:]] ]] &> /dev/null ; then echo "这是一个整数。" else echo "这不是一个整数。" fi
循环执行结构:
循环:将某一段代码或者命令重复执行0次,1次或多次;
一个好的循环结构,必须要包括两个重要的环节;
1.进入循环的条件:
在符合要求或满足条件时才开始循环;
2.退出循环的条件:
达到某个要求或符号某个条件时需要结束或终止循环的执行;
for循环:
1.遍历列表的循环:
Execute commands for each member in a list.
格式:
for NAME [in WORDS ... ] ; do COMMANDS; done
建议在脚本中的书写格式:
for VAR_NAME in LiST ; do
循环体
done
或者
for VAR_NAME in LiST
do
循环体
done
注意:
VAR_NAME :任意指定的变量名称,变量的值是从LIST中遍历获取的各个元素;
LIST:for循环需要遍历的列表;可以通过以下方式生成列表;
1.直接给出列表;
2.纯整数列表;
1)花括号展开:
{FIRSTNUM..LASTNUM}
{FIRST,SECIND,THIRD,...LAST}
2)seq命令:
seq [OPTION]... LAST
seq [OPTION]... FIRST LAST
seq [OPTION]... FIRST INCREMENT LAST
3.花括号展开:
{FIRST..LAST} (字符)
4.命令的执行结果:
ls /etc
grep /PATH/TO/SOMEFILE
5.GLOBBING
6.某些特殊变量的值:
$*,&@
循环体:
一般来说,循环体中应该能够用到VAR_NAME变量的值的命令或命令的组合;如果循环体中的命令并没有用到VAR_NAME变量的值得话,列表的元素个数就是此次for循环的次数;
使用for循环,计算1到100的数字之和:
#!/bin/bash # for I in {1..100} 或者 $(seq 1 100}; do SUM=$[SUM+I] 或者 let SUM+=$I done echo "The summary is: $SUM:"
1到100所有奇数的和
#!/bin/bash # for I in {1..100..2} 或者${seq 1 2 100}; do SUM=$[SUM+I] 或者 let SUM+=$I done echo "The summary is: $SUM:"
0到100所有偶数的和
#!/bin/bash # for I in {0..100..2} 或者${seq 0 2 100}; do SUM=$[SUM+I] 或者 let SUM+=$I done echo "The summary is: $SUM:"
写一个脚本,能够通过-a或-d选项添加或删除一个或多个用户账户;
#!/bin/bash # if [ $# -lt 2 ] ; then echo "Make sure provide more than TWO arguments." exit 5 fi if [ $1 == '-a' ] ; then shift for I in "[email protected]" ; do if ! id $I &> /dev/null ; then useradd $I &> /dev/null echo $I | passwd --stdin $I &> /dev/null echo "User $I created succesfully and password changed to it's username." else echo "$I exists already." fi done elif [ $1 == '-d' ] ; then shift for J in "[email protected]" ; do if id $J &> /dev/null ; then userdel -r $J &> /dev/null echo "User $J delete finished." else echo "$J does not exist yet." fi done else echo "Usage: $(basename $0) -a UNAME1 [UNAME2 ...] | -d UNAME1 [UNAME2 ...]" exit 6 fi
总结:
1.进入循环的条件:LIST中尚有未被取尽的元素;
2.退出循环的条件:LIST中的元素被取尽;
3.for循环几乎不会出现死循环;
4.在执行循环的过程中,需要将整个LIST载入内存,因此,对于大列表来说,可能会消耗较多的内存及CPU资源;
指定指定范围内自然数的和:
#!/bin/bash # declare -i SUM=0 read -p "Please input TWO integer: " INT1 INT2 if [[ $INT1 =~ [^[:digit:]] ]] ; then echo "$INT1 must be an integer." exit 5 fi if [[ $INT2 =~ [^[:digit:]] ]] ; then echo "$INT2 must be an integer." exit 5 fi if [ $INT1 -gt $INT2 ] ; then for I in $(seq $INT2 $INT1) ; do # SUM=$[SUM+I] let SUM+=$I done echo "The summary is: $SUM" else for I in $(seq $INT1 $INT2) ; do # SUM=$[SUM+I] let SUM+=$I done echo "The summary is: $SUM" fi
打印有"*"组成的倒置的等腰三角形;
********* 1行,0个空白字符,9个"*"
******* 2行,1个空白字符,7个"*"
***** 3行,2个空白字符,5个"*"
*** 4行,3个空白字符,3个"*"
* 5行,4个空白字符,1个"*"
N行,N-1个空白字符,2*(总行数-当前行数)+1个"*"
#!/bin/bash # LINENUM=$1 for I in $(seq $LINENUM) ; do for J in $(seq $[I-1]) ; do echo -n " " done for K in $(seq $[2*(LINENUM-I)+1]) ; do echo -n "*" done echo done
打印九九乘法表:
第一行:1个
第二行:2个
...
第九行:9个
#!/bin/bash # for I in {1..9} ; do for J in $(seq $I) ; do echo -ne "$I×$J=$[I*J]\t" done echo done
#!/bin/bash # for (( I=1 ; I<=9 ; I++ )) ; do for (( J=1 ; J<=I ; J++ )) ; do echo -ne "$J×$I=$[I*J]\t" done echo done
以上两个例子,均使用for循环的嵌套;往往需要两层的循环嵌套才能打印平面效果;外层的for循环,负责控制行数输出;内层的for循环,负责每一行中各个 列的输出;
2.通过控制变量实现for循环;
for ((: for (( exp1; exp2; exp3 )); do COMMANDS; done
可以在脚本中携程如下格式:
for ((: for (( exp1; exp2; exp3 )) ; do
COMMANDS
done
exp1:表达式1,为指定的变量赋初始值;
exp2:表达式2,此次循环的退出条件;
exp3:表达式3,指定的变量值得变化规律;
计算从1到100的自然数的和:
#!/bin/bash # for(( i=1 ; I<=100 ; I++ )) ; do let SUM+=$I done echo "The summary is :$SUM"
以上是关于bash脚本编程的主要内容,如果未能解决你的问题,请参考以下文章
谢烟客---------Linux之bash脚本编程---if补充和for循环
-bash: /usr/bin/ls: /lib64/ld-linux-x86-64.so.2: bad ELF interpreter: No such file or directory(代码片段