当前位置: 首页 > news >正文

shell流程控制

shell流程控制

if

if commands; thencommands
[elif commands; thencommands...]
[elsecommands]
fi

commands可以是test表达式[ expression ]

[expression]表达式(不推荐)

测试文件表达式

表达式 如果下列条件为真则返回True
file1 -ef file2 file1 和 file2 拥有相同的索引号(通过硬链接两个文件名指向相同的文件)。
file1 -nt file2 file1新于 file2。
file1 -ot file2 file1早于 file2。
-b file file 存在并且是一个块(设备)文件。
-c file file 存在并且是一个字符(设备)文件。
-d file file 存在并且是一个目录。
-e file file 存在。
-f file file 存在并且是一个普通文件。
-g file file 存在并且设置了组 ID。
-G file file 存在并且由有效组 ID 拥有。
-k file file 存在并且设置了它的“sticky bit”。
-L file file 存在并且是一个符号链接。
-O file file 存在并且由有效用户 ID 拥有。
-p file file 存在并且是一个命名管道。
-r file file 存在并且可读(有效用户有可读权限)。
-s file file 存在且其长度大于零。
-S file file 存在且是一个网络 socket。
-t fd fd 是一个定向到终端/从终端定向的文件描述符 。 这可以被用来决定是否重定向了标准输入/输出错误。
-u file file 存在并且设置了 setuid 位。
-w file file 存在并且可写(有效用户拥有可写权限)。
-x file file 存在并且可执行(有效用户有执行/搜索权限)
#!/bin/bash
# test-file: Evaluate the status of a file
FILE=~/.bashrc
if [ -e "$FILE" ]; thenif [ -f "$FILE" ]; thenecho "$FILE is a regular file."fiif [ -d "$FILE" ]; thenecho "$FILE is a directory."fiif [ -r "$FILE" ]; thenecho "$FILE is readable."fiif [ -w "$FILE" ]; thenecho "$FILE is writable."fiif [ -x "$FILE" ]; thenecho "$FILE is executable/searchable."fi
elseecho "$FILE does not exist"exit 1
fi
exit

exit与return

<font style="color:#3C3C3C;">exit </font>命令接受一个单独的,可选的参数,其成为脚本的退出状态。当不传递参数时,退出状态默认为零。 以这种方式使用 exit 命令,则允许此脚本提示失败如果 $FILE 展开成一个不存在的文件名。这个 exit 命令出现在脚本中的最后一行,是一个当一个脚本“运行到最后”(到达文件末尾),不管怎样, 默认情况下它以退出状态零终止。

类似地,通过带有一个整数参数的 <font style="color:#3C3C3C;">return</font> 命令,shell 函数可以返回一个退出状态。如果我们打算把 上面的脚本转变为一个 shell 函数,为了在更大的程序中包含此函数,用 return 语句来代替 exit 命令

test_file () {# test-file: Evaluate the status of a fileFILE=~/.bashrcif [ -e "$FILE" ]; thenif [ -f "$FILE" ]; thenecho "$FILE is a regular file."fiif [ -d "$FILE" ]; thenecho "$FILE is a directory."fiif [ -r "$FILE" ]; thenecho "$FILE is readable."fiif [ -w "$FILE" ]; thenecho "$FILE is writable."fiif [ -x "$FILE" ]; thenecho "$FILE is executable/searchable."fielseecho "$FILE does not exist"return 1fi
}

测试字符串表达式

表达式 如果下列条件为真则返回True
string string 不为 null。
-n string 字符串 string 的长度大于零。
-z string 字符串 string 的长度为零。
string1 = string2
string1 == string2
string1 和 string2 相同。 单或双等号都可以,不过双等号更受欢迎。
string1 != string2 string1 和 string2 不相同。
string1 > string2 sting1 排列在 string2 之后。
string1 < string2 string1 排列在 string2 之前。

<font style="color:#3C3C3C;">></font><font style="color:#3C3C3C;"><</font>表达式操作符必须用引号引起来(或者是用反斜杠转义)。如果不这样,它们会被 shell 解释为重定向操作符,造成潜在的破坏结果。

#!/bin/bash
# test-string: evaluate the value of a string
ANSWER=maybe
if [ -z "$ANSWER" ]; thenecho "There is no answer." >&2exit 1
fi
if [ "$ANSWER" = "yes" ]; thenecho "The answer is YES."
elif [ "$ANSWER" = "no" ]; thenecho "The answer is NO."
elif [ "$ANSWER" = "maybe" ]; thenecho "The answer is MAYBE."
elseecho "The answer is UNKNOWN."
fi

