前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Shell流程控制

Shell流程控制

原创
作者头像
入门笔记
修改2021-01-08 10:16:08
8840
修改2021-01-08 10:16:08
举报
文章被收录于专栏:入门小站入门小站

if 语句


代码语言:txt
复制
if test-commands; then
  consequent-commands;
[elif more-test-commands; then
  more-consequents;]
[else alternate-consequents;]
fi

test-commands 既可以是 test 测试或[]、[[]]测试,也可以是任何其它命令,test-commands 用于条件测试,它只判断命令的退出状态码是否为 0,为 0 则为 true。

例如:

代码语言:txt
复制
if [ "$a" ];then echo '$a' is not none;else echo '$a' ?developer/article/1770380/undefined or empty;fi

if [ ! -d ~/.ssh ];then
  mkdir ~/.ssh
  chown -R $USER.$USER ~/.ssh
  chmod 700 ~/.ssh
fi

if grep 'rumenz' /etc/passwd &>/dev/null;then
  echo 'User "rumenz" already exists...'
elif grep 'rumenz' /etc/passwd &>/dev/null;then
  echo 'User "rumenz" already exists...'
else
  echo 'you should create user,exit...'
  exit 1
fi

case


case 常用于确定的分支判断。比如:

代码语言:txt
复制
while [ "$1" ];do
  case "$1" in
    start)
      echo start;;
    stop)
      echo stop;;
    restart)
      echo restart;;
    reload | force-reload)
      echo reload;;
    status)
      echo status;;
    *)
      echo $"Usage: $0 {start|stop|status|restart|reload|force-reload}"
      exit 2
  esac
done

case 用法基本要求:

  • 每个小分句中的 pattern 部分都使用括号『()』包围,只不过左括号『(』不是必须的
  • 每个小分句的 pattern 支持通配模式匹配,可使用『|』分隔多个通配模式,表示满足其中一个模式即可
代码语言:txt
复制
*   例如`([yY]|[yY][eE][sS]])`表示即可以输入单个字母的 y 或 Y,还可以输入 yes 三个字母的任意大小写格式最后一般会定义一个能匹配其它任意条件的默认分支,即

for 循环


有两种 for 循环结构:

代码语言:txt
复制
# 成员测试类语法
for i [ [in [words …] ] ; ] do commands; done

# C语言for语法
for (( expr1;expr2;expr3 ));do cmd_list;done

成员测试类的 for 循环中,in 关键字后是默认使用空格分隔的一个或多个元素,for 循环时,每次从 in 关键字后面取一个元素并赋值给 i 变量。

例如:

代码语言:txt
复制
$ for i in 1 2 "3 4";do echo $i;done
1
2
3 4

如果省略 in words,则等价于in "$@",即迭代位置参数。例如:

代码语言:txt
复制
set -- a b c
for i do echo $i;done
for i;do echo $i;done

C 语言型的 for 语法中,expr1 是初始化语句,expr2 是循环终点条件判断语句,expr3 是每轮循环后执行的语句,一般用来更改条件判断相关的变量。

代码语言:txt
复制
for ((i=1;i<=3;++i));do echo $i;done
1
2
3

对于成员测试类的语法,两点需要注意:

  1. 命令行解析时,路径扩展的过程在单词分割过程之后
  2. 迭代的元素中包含了空白
代码语言:txt
复制
touch "aa aaa.txt"
touch "bb bbb.txt"
for i in *.txt;do	echo $i;done 
for i in $(ls *.txt);do echo $i;done
(IFS=$'\n';for i in $(ls *.txt);do echo $i;done)

现在记住结论,后面介绍命令行解析的时候再做解释。

while 循环


代码语言:txt
复制
while test_cmd_list;do cmd_list;done

while 循环,开始时会测试test_cmd_list,如果测试的退出状态码为 0,则执行一次循环体语句cmd_list,然后再测试test_cmd_list,一直循环,直到测试退出状态码非 0,循环退出。

例如:

代码语言:txt
复制
let i=1,sum=0;
while [ $i -le 10 ];do 
  let sum=sum+i
  let ++i
done

还有 until 循环语句,但在 Shell 中用的很少。

while 循环经常会和 read 命令一起使用,read 是 Bash 的内置命令,可用来读取文件,通常会按行读取:每次读一行。

例如:

代码语言:txt
复制
cat /etc/fstab | while read line;do
  let num+=1
  echo $num: $line
done

上面的命令行中,首先 cat 进程和 while 结构开始运行,while 结构中的 read 命令从标准输入中读取,也就是从管道中读取数据,每次读取一行,因为管道中最初没有数据,所以 read 命令被阻塞处于数据等待状态。当 cat 命令读完文件所有数据后,将数据放入到管道中,于是 read 命令从管道中每次读取一行并将所读行赋值给变量 line,然后执行循环体,然后继续循环,直到 read 读完所有数据,循环退出。

但注意,管道两边的命令默认是在子 Shell 中执行的,所以其设置的变量在命令执行完成后就消失。换句话说,在父 Shell 中无法访问这些变量。比如上面的 num 变量是在管道的 while 结构中设置的,除了在 while 中能访问该变量,其它任何地方都无法访问它。

如果想要访问 while 中赋值的变量,就不能使用管道。如果是直接从文件读取,可使用输入重定向,如果是读取命令产生的数据,可使用进程替换。

代码语言:txt
复制
while read line;do
  let num1+=1
  echo $num1: $line
done </etc/fstab
echo $num1

while read line;do
  let num2+=1
  echo $num2: $line
done < <(grep 'UUID' /etc/fstab)

select 选项选择


select 可提供选项给用户选择。

代码语言:txt
复制
select name [ in word ] ; do cmd_list ; done

in word部分就是展示给用户的各个选项,如果省略,则等价于in "$@"。当用户输入其所选择的项后,对应项的内容保存到 name 变量,用户输入的内容保存到 REPLY 变量中。

注:REPLY 变量一般是序号值,但用户可以不按常理出牌,随意输入,所以 REPLY 保存的不一定是序号。

另外,用户做出选择后 select 会执行相关命令,执行完命令后会再次让用户选择。所以,应该在命令尾部使用 break 命令来终止 select。

例如:

代码语言:txt
复制
select fname in cat dog sheep mouse;do
  echo your choice: \"$REPLY\) $fname\"
  break
done
1) cat
2) dog
3) sheep
4) mouse
#? 3                      
your choice: "3) sheep"   # 将输出序号3对应的内容

