Loading... <p style="text-indent: 2em"><span style="font-size: 18px"><strong>前言</strong></span><br style="text-indent: 2em" /></p> <p style="text-indent: 2em">在Linux学习过程中,我们无可避免的会碰到一个既让人喜欢,又令人头疼的神奇的东西——bash编程,即shell脚本。那么什么是shell脚本呢?shell是一个命令语言解释器,而shell脚本则是Linux命令的集合,按照预设的顺序依次解释执行,来完成特定的、较复杂的系统管理任务,类似于Windows中的批处理文件。本文带来的是bash编程的基础语法讲解。</p> <p style="text-indent: 2em"><span style="font-size: 18px"><strong>bash编程之变量</strong></span><br style="text-indent: 2em" /></p> <p style="text-indent: 2em"><strong>bash变量类别</strong></p> <p style="text-indent: 2em">本地变量:只对当前shell进程有效的变量,对其它shell进程无效,包当前shell进程的子进程</p> <pre class="brush:bash;toolbar:false">VAR_NAME=VALUE</pre> <p style="text-indent: 2em">变量赋值:向变量的存储空间保存数据<br style="text-indent: 2em" /></p> <p style="text-indent: 2em">变量引用:${VAR_NAME} </p> <pre class="brush:bash;toolbar:false">"":弱引用,里面的变量会被替换 '':强引用,里面的所有字符都是字面量,直接输出</pre> <p style="text-indent: 2em">环境变量:对当前shell进程及其子shell有效,对其它的shell进程无效 </p> <pre class="brush:bash;toolbar:false">定义:export VAR_NAME=VALUE 导出:export VAR_NAME 撤消变量:unset VAR_NAME 只读变量:readonly VAR_NAME</pre> <p style="text-indent: 2em">局部变量:对shell脚本中某代码片断有效,通常用于函数本地</p> <pre class="brush:bash;toolbar:false">local VAR_NAME=VALUE</pre> <p style="text-indent: 2em">位置变量:用来接受变量指定位置的参数</p> <pre class="brush:bash;toolbar:false">$1,$2...,${10}</pre> <p style="text-indent: 2em">特殊变量:shell对一些参数做特殊处理,这些参数只能被引用而不能被赋值<br style="text-indent: 2em" /></p> <pre class="brush:bash;toolbar:false">$# 传递到脚本的参数个数 $* 显示所有向脚本传递的参数 #与位置变量不同,此选项参数可超过9个 $$ 获取当前shell的进程号 $! 执行上一个指令的进程号 $? 获取执行的上一个指令的返回值 #0为执行成功,非零为执行失败 $- 显示shell使用的当前选项,与set命令功能相同 $@ 与$*相同,但是使用时加引号,并在引号中返回每个参数</pre> <p style="text-indent: 2em">查看变量:<br style="text-indent: 2em" /></p> <pre class="brush:bash;toolbar:false">set:查看当前shell进程中的所有变量 export, printenv, env:查看当前shell进程中的所有环境变量</pre> <p style="text-indent: 2em">变量命名:</p> <p style="text-indent: 2em">1、不能使用程序中的关键字(保留字)</p> <p style="text-indent: 2em">2、只能使用数字、字母和下划线,且不能以数字开头</p> <p style="text-indent: 2em">3、要见名知义</p> <p style="text-indent: 2em">变量类型:</p> <p style="text-indent: 2em">数值型:精确数值(整数),近似数值(浮点型)<br style="text-indent: 2em" /></p> <p style="text-indent: 2em">字符型:char,string</p> <p style="text-indent: 2em">布尔型:true, false</p> <p style="text-indent: 2em">类型转换:显式转换,隐式转换</p> <p style="text-indent: 2em">bash的配置文件:<br style="text-indent: 2em" /></p> </p> <p style="text-indent: 2em">profile类:为交互式登录的用户提供配置</p> <p style="text-indent: 2em">全局:/etc/profile、/etc/profile.d/*.sh</p> <p style="text-indent: 2em">用户:~/.bash_profile</p> <p style="text-indent: 2em">bashrc类:为非交互式的用户提供配置</p> <p style="text-indent: 2em">全局:/etc/bashrc</p> <p style="text-indent: 2em">用户:~/.bashrc</p> <p style="text-indent: 2em">功能:设定本地变量,定义命令别名</p> <p style="text-indent: 2em"><span style="font-size: 18px"><strong>bash编程之编写格式及执行方式</strong></span></p> <p style="text-indent: 2em">编写格式:</p> <p style="text-indent: 2em">shell脚本第一行必须顶格写,用shebang定义指定的解释器来解释该脚本。<br style="text-indent: 2em" /></p> <pre class="brush:bash;toolbar:false">#!/bin/bash #!即为shebang</pre> <p style="text-indent: 2em">其它的以#开头的行均为注释,会被解释器忽略,可用来注释脚本用途及版本,方便使用管理。</p> <p style="text-indent: 2em">执行方式:<br style="text-indent: 2em" /></p> </p> <p style="text-indent: 2em">bash编程属于面向过程编程,执行方式如下:</p> <p style="text-indent: 2em">顺序执行:按命令先后顺寻依次执行</p> <p style="text-indent: 2em">选择执行:测试条件,可能会多个测试条件,某条件满足时,则执行对应的分支</p> <p style="text-indent: 2em">循环执行:将同一段代码反复执行多次,因此,循环必须有退出条件;否则,则陷入死循环</p> <p style="text-indent: 2em">bash执行选项:<br style="text-indent: 2em" /></p> <pre class="brush:bash;toolbar:false">bash -n SHELLNAME #语法测试,测试是否存在语法错误 bash -x SHELLNAME #模拟单步执行,显示每一步执行过程</pre> <p style="text-indent: 2em"><span style="font-size: 18px"><strong>bash之算数运算与逻辑运算</strong></span></p> <p style="text-indent: 2em"><strong>算数运算</strong></p> <p style="text-indent: 2em">定义整型变量:<br style="text-indent: 2em" /></p> <pre class="brush:bash;toolbar:false">let VAR_NAME=INTEGER_VALUE #例如:let a=3 declare -i VAR_NAME=INTEGER_VALUE #declare -i a=3</pre> <p style="text-indent: 2em">实现算术运算的方式:</p> <pre class="brush:bash;toolbar:false">let VAR_NAME=ARITHMATIC_EXPRESSION VAR_NAME=$[ARITHMATIC_EXRESSION] VAR_NAME=$((EXPRESSION)) VAR_NAME=$(expr $num1 + $num2)</pre> <p style="text-indent: 2em">算术运算符:</p> <pre class="brush:bash;toolbar:false">+:加法 -:减法 *:乘法 /:整除 %:取余数 **:乘幂</pre> </p> <p style="text-indent: 2em">注意:即使没有定义为整型变量,字符型的数字依然可以参与算术运算,bash会执行变量类型的隐式类型转换。</p> <p style="text-indent: 2em"><strong>逻辑运算</strong></p> <pre class="brush:bash;toolbar:false">布尔运算:真,假 与运算:真 && 真 = 真 真 && 假 = 假 假 && 真 = 假 假 && 假 = 假 或运算:真 || 真 = 真 真 || 假 = 真 假 || 真 = 真 假 || 假 = 假 非运算:!真=假 !假=真</pre> <p style="text-indent: 2em"><span style="font-size: 18px"><strong>bash编程之条件测试语句</strong></span></p> <p style="text-indent: 2em"><strong>bash条件测试</strong><br style="text-indent: 2em" /></p> <p style="text-indent: 2em">整型测试:整数比较</p> <p style="text-indent: 2em">例如 [ $num1 -gt $num2 ]</p> <pre class="brush:bash;toolbar:false">-gt: 大于则为真 -lt: 小于则为真 -ge: 大于等于则为真 -le: 小于等于则为真 -eq: 等于则为真 -ne: 不等于则为真</pre> <p style="text-indent: 2em">字符测试:字符串比较</p> <p style="text-indent: 2em">双目:</p> <p style="text-indent: 2em">例如[[ "$str1" > "$str2" ]]</p> <pre class="brush:bash;toolbar:false">>: 大于则为真 <: 小于则为真 >=:大于等于则为真 <=:小于等于则为真 ==:等于则为真 !=:不等于则为真</pre> <p style="text-indent: 2em">单目:</p> <pre class="brush:bash;toolbar:false">-n String: 是否不空,不空则为真,空则为假 -z String: 是否为空,空则为真,不空则假</pre> <p style="text-indent: 2em">文件测试:判断文件的存在性及属性等</p> <pre class="brush:bash;toolbar:false">-a FILE:存在则为真;否则则为假; -e FILE: 存在则为真;否则则为假; -f FILE: 存在并且为普通文件,则为真;否则为假; -d FILE: 存在并且为目录文件,则为真;否则为假; -L/-h FILE: 存在并且为符号链接文件,则为真;否则为假; -b: 存在并且为块设备,则为真;否则为假; -c: 存在并且为字符设备,则为真;否则为假 -S: 存在并且为套接字文件,则为真;否则为假 -p: 存在并且为命名管道,则为真;否则为假 -s FILE: 存在并且为非空文件则为值,否则为假; -r FILE:文件可读为真,否则为假 -w FILE:文件可写为真,否则为假 -x FILE:文件可执行为真,否则为假 file1 -nt file2: file1的mtime新于file2则为真,否则为假; file1 -ot file2:file1的mtime旧于file2则为真,否则为假;</pre> <p style="text-indent: 2em">组合条件测试:在多个条件间实现逻辑运算<strong><br style="text-indent: 2em" /></strong></p> <pre class="brush:bash;toolbar:false">与:[ condition1 -a condition2 ] condition1 && condition2 或:[ condition1 -o condition2 ] condition1 || condition2 非:[ -not condition ] ! condition 与:COMMAND1 && COMMAND2 COMMAND1如果为假,则COMMAND2不执行 或:COMMAND1 || COMMAND2 COMMAND1如果为真,则COMMAND2不执行 非:! COMMAND</pre> <p style="text-indent: 2em"><strong>条件测试之if语句</strong><br style="text-indent: 2em" /></p> <p style="text-indent: 2em">if语句之单分支</p> <p style="text-indent: 2em">语句结构:<br style="text-indent: 2em" /></p> <pre class="brush:bash;toolbar:false">if 测试条件;then 选择分支 fi</pre> <p style="text-indent: 2em">表示条件测试状态返回值为值,则执行选择分支</p> <p style="text-indent: 2em">例:写一个脚本,接受一个参数,这个参数是用户名;如果此用户不存在,则创建该用户;</p> <pre class="brush:bash;toolbar:false">#!/bin/bash if ! id $1 &> /dev/null;then useradd $1 fi</pre> <p style="text-indent: 2em">if语句之双分支</p> <p style="text-indent: 2em">语句结构:<br style="text-indent: 2em" /></p> <pre class="brush:bash;toolbar:false">if 测试条件;then 选择分支1 else 选择分支2 fi</pre> <p style="text-indent: 2em">两个分支仅执行其中之一</p> <p style="text-indent: 2em">例:通过命令行给定一个文件路径,而后判断:如果此文件中存在空白行,则显示其空白行的总数;否则,则显示无空白行;</p> <pre class="brush:bash;toolbar:false">#!/bin/bash if grep "^[[:space]]*$" $1 &> /dev/null; then echo "$1 has $(grep "^[[:space]]*$" $1 | wc -l) blank lines." else echo "No blank lines" fi</pre> <p style="text-indent: 2em">注意:如果把命令执行成功与否当作条件,则if语句后必须只跟命令本身,而不能引用。</p> <p style="text-indent: 2em">补充:bash交互式编程</p> <p style="text-indent: 2em">read [option] “prompt”</p> <pre class="brush:bash;toolbar:false">-p:直接指定一个变量接受参数 -t timaout:指定等待接受参数的时间 -n:表示不换行</pre> <p style="text-indent: 2em">例:输入用户名,可返回其shell</p> <pre class="brush:bash;toolbar:false">#!/bin/bash read -p "Plz input a username: " userName if id $userName &> /dev/null; then echo "The shell of $userName is `grep "^$userName\>" /etc/passwd | cut -d: -f7`." else echo "No such user. stupid." fi</pre> <p style="text-indent: 2em">if语句之多分支<br style="text-indent: 2em" /></p> <p style="text-indent: 2em">语句结构:</p> <pre class="brush:bash;toolbar:false">if 条件1;then 分支1 elif 条件2;then 分支2 elif 条件3;then 分支3 ... else 分支n fi</pre> <p style="text-indent: 2em">例:传递一个用户名给脚本:如果此用户的id号为0,则显示说这是管理员;如果此用户的id号大于等于500,则显示说这是普通用户;否则,则说这是系统用户。</p> <pre class="brush:bash;toolbar:false">#!/bin/bash if [ $# -lt 1 ]; then echo "Usage: `basename $0` username" exit 1 fi if ! id -u $1 &> /dev/null; then echo "Usage: `basename $0` username" echo "No this user $1." exit 2 fi if [ $(id -u $1) -eq 0 ]; then echo "Admin" elif [ $(id -u $1) -ge 500 ]; then echo "Common user." else echo "System user." fi</pre> <p style="text-indent: 2em"><strong>条件测试之case语句</strong></p> <p style="text-indent: 2em">case语句:有多个测试条件时,case语句会使得语法结构更明晰</p> <p style="text-indent: 2em">语句结构:</p> <pre class="brush:bash;toolbar:false">case 变量引用 in PATTERN1) 分支1 ;; PATTERN2) 分支2 ;; ... *) 分支n ;; esac</pre> <p style="text-indent: 2em">PATTERN:类同于文件名通配机制,但支持使用|表示或者</p> <pre class="brush:bash;toolbar:false">a|b: a或者b *:匹配任意长度的任意字符 ?: 匹配任意单个字符 []: 指定范围内的任意单个字符</pre> <p style="text-indent: 2em">例:写一个脚本,完成如下任务,其使用形式如下所示:</p> <p style="text-indent: 2em">script.sh {start|stop|restart|status}</p> <p style="text-indent: 2em">其中:</p> <p style="text-indent: 2em">如果参数为空,则显示帮助信息,并退出脚本;</p> <p style="text-indent: 2em">如果参数为start,则创建空文件/var/lock/subsys/script,并显示“starting script successfully.”</p> <p style="text-indent: 2em">如果参数为stop,则删除文件/var/lock/subsys/script,并显示“Stop script successfully.”</p> <p style="text-indent: 2em">如果参数为restart,则删除文件/var/locksubsys/script并重新创建,而后显示“Restarting script successfully.”</p> <p style="text-indent: 2em">如果参数为status,那么:如果文件/var/lock/subsys/script存在,则显示“Script is running…”,否则,则显示“Script is stopped.”</p> <pre class="brush:bash;toolbar:false">#!/bin/bash file='/var/lock/subsys/script' case $1 in start) if [ -f $file ];then echo "Script is running..." exit 3 else touch $file [ $? -eq 0 ] && echo "Starting script successfully." fi ;; stop) if [ -f $file ];then rm -rf $file [ $? -eq 0 ] && echo "Stop script successfully." else echo "Script is stopped..." exit 4 fi ;; restart) if [ -f $file ];then rm -rf $file [ $? -eq 0 ] && echo "Stop script successfully" else echo "Script is stopped..." exit 5 fi touch $file [ $? -eq 0 ] && echo "Starting script successfully" ;; status) if [ -f $file ];then echo "Script is running..." else echo "Script is stopped." fi ;; *) echo "`basename $0` {start|stop|restart|status}" exit 2 ;; esac</pre> <p style="text-indent: 2em"><span style="font-size: 18px"><strong>bash编程之循环语句</strong></span></p> <p style="text-indent: 2em"><strong>循环之for循环</strong></p> <p style="text-indent: 2em">for语句格式一</p> <p style="text-indent: 2em">语句结构:</p> </p> <pre class="brush:bash;toolbar:false">for 变量名 in 列表; do 循环体 done</pre> <p style="text-indent: 2em">列表:可包含一个或多个元素<br style="text-indent: 2em" /></p> <p style="text-indent: 2em">循环体:依赖于调用变量来实现其变化</p> <p style="text-indent: 2em">循环可嵌套</p> <p style="text-indent: 2em">退出条件:遍历元素列表结束</p> <p style="text-indent: 2em">例:求100以内所有正整数之和<br style="text-indent: 2em" /></p> <pre class="brush:bash;toolbar:false">#!/bin/bash declare -i sum=0 for i in {1..100}; do let sum+=$i done echo $sum</pre> <p style="text-indent: 2em">for语句格式二</p> <pre class="brush:bash;toolbar:false">for ((初始条件;测试条件;修改表达式)); do 循环体 done</pre> <p style="text-indent: 2em">先用初始条件和测试条件做判断,如果符合测试条件则执行循环体,再修改表达式,否则直接跳出循环。</p> <p style="text-indent: 2em">例:求100以内所有正整数之和(for二实现)</p> <pre class="brush:bash;toolbar:false">#!/bin/bash declare -i sum=0 for ((counter=1;$counter <= 100; counter++)); do let sum+=$counter done echo $sum</pre> <p style="text-indent: 2em"><strong>循环之while语句</strong><br style="text-indent: 2em" /></p> <p style="text-indent: 2em">while适用于循环次数未知,或不便用for直接生成较大的列表时<strong><br style="text-indent: 2em" /></strong></p> <p style="text-indent: 2em">语句结构:</p> <pre class="brush:bash;toolbar:false">while 测试条件; do 循环体 done</pre> <p style="text-indent: 2em">测试条件为真,进入循环;测试条件为假,退出循环</p> <p style="text-indent: 2em">例:求100以内所有偶数之和,要求使用取模方法</p> <pre class="brush:bash;toolbar:false">#!/bin/bash declare -i counter=1 declare -i sum=0 while [ $counter -le 100 ]; do if [ $[$counter%2] -eq 0 ]; then let sum+=$counter fi let counter++ done echo $sum</pre> <p style="text-indent: 2em">例:提示用户输入一个用户名,如果用户存在,就显示用户的ID号和shell;否则显示用户不存在;显示完成之后不退出,再次重复前面的操作,直到用户输入q或quit为止</p> <pre class="brush:bash;toolbar:false">#!/bin/bash read -p "Plz enter a username: " userName while [ "$userName" != 'q' -a "$userName" != 'quit' ]; do if id $userName &> /dev/null; then grep "^$userName\>" /etc/passwd | cut -d: -f3,7 else echo "No such user." fi read -p "Plz enter a username again: " userName done</pre> <p style="text-indent: 2em">while特殊用法:遍历文本文件<strong><br style="text-indent: 2em" /></strong></p> <p style="text-indent: 2em">语句结构:<br style="text-indent: 2em" /></p> <pre class="brush:bash;toolbar:false">while read 变量名; do 循环体 done < /path/to/somefile</pre> <p style="text-indent: 2em">变量名,每循环一次,记忆了文件中一行文本</p> <p style="text-indent: 2em">例:显示ID号为偶数,且ID号同GID的用户的用户名、ID和SHELL</p> <pre class="brush:bash;toolbar:false">while read line; do userID=`echo $line | cut -d: -f3` groupID=`echo $line | cut -d: -f4` if [ $[$userID%2] -eq 0 -a $userID -eq $groupID ]; then echo $line | cut -d: -f1,3,7 fi done < /etc/passwd</pre> <p style="text-indent: 2em"><strong>循环之until语句</strong><br style="text-indent: 2em" /></p> <p style="text-indent: 2em">语句结构:<strong><br style="text-indent: 2em" /></strong></p> <pre class="brush:bash;toolbar:false">until 测试条件; do 循环体 done</pre> <p style="text-indent: 2em">测试条件为假,进入循环;测试条件为真,退出循环</p> <p style="text-indent: 2em">例:求100以内所有偶数之和,要求使用取模方法(until实现)</p> <pre class="brush:bash;toolbar:false">#!/bin/bash declare -i counter=1 declare -i sum=0 until [ $counter -gt 100 ]; do if [ $[$counter%2] -eq 0 ]; then let sum+=$counter fi let counter++ done echo $sum</pre> <p style="text-indent: 2em">例:提示用户输入一个用户名,如果用户存在,就显示用户的ID号和shell;否则显示用户不存在;显示完成之后不退出,再次重复前面的操作,直到用户输入q或quit为止(until实现)</p> <pre class="brush:bash;toolbar:false">#!/bin/bash read -p "Plz enter a username: " userName until [ "$userName" = 'q' -a "$userName" = 'quit' ]; do if id $userName &> /dev/null; then grep "^$userName\>" /etc/passwd | cut -d: -f3,7 else echo "No such user." fi read -p "Plz enter a username again: " userName done</pre> <p style="text-indent: 2em"><strong>循环之循环控制和shift</strong><br style="text-indent: 2em" /></p> <p style="text-indent: 2em">循环控制命令:<strong><br style="text-indent: 2em" /></strong></p> <p style="text-indent: 2em">break:提前退出循环</p> <p style="text-indent: 2em">break [N]: 退出N层循环;N省略时表示退出break语句所在的循环</p> <p style="text-indent: 2em">continue: 提前结束本轮循环,而直接进入下轮循环</p> <p style="text-indent: 2em">continue [N]:提前第N层的循环的本轮循环,而直接进入下轮循环</p> <p style="text-indent: 2em">死循环:</p> <pre class="brush:bash;toolbar:false">#while体 while true; do 循环体 done #until体 until false; do 循环体 done</pre> <p style="text-indent: 2em">例:写一个脚本,判断给定的用户是否登录了当前系统</p> </p> <p style="text-indent: 2em">(1) 如果登录了,则脚本终止;</p> <p style="text-indent: 2em">(2) 每5秒种,查看一次用户是否登录;</p> <pre class="brush:bash;toolbar:false">#!/bin/bash while true; do who | grep "gentoo" &> /dev/null if [ $? -eq 0 ];then break fi sleep 5 done echo "gentoo is logged."</pre> <p style="text-indent: 2em">shift:如果没有数字,只有shift 就是跳过一个参数获取下一个参数,如果加上数字,比如shift 2 ,跳过两个参数获取下一个参数。</p> <p style="text-indent: 2em">例:写一个脚本,使用形式如下所示 </p> <p style="text-indent: 2em"> showifinfo.sh [-i INTERFACE|-a] [-v]</p> <p style="text-indent: 2em"> 要求:</p> <p style="text-indent: 2em"> 1、-i或-a不可同时使用,-i用于指定特定网卡接口,-a用于指定所有接口;</p> <p style="text-indent: 2em"> 显示接口的ip地址</p> <p style="text-indent: 2em"> 2、使用-v,则表示显示详细信息</p> <p style="text-indent: 2em"> 显示接口的ip地址、子网掩码、广播地址;</p> <p style="text-indent: 2em"> 3、默认表示仅使用-a选项;</p> <pre class="brush:bash;toolbar:false">#!/bin/bash allinterface=0 ifflag=0 verbose=0 interface=0 if [ $# -eq 0 ];then ifconfig | grep "inet addr:" | awk '{print $2}' fi while [ $# -ge 1 ];do case $1 in -a) allinterface=1 shift 1 ;; -i) ifflag=1 interface=$2 shift 2 ;; -v) verbose=1 shift 1 ;; *) echo "error option" exit 2 ;; esac done if [ $allinterface -eq 1 ];then if [ $ifflag -eq 1 ];then echo "command not found" exit 5 fi if [ $verbose -eq 1 ];then ifconfig | grep "inet addr:" else ifconfig | grep "inet addr:" | awk '{print $2}' fi fi if [ $ifflag -eq 1 ];then if [ $allinterface -eq 1 ];then echo "command not found" exit 5 fi if [ $verbose -eq 1 ];then ifconfig $interface | grep "inet addr:" else ifconfig $interface | grep "inet addr:" | awk '{print $2}' fi fi</pre> <p style="text-indent: 2em"><span style="font-size: 18px"><strong>bash编程之函数</strong></span><br style="text-indent: 2em" /></p> <p style="text-indent: 2em">语法结构:<br style="text-indent: 2em" /></p> <pre class="brush:bash;toolbar:false">function F_NAME { 函数体 } F_NAME() { 函数体 }</pre> <p style="text-indent: 2em">可调用:使用函数名,函数名出现的地方,会被自动替换为函数</p> </p> <p style="text-indent: 2em">函数的返回值:</p> <p style="text-indent: 2em"> 函数的执行结果返回值:代码的输出</p> <p style="text-indent: 2em"> 函数中使用打印语句:echo, printf</p> <p style="text-indent: 2em"> 函数中调用的系统命令执行后返回的结果</p> <p style="text-indent: 2em"> 执行状态返回值:</p> <p style="text-indent: 2em"> 默认取决于函数体执行的最后一个命令状态结果</p> <p style="text-indent: 2em"> 自定义退出状态码:return [0-255]</p> <p style="text-indent: 2em"> 注意:函数体运行时,一旦遇到return语句,函数即返回;</p> <p style="text-indent: 2em">函数可以接受参数:</p> <p style="text-indent: 2em">在函数体中调用函数参数的方式同脚本中调用脚本参数的方式:位置参数</p> </p> <p style="text-indent: 2em">$1, $2, …</p> <p style="text-indent: 2em">$#, $*, $@</p> <p style="text-indent: 2em">例:写一个脚本,完成如下功能(使用函数):</p> <p style="text-indent: 2em">1、提示用户输入一个可执行命令;</p> <p style="text-indent: 2em">2、获取这个命令所依赖的所有库文件(使用ldd命令);</p> <p style="text-indent: 2em">3、复制命令至/mnt/sysroot/对应的目录中</p> <p style="text-indent: 2em">解释:假设,如果复制的是cat命令,其可执行程序的路径是/bin/cat,那么就要将/bin/cat复到/mnt/sysroot/bin/目录中,如果复制的是useradd命令,而useradd的可执行文件路径为/usr/sbin/useradd,那么就要将其复制到/mnt/sysroot/usr/sbin/目录中;</p> <p style="text-indent: 2em">4、复制各库文件至/mnt/sysroot/对应的目录中;</p> <pre class="brush:bash;toolbar:false">#!/bin/bash # target=/mnt/sysroot/ [ -d $target ] || mkdir $target preCommand() { if which $1 &> /dev/null; then commandPath=`which --skip-alias $1` return 0 else echo "No such command." return 1 fi } commandCopy() { commandDir=`dirname $1` [ -d ${target}${commandDir} ] || mkdir -p ${target}${commandDir} [ -f ${target}${commandPath} ] || cp $1 ${target}${commandDir} } libCopy() { for lib in `ldd $1 | egrep -o "/[^[:space:]]+"`; do libDir=`dirname $lib` [ -d ${target}${libDir} ] || mkdir -p ${target}${libDir} [ -f ${target}${lib} ] || cp $lib ${target}${libDir} done } read -p "Plz enter a command: " command until [ "$command" == 'quit' ]; do if preCommand $command &> /dev/null; then commandCopy $commandPath libCopy $commandPath fi read -p "Plz enter a command: " command done</pre> <p style="text-indent: 2em"><strong>bash编程之信号捕捉</strong><br style="text-indent: 2em" /></p> </p> <p style="text-indent: 2em">trap命令用于在shell程序中捕捉到信号,之后可以有三种反应方式:</p> <p style="text-indent: 2em">(1)执行一段程序来处理这一信号</p> <p style="text-indent: 2em">(2)接受信号的默认操作</p> <p style="text-indent: 2em">(3)忽视这一信号</p> <p style="text-indent: 2em">trap对上面三种方式提供了三种基本形式:</p> <p style="text-indent: 2em">第一种形式的trap命令在shell接收到signal list清单中数值相同的信号时,将执行双引号中的命令串。</p> <p style="text-indent: 2em">trap 'commands' signal-list</p> <p style="text-indent: 2em">trap "commands" signal-list</p> <p style="text-indent: 2em">第二种形式的trap命令恢复信号的默认操作:trap signal-list</p> <p style="text-indent: 2em">第三种形式的trap命令允许忽视信号:trap " " signal-list</p> <p style="text-indent: 2em">trap 'COMMAND' SIGINT(表示关闭进程)</p> <p style="text-indent: 2em">例:写一个脚本,能够ping探测指定网络内的所有主机是否在线,当没有执行完时可接收ctrl+c命令退出。</p> <pre class="brush:bash;toolbar:false">#!/bin/bash quitScript() { echo "Quit..." } trap 'quitScript; exit 5' SIGINT cnetPing() { for i in {1..254}; do if ping -c 1 -W 1 $1.$i &> /dev/null; then echo "$1.$i is up." else echo "$1.$i is down." fi done } bnetPing() { for j in {0..255}; do cnetPing $1.$j done } anetPing() { for m in {0..255}; do bnetPing $1.$m done } netType=`echo $1 | cut -d"." -f1` if [ $netType -ge 1 -a $netType -le 126 ]; then anetPing $netType elif [ $netType -ge 128 -a $netType -le 191 ]; then bnetPing $(echo $1 | cut -d'.' -f1,2) elif [ $netType -ge 192 -a $netType -le 223 ]; then cnetPing $(echo $1 | cut -d'.' -f1-3) else echo "Wrong" exit 2 fi</pre> <p style="text-indent: 2em"><strong>bash编程之数组</strong><br style="text-indent: 2em" /></p> <p style="text-indent: 2em">数组:连续的多个独立内存空间,每个内存空间相当于一个变量<br style="text-indent: 2em" /></p> <p style="text-indent: 2em">数组元素:数组名+索引(从0开始编号)</p> </p> <p style="text-indent: 2em">索引的表示方式:a[0], a[1]</p> <p style="text-indent: 2em">声明数组:declare -a ARRAR_NAME<br style="text-indent: 2em" /></p> <p style="text-indent: 2em">关联数组:declare -A ARRAY_NAME</p> <p style="text-indent: 2em">支持稀疏格式:仅一维数组 </p> </p> <p style="text-indent: 2em">数组元素的赋值:</p> <p style="text-indent: 2em">(1) 一次只赋值一个元素</p> <p style="text-indent: 2em">a[0]=$RANDOM</p> <p style="text-indent: 2em">…</p> <p style="text-indent: 2em">(2) 一次赋值全部元素</p> <p style="text-indent: 2em">a=(red blue yellow green)</p> <p style="text-indent: 2em">(3) 指定索引进行赋值</p> <p style="text-indent: 2em">a=([0]=green [3]=red [2]=blue [6]=yellow)</p> <p style="text-indent: 2em">(4) 用户输入</p> <p style="text-indent: 2em">read -a ARRAY</p> <p style="text-indent: 2em">数组的访问:<br style="text-indent: 2em" /></p> <p style="text-indent: 2em">用索引访问:ARRAY[index]<br style="text-indent: 2em" /></p> </p> <p style="text-indent: 2em">数组的长度:</p> <p style="text-indent: 2em">${#ARRAY[*]}</p> <p style="text-indent: 2em">${#ARRAY[@]}</p> <p style="text-indent: 2em">例:写一个脚本,生成10个随机数,保存至数组中;而后显示数组下标为偶数的元素</p> <pre class="brush:bash;toolbar:false">#!/bin/bash for i in {0..9}; do rand[$i]=$RANDOM [ $[$i%2] -eq 0 ] && echo "$i:${rand[$i]}" done</pre> </p> <p style="text-indent: 2em">从数组中挑选某元素:</p> <p style="text-indent: 2em">${ARRAY[@]:offset:number}</p> <p style="text-indent: 2em">切片:</p> <p style="text-indent: 2em">offset: 偏移的元素个数</p> <p style="text-indent: 2em">number: 取出的元素的个数</p> <p style="text-indent: 2em">${ARRAY[@]:offset}:取出偏移量后的所有元素</p> <p style="text-indent: 2em">${ARRAY[@]}: 取出所有元素<br style="text-indent: 2em" /></p> </p> <p style="text-indent: 2em">数组复制:</p> <p style="text-indent: 2em">要使用${ARRAY[@]}</p> <p style="text-indent: 2em">$@: 每个参数是一个独立的串<br style="text-indent: 2em" /></p> <p style="text-indent: 2em">$*: 所有参数是一个串</p> </p> <p style="text-indent: 2em">向数组中追加元素:非稀疏格式</p> <p style="text-indent: 2em">week, </p> <p style="text-indent: 2em">week[${#week[@]}]</p> </p> <p style="text-indent: 2em">从数组中删除元素:</p> <p style="text-indent: 2em">unset ARRAY[index]<br style="text-indent: 2em" /></p> <p style="text-indent: 2em">例:复制一个数组中下标为偶数的元素至一个新数组中<br style="text-indent: 2em" /></p> <pre class="brush:bash;toolbar:false">#!/bin/bash declare -a mylogs logs=(/var/log/*.log) echo ${logs[@]} for i in `seq 0 ${#logs[@]}`; do if [ $[$i%2] -eq 0 ];then index=${#mylogs[@]} mylogs[$index]=${logs[$i]} fi done echo ${mylogs[@]}</pre> <p style="text-indent: 2em">例:生成10个随机数,升序排序</p> <pre class="brush:bash;toolbar:false">#!/bin/bash for((i=0;i<10;i++)) do rnd[$i]=$RANDOM done echo -e "total=${#rnd[@]}\n${rnd[@]}\nBegin to sort" for((i=9;i>=1;i--)) do for((j=0;j<i;j++)) do if [ ${rnd[$j]} -gt ${rnd[$[$j+1]]} ] ;then swapValue=${rnd[$j]} rnd[$j]=${rnd[$[$j+1]]} rnd[$[$j+1]]=$swapValue fi done done echo ${rnd[@]}</pre> <p style="text-indent: 2em">例:打印九九乘法表</p> <pre class="brush:bash;toolbar:false">#!/bin/bash for((i=1;i<=9;i++)) do strLine="" for((j=1;i<=9;j++)) do strLine=$strLine"$i*$j="$[$i*$j]"\t" [ $i -eq $j ] && echo -e $strLine && break done done</pre> <p style="text-indent: 2em"><span style="font-size: 18px"><strong>bash编程之字符串操作</strong></span></p> </p> <p style="text-indent: 2em">字符串切片:</p> <p style="text-indent: 2em">${string:offset:length}</p> <pre class="brush:bash;toolbar:false">[root@scholar scripts]# string='hello word' [root@scholar scripts]# echo ${string:2:4} llo</pre> <p style="text-indent: 2em">取尾部的指定个数的字符:<br style="text-indent: 2em" /></p> <p style="text-indent: 2em">${string: -length}</p> <pre class="brush:bash;toolbar:false">[root@scholar scripts]# echo ${string: -2} rd</pre> <p style="text-indent: 2em">取子串:基于模式</p> </p> <p style="text-indent: 2em">${variable#*word}:在variable中存储字串上,自左而右,查找第一次出现word,删除字符开始至此word处的所有内容;</p> <p style="text-indent: 2em">${variable##*word}:在variable中存储字串上,自左而右,查找最后一次出现word,删除字符开始至此word处的所有内容;</p> </p> <pre class="brush:bash;toolbar:false">file='/var/log/messages' ${file#*/}: 返回的结果是var/log/messages ${file##*/}: 返回messages</pre> </p> <p style="text-indent: 2em">${variable%word*}: 在variable中存储字串上,自右而左,查找第一次出现word,删除此word处至字串尾部的所有内容;</p> <p style="text-indent: 2em">${variable%%world*}:在variable中存储字串上,自右而左,查找最后一次出现word,删除此word处至字串尾部的所有内容;</p> <pre class="brush:bash;toolbar:false">file='/var/log/messages' ${file%*/}: 返回的结果是/var/log ${file%%*/}: 返回结果为空</pre> <p style="text-indent: 2em">例:url="http://www.redhat.com:80"</p> <pre class="brush:bash;toolbar:false">取端口:${url##*:} 取协议:${url%%:*}</pre> <p style="text-indent: 2em">查找替换:<br style="text-indent: 2em" /></p> <p style="text-indent: 2em">${variable/pattern/substi}: 替换第一次出现</p> <pre class="brush:bash;toolbar:false">#userinfo=`tail -1 /etc/passwd #echo $userinfo scholar:x:500:500:scholar:/home/scholar:/bin/bash #echo ${userinfo/scholar/redhat} redhat:x:500:500:scholar:/home/scholar:/bin/bash</pre> <p style="text-indent: 2em">${variable//pattern/substi}:替换所有的出现</p> </p> <pre class="brush:bash;toolbar:false">#echo ${userinfo//scholar/redhat} redhat:x:500:500:redhat:/home/redhat:/bin/bash</pre> <p style="text-indent: 2em">${variable/#pattern/substi}:替换行首被pattern匹配到的内容</p> <pre class="brush:bash;toolbar:false">#echo ${userinfo/#scholar/redhat} redhat:x:500:500:scholar:/home/scholar:/bin/bash</pre> <p style="text-indent: 2em">${variable/%pattern/substi}:替换行尾被pattern匹配到的内容<br style="text-indent: 2em" /></p> </p> <pre class="brush:bash;toolbar:false">#echo ${userinfo/%bash/redhat} scholar:x:500:500:scholar:/home/scholar:/bin/redhat</pre> <p style="text-indent: 2em">pattern可以使用globbing中的元字符:* ?</p> <p style="text-indent: 2em">查找删除:</p> </p> <p style="text-indent: 2em">${variable/pattern}:删除第一次出现</p> </p> <pre class="brush:bash;toolbar:false">#echo ${userinfo/scholar} :x:500:500:scholar:/home/scholar:/bin/bash</pre> <p style="text-indent: 2em">${variable//pattern}:删除所有的出现</p> </p> <pre class="brush:bash;toolbar:false">#echo ${userinfo//scholar} :x:500:500::/home/:/bin/bash</pre> <p style="text-indent: 2em">${variable/#pattern}:删除行首被pattern匹配到的内容</p> </p> <pre class="brush:bash;toolbar:false">#echo ${userinfo/#scholar} :x:500:500:scholar:/home/scholar:/bin/bash</pre> <p style="text-indent: 2em">${variable/%pattern}:删除行尾被pattern匹配到的内容</p> </p> <pre class="brush:bash;toolbar:false">#echo ${userinfo/%bash} scholar:x:500:500:scholar:/home/scholar:/bin/</pre> <p style="text-indent: 2em">大小写转换:</p> <p style="text-indent: 2em">小–>大:${variable^^}</p> </p> <pre class="brush:bash;toolbar:false">#echo ${userinfo^^} SCHOLAR:X:500:500:SCHOLAR:/HOME/SCHOLAR:/BIN/BASH</pre> <p style="text-indent: 2em">大–>小:${variable,,}</p> <pre class="brush:bash;toolbar:false">#name="SCHOLAR" #echo ${name,,} scholar</pre> <p style="text-indent: 2em">变量赋值操作:<br style="text-indent: 2em" /></p> <p style="text-indent: 2em">${variable:-string}:variable为空或未设定,那么返回string,否则,返回variable变量的值;</p> <p style="text-indent: 2em">${variable:=string}:variable为空或未设定,则返回string,且将string赋值给变量variable,否则,返回variable的值;</p> <p style="text-indent: 2em">为脚本使用配置文件,并确保某变量有可用值的方式</p> <p style="text-indent: 2em">variable=${variable:-default vaule}</p> </p> <p style="text-indent: 2em">写个脚本,配置etc目录;</p> <p style="text-indent: 2em">(1) 在配置文件中定义变量;</p> <p style="text-indent: 2em">(2) 在脚本中source配置文件;</p> <pre class="brush:bash;toolbar:false">#!/bin/bash [ -f /etc/sysconfig/network ] && source /etc/network/network [-z "$HOSTAME" -o "$HOSTNAME" = '(none)' ] || HOSTNAME ='localhost' /bin/hostname $HOSTNAME /bin/hostname</pre> <p style="text-indent: 2em"><strong>bash编程之补充</strong><br style="text-indent: 2em" /></p> <p style="text-indent: 2em">mktemp命令:<br style="text-indent: 2em" /></p> <p style="text-indent: 2em">mktemp [OPTIONS] filename.XXX</p> </p> <pre class="brush:bash;toolbar:false">-d: 创建临时目录 --tmpdir=/path/to/somewhere :指定临时文件所在的目录</pre> <pre class="brush:bash;toolbar:false">mktemp /tmp/tmp.XXX #XXX生成相同数量随机字符 mktemp --tmpdir=/var/tmp tmp.XXX #指定目录创建临时文件 mktemp --tmpdir=/var/tmp -d tmp.XXX #指定目录创建临时目录</pre> <p style="text-indent: 2em">install命令:</p> </p> <p style="text-indent: 2em">install [OPTIONS] SOURCE DEST</p> <p style="text-indent: 2em">install [OPTIONS] SOURCE… DIR</p> <p style="text-indent: 2em">install [OPTIONS] -d DIR …</p> <p style="text-indent: 2em">增强型的复制命令:</p> <pre class="brush:bash;toolbar:false">-o OWNER -g GROUP -m MODE</pre> <pre class="brush:bash;toolbar:false">-d : 创建目录</pre> <pre class="brush:bash;toolbar:false">install /etc/fstab /tmp #复制文件到指定目录 install --mode=644 /etc/fstab /tmp/ #复制时指定权限 install --owner=scholar /etc/fstab /tmp #复制时指定属主 install --group=scholar /etc/fstab /tmp #复制时指定属组 install -d /tmp/install #创建目录</pre> <p style="text-indent: 2em"><strong>The end</strong></p> <p style="text-indent: 2em">至此,bash编程的所有基础语法知识点就总结完毕了,掌握了以上知识点,bash编程算是略有小成了吧,如果有兴趣可继续钻研高级语法,目前能力不足就不介绍啦。这里给大家推荐两本bash编程的书籍,能力相对较弱的可以看一下《Linux命令行和shell编程宝典》,有能力的就看《abs-guide》吧。看在码字辛苦的份上,多多点赞吧。</p> <p style="text-indent: 2em">以上仅为个人学习总结,如有错漏,大神勿喷~~~</p> <p></p> 最后修改:2021 年 12 月 10 日 10 : 53 AM © 允许规范转载 赞赏 如果觉得我的文章对你有用,请随意赞赏 赞赏作者 支付宝微信