这个脚本中,我们计算常量 ANSWER。我们首先确定是否此字符串为空。如果为空,我们就终止 脚本,并把退出状态设为1。注意这个应用于 echo 命令的重定向操作。其把错误信息 “There is no answer.” 重定向到标准错误,这是处理错误信息的“正确”方法。如果字符串不为空,我们就计算 字符串的值,看看它是否等于“yes,” “no,” 或者“maybe”。为此使用了 elif,它是 “else if” 的简写。 

测试整数表达式

表达式 如果为真...
integer1 -eq integer2 integer1 等于 integer2。
integer1 -ne integer2 integer1 不等于 integer2。
integer1 -le integer2 integer1 小于或等于 integer2。
integer1 -lt integer2 integer1 小于 integer2。
integer1 -ge integer2 integer1 大于或等于 integer2。
integer1 -gt integer2 integer1 大于 integer2。

[[]] - test 命令替代物(推荐)

目前的 bash 版本包括一个复合命令,作为加强的 test 命令替代物。它使用以下语法:

[[ expression ]]

增加了一个重要的新的字符串表达式,其返回值为真,如果 string1匹配扩展的正则表达式 regex。这就为执行比如数据验证等任务提供了许多可能性。 

string1 =~ regex

通过应用正则表达式,我们能够限制 INT 的值只是字符串,其开始于一个可选的减号,随后是一个或多个数字。 这个表达式也消除了空值的可能性。

#!/bin/bash
# test-integer2: evaluate the value of an integer.
INT=-5
if [[ "$INT" =~ ^-?[0-9]+$ ]]; thenif [ $INT -eq 0 ]; thenecho "INT is zero."elseif [ $INT -lt 0 ]; thenecho "INT is negative."elseecho "INT is positive."fiif [ $((INT % 2)) -eq 0 ]; thenecho "INT is even."elseecho "INT is odd."fifi
elseecho "INT is not an integer." >&2exit 1
fi

[[ ]]添加的另一个功能是==操作符支持类型匹配,正如路径名展开所做的那样

这就使[[ ]]有助于计算文件和路径名。

if [[ $FILE == foo.* ]]; then
> echo "$FILE matches pattern 'foo.*'"
> fi

(( )) - 为整数设计(推荐)

除了 [[ ]] 复合命令之外,bash 也提供了 (( )) 复合命令,其有利于操作整数。它支持一套 完整的算术计算,

(( ))被用来执行算术真测试。如果算术计算的结果是非零值,则其测试值为真。

$ if ((1)); then echo "It is true."; fi
It is true.
$ if ((0)); then echo "It is true."; fi
#!/bin/bash
# test-integer2a: evaluate the value of an integer.
INT=-5
if [[ "$INT" =~ ^-?[0-9]+$ ]]; thenif ((INT == 0)); thenecho "INT is zero."elseif ((INT < 0)); thenecho "INT is negative."elseecho "INT is positive."fiif (( ((INT % 2)) == 0)); thenecho "INT is even."elseecho "INT is odd."fifi
elseecho "INT is not an integer." >&2exit 1
fi

注意我们使用小于和大于符号,以及==用来测试是否相等。这是使用整数较为自然的语法了。也要 注意,因为复合命令 (( )) 是 shell 语法的一部分,而不是一个普通的命令,而且它只处理整数, 所以它能够通过名字识别出变量,而不需要执行展开操作。

表达式的逻辑运算

也有可能把表达式结合起来创建更复杂的计算。通过使用逻辑操作符来结合表达式。有三个用于 test 和 [[ ]] 的逻辑操作。 它们是 AND、OR 和 NOT。test 和 [[ ]] 使用不同的操作符来表示这些操作:

逻辑操作符

操作符 测试 [[ ]] and (( ))
AND -a &&
OR -o
NOT ! !

下面的脚本决定了一个整数是否属于某个范围内的值:

#!/bin/bash
# test-integer3: determine if an integer is within a
# specified range of values.
MIN_VAL=1
MAX_VAL=100
INT=50
if [[ "$INT" =~ ^-?[0-9]+$ ]]; thenif [[ INT -ge MIN_VAL && INT -le MAX_VAL ]]; thenecho "$INT is within $MIN_VAL to $MAX_VAL."elseecho "$INT is out of range."fi
elseecho "INT is not an integer." >&2exit 1
fi

我们也可以对表达式使用圆括号,为的是分组。如果不使用括号,那么否定只应用于第一个 表达式,而不是两个组合的表达式。用 test 可以这样来编码:

if [ ! \( $INT -ge $MIN_VAL -a $INT -le $MAX_VAL \) ]; thenecho "$INT is outside $MIN_VAL to $MAX_VAL."
elseecho "$INT is in range."
fi

因为 test 使用的所有的表达式和操作符都被 shell 看作是命令参数(不像 [[ ]] 和 (( )) ), 对于 bash 有特殊含义的字符,比如说 <,>,(,和 ),必须引起来或者是转义。

知道了 test 和 [[ ]] 基本上完成相同的事情,哪一个更好呢?test 更传统(是 POSIX 的一部分), 然而 [[ ]] 特定于 bash。知道怎样使用 test 很重要,因为它被非常广泛地应用,但是显然 [[ ]] 更 有用,并更易于编码。

通过&&||实现条件判断

command1 && command2
command1 || command2
$ mkdir temp && cd temp
# 这会创建一个名为 temp 的目录,并且若它执行成功后,当前目录会更改为 temp。第二个命令会尝试 执行只有当 mkdir 命令执行成功之后。
$ [ -d temp ] || mkdir temp
# 会测试目录 temp 是否存在,并且只有测试失败之后,才会创建这个目录。这种构造类型非常有助于在 脚本中处理错误
[ -d temp ] || exit 1
# 如果这个脚本要求目录 temp,且目录不存在,然后脚本会终止,并返回退出状态1。

while/until循环

while

while commands; do commands; done
#!/bin/bash
count=1
while [ $count -le 5 ]; doecho $countcount=$((count + 1))
done
echo "Finished."

只要退出状态为零,它就执行循环内的命令。 在上面的脚本中,创建了变量 count ,并初始化为1。 while 命令将会计算 test 命令的退出状态。 只要 test 命令返回退出状态零,循环内的所有命令就会执行。每次循环结束之后,会重复执行 test 命令。 第六次循环之后, count 的数值增加到6, test 命令不再返回退出状态零,且循环终止。