continue、break、return、exit


代码语言:txt
复制
exit [n]
退出当前shell,在脚本中应用则表示退出整个脚本。其中数值n表示退出状态码。

break [n]
退出整个循环,包括for、while、until和select语句。其中数值n表示退出的循环层次。

continue [n]
退出当前循环进入下一次循环。n表示继续执行向外退出n层的循环。默认n=1,表示继续当前层的下一循环,n=2表示继续上一层的下一循环。

return [n]
退出整个函数。n表示函数的退出状态码。

唯一需要注意的是 return,它并非只能用于 function 内部,绝大多数人都有这样的误解。如果 return 用在 function 之外,但在 source 命令的执行过程中,则直接停止该执行操作,并返回给定状态码 n(如果未给定,则为 0)。如果 return 在 function 之外,且不在 source 的执行过程中,则这是一个错误用法。

为什么要让 return 单独作用于 source 命令?如果了解 source 的特性『在当前 shell 而非子 shell 执行指定脚本中的代码』的话,就能理解为什么会这样。

比如设计一个脚本,它可以在当前 Shell 命令行下激活几个代理相关的变量,还能注销这些代理变量:

代码语言:txt
复制
proxy="http://127.0.0.1:8080"
function active_proxy() {
  export http_proxy=$proxy
  export https_proxy=$proxy
  export ftp_proxy=$proxy
  export no_proxy=localhost
}

case $1 in 
  set) active_proxy;;
  unset) unset http_proxy https_proxy ftp_proxy no_proxy;;
  *) return 0
esac

因为变量要在当前 Shell 下生效,所以应该使用 source 命令去执行脚本:

代码语言:txt
复制
source proxy.sh set
source proxy.sh unset
wx.jpg
wx.jpg

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • if 语句
  • case
  • for 循环
  • while 循环
  • select 选项选择
  • continue、break、return、exit
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
http://www.vxiaotou.com