#!/bin/bash
DELAY=3 # Number of seconds to display results
# while [[ $REPLY != 0 ]]; do  使用了break这个就没有必要了
while true; doclearcat <<- _EOF_Please Select:1. Display System Information2. Display Disk Space3. Display Home Space Utilization0. Quit_EOF_read -p "Enter selection [0-3] > "if [[ $REPLY =~ ^[0-3]$ ]]; thenif [[ $REPLY == 1 ]]; thenecho "Hostname: $HOSTNAME"uptimesleep $DELAYcontinuefiif [[ $REPLY == 2 ]]; thendf -hsleep $DELAYcontinuefiif [[ $REPLY == 3 ]]; thenif [[ $(id -u) -eq 0 ]]; thenecho "Home Space Utilization (All Users)"du -sh /home/*elseecho "Home Space Utilization ($USER)"du -sh $HOMEfisleep $DELAYcontinuefiif [[ $REPLY == 0 ]]; thenbreakfielseecho "Invalid entry."sleep $DELAYfi
done
echo "Program terminated."

until

until 命令与 while 非常相似,除了当遇到一个非零退出状态的时候, while 退出循环, 而 until 不退出。一个 until 循环会继续执行直到它接受了一个退出状态零。

#!/bin/bash
# until-count: display a series of numbers
count=1
until [ $count -gt 5 ]; doecho $countcount=$((count + 1))
done
echo "Finished."

使用循环读取文件

#!/bin/bash
# while-read: read lines from a file
while read distro version release; doprintf "Distro: %s\tVersion: %s\tReleased: %s\n" \$distro \$version \$release
done < distros.txt

为了重定向文件到循环中,我们把重定向操作符放置到 done 语句之后。循环将使用 read 从重定向文件中读取 字段。这个 read 命令读取每个文本行之后,将会退出,其退出状态为零,直到到达文件末尾。到时候,它的退出状态为非零数值,因此终止循环。也有可能把标准输入管道到循环中。

#!/bin/bash
# while-read2: read lines from a file
sort -k 1,1 -k 2n distros.txt | while read distro version release; doprintf "Distro: %s\tVersion: %s\tReleased: %s\n" \$distro \$version \$release
done

这里我们接受 sort 命令的标准输出,然后显示文本流。然而,因为管道将会在子 shell 中执行 循环,当循环终止的时候,循环中创建的任意变量或赋值的变量都会消失,

case

case word in[pattern [| pattern]...) commands ;;]...
esac

case 语句使用的模式pattern 和路径展开中使用的那些是一样的。模式以一个 “)” 为终止符

#!/bin/bash
clear
echo "
Please Select:
1. Display System Information
2. Display Disk Space
3. Display Home Space Utilization
0. Quit
"
read -p "Enter selection [0-3] > "
case $REPLY in0)  echo "Program terminated."exit;;1)  echo "Hostname: $HOSTNAME"uptime;;2)  df -h;;3)  if [[ $(id -u) -eq 0 ]]; thenecho "Home Space Utilization (All Users)"du -sh /home/*elseecho "Home Space Utilization ($USER)"du -sh $HOMEfi;;*)  echo "Invalid entry" >&2exit 1;;
esac

case 模式

模式 描述
a) 若单词为 “a”,则匹配
[[:alpha:]]) 若单词是一个字母字符,则匹配
???) 若单词只有3个字符,则匹配
*.txt) 若单词以 “.txt” 字符结尾,则匹配
*) 匹配任意单词。把这个模式做为 case 命令的最后一个模式,是一个很好的做法, 可以捕捉到任意一个与先前模式不匹配的数值;也就是说,捕捉到任何可能的无效值。
#!/bin/bash
read -p "enter word > "
case $REPLY in[[:alpha:]])        echo "is a single alphabetic character." ;;[ABC][0-9])         echo "is A, B, or C followed by a digit." ;;???)                echo "is three characters long." ;;*.txt)              echo "is a word ending in '.txt'" ;;*)                  echo "is something else." ;;
esac

还可以使用竖线字符作为分隔符,把多个模式结合起来。这就创建了一个 “或” 条件模式。

#!/bin/bash
# case-menu: a menu driven system information program
clear
echo "
Please Select:
A. Display System Information
B. Display Disk Space
C. Display Home Space Utilization
Q. Quit
"
read -p "Enter selection [A, B, C or Q] > "
case $REPLY in
q|Q) echo "Program terminated."exit;;
a|A) echo "Hostname: $HOSTNAME"uptime;;
b|B) df -h;;
c|C) if [[ $(id -u) -eq 0 ]]; thenecho "Home Space Utilization (All Users)"du -sh /home/*elseecho "Home Space Utilization ($USER)"du -sh $HOMEfi;;
*)   echo "Invalid entry" >&2exit 1;;
esac

添加的 “;;&” 的语法允许 case 语句继续执行下一条测试,而不是简单地终止运行。

#!/bin/bash
# case4-2: test a character
read -n 1 -p "Type a character > "
echo
case $REPLY in[[:upper:]])    echo "'$REPLY' is upper case." ;;&[[:lower:]])    echo "'$REPLY' is lower case." ;;&[[:alpha:]])    echo "'$REPLY' is alphabetic." ;;&[[:digit:]])    echo "'$REPLY' is a digit." ;;&[[:graph:]])    echo "'$REPLY' is a visible character." ;;&[[:punct:]])    echo "'$REPLY' is a punctuation symbol." ;;&[[:space:]])    echo "'$REPLY' is a whitespace character." ;;&[[:xdigit:]])   echo "'$REPLY' is a hexadecimal digit." ;;&
esac

for

for: 传统 shell 格式

for variable [in words]; docommands
done

variable这个变量在循环执行期间会增加,words 是一个可选的条目列表其值会按顺序赋值给 variable,

花括号展开:

$ for i in {A..D}; do echo $i; done
A
B
C
D

路径名展开:

$ for i in distros*.txt; do echo $i; done
distros-by-date.txt
distros-dates.txt
distros-key-names.txt
distros-key-vernums.txt
distros-names.txt
distros.txt
distros-vernums.txt
distros-versions.txt

命令替换:

#!/bin/bash
while [[ -n $1 ]]; doif [[ -r $1 ]]; thenmax_word=max_len=0for i in $(strings $1); dolen=$(echo $i | wc -c)if (( len > max_len )); thenmax_len=$lenmax_word=$ifidoneecho "$1: '$max_word' ($max_len characters)"fishift
done

在这个示例中,我们要在一个文件中查找最长的字符串。当在命令行中给出一个或多个文件名的时候, 该程序会使用 strings 程序(其包含在 GNU binutils 包中),为每一个文件产生一个可读的文本格式的 “words” 列表。 然后这个 for 循环依次处理每个单词,判断当前这个单词是否为目前为止找到的最长的一个。当循环结束的时候,显示出最长的单词。

如果省略掉 for 命令的可选项 words 部分,for 命令会默认处理位置参数()。

#!/bin/bash
for i; doif [[ -r $i ]]; thenmax_word=max_len=0for j in $(strings $i); dolen=$(echo $j | wc -c)if (( len > max_len )); thenmax_len=$lenmax_word=$jfidoneecho "$i: '$max_word' ($max_len characters)"fi
done

正如我们所看到的,我们已经更改了最外围的循环,用 for 循环来代替 while 循环。通过省略 for 命令的 words 列表, 用位置参数替而代之。在循环体内,之前的变量 i 已经改为变量 j。同时 shift 命令也被淘汰掉了。

for: C 语言格式

最新版本的 bash 已经添加了第二种格式的 for 命令语法,该语法相似于 C 语言中的 for 语法格式。 其它许多编程语言也支持这种格式:

for (( expression1; expression2; expression3 )); docommands
done

这里的 expression1、expression2和 expression3 都是算术表达式,

在行为方面,这相当于以下构造形式:

(( expression1 ))
while (( expression2 )); docommands(( expression3 ))
done

expression1 用来初始化循环条件,expression2 用来决定循环结束的时间,还有在每次循环迭代的末尾会执行 expression3。

#!/bin/bash
for (( i=0; i<5; i=i+1 )); doecho $i
done

实例

report_home_space () {if [[ $(id -u) -eq 0 ]]; thencat <<- _EOF_<H2>Home Space Utilization (All Users)</H2><PRE>$(du -sh /home/*)</PRE>_EOF_elsecat <<- _EOF_<H2>Home Space Utilization ($USER)</H2><PRE>$(du -sh $HOME)</PRE>_EOF_fireturn
}

下一步,我们将重写它,以便提供每个用户家目录的更详尽信息,并且包含用户家目录中文件和目录的总个数:

report_home_space () {local format="%8s%10s%10s\n"local i dir_list total_files total_dirs total_size user_nameif [[ $(id -u) -eq 0 ]]; thendir_list=/home/*user_name="All Users"elsedir_list=$HOMEuser_name=$USERfiecho "<H2>Home Space Utilization ($user_name)</H2>"for i in $dir_list; dototal_files=$(find $i -type f | wc -l)total_dirs=$(find $i -type d | wc -l)total_size=$(du -sh $i | cut -f 1)echo "<H3>$i</H3>"echo "<PRE>"printf "$format" "Dirs" "Files" "Size"printf "$format" "----" "-----" "----"printf "$format" $total_dirs $total_files $total_sizeecho "</PRE>"donereturn
}

在 if 语句块内设置了一些随后会在 for 循环中用到的变量,来取代在 if 语句块内执行完备的动作集合。我们给 函数添加了几个本地变量,并且使用 printf 来格式化输出。

http://www.hskmm.com/?act=detail&tid=29552

相关文章:

  • shell展开shell数组
  • shell排错
  • 原木
  • 格式化输出与文本处理
  • 2025年10月镀锌卷板厂家最新推荐排行榜,有花镀锌卷板,无花镀锌卷板,高锌层镀锌卷板,批发镀锌卷板公司推荐
  • React 19.2 重磅更新!这几个新特性终于来了
  • Akka.NET高性能分布式Actor框架完全指南
  • 基于Docker搭建MySQL Cluster
  • 2025 年抗氧剂厂家最新推荐排行榜,聚酯防黄变抗氧剂,透明膜防晶点抗氧剂,PC聚碳防黄变抗氧剂公司推荐!
  • PaddleX服务化部署精度低于命令行调用的原因及解决方案 - 指南
  • 某中心与华盛顿大学公布机器人研究奖项与学者名单
  • 会话跟踪方案
  • 阻塞、非阻塞、同步、异步的区别是什么?
  • 如何防范员工泄露数据给 AI?2025年选型与落地实战版
  • Linux文本编辑三剑客之grep
  • Linux文本编辑三剑客之sed
  • 做了项目经理才发现:上台发言,其实都有套路
  • 占位符
  • 什么是IO多路复用?
  • 进程、线程和协程之间的区别和联系
  • 挣点小钱的副业(附带新手教程)0元的快乐
  • Linux文本编辑三剑客之awk
  • 软考~高效的系统规划与管理师考试—知识篇—V2.0—第四章 IT 服务规划设计 — 2017 年 2018 年 2020 年 2022 年 2023 年
  • 10.12
  • 从“优化工具”到“价值生态”:多价值主体系统如何重塑AI价值对齐范式
  • 2.2 深度学习(Deep Learning)
  • 结对项目
  • 第十二篇
  • 2.1 函数逼近(Function Approximation)
  • Elasticsearch 备份:snapshot 镜像使用篇