Shell脚本编程基础

一、构建基本脚本

1.1 使用多个命令

  1. 两个命令一起运行,放在同一行,用;隔开,例如pwd; ls

1.2 创建shell脚本

  1. 在创建shell脚本文件时,必须在文件的第一行指定要使用的shell。其格式为:#!/bin/bash
    • 井号(#)用作注释行。shell并不会处理shell脚本中的注释行。然而,shell脚本文件的第一行是个例外,#后面的惊叹号会告诉shell用哪个shell来运行脚本。

1.3 显示消息

  1. 使用echo 内容

    • -n不换行
    • -e启用转义字符,echo默认不处理转义字符
  2. 防止字符串被Shell特殊字符解析

    • 输出内容用""双引号包裹:允许变量和转义字符的解析
    • 输出内容用''单引号包裹:完全禁止解析,包括变量
  3. 长文本输出,使用\反斜杠

    echo "This is a \
    long line"
  4. 命令捕获:将命令传给echo,使用$(命令)

    • echo "current pwd is $(pwd)"

1.4 使用变量

  1. 环境变量可以直接用使用,用$环境变量即可。
  2. 用户变量可以是任何由字母、数字或下划线组成的文本字符串,长度不超过20个。用户变量区分大小写。
  3. 在变量、等号和值之间不能出现空格
  4. shell脚本会自动决定变量值的数据类型。
  5. 引用一个变量值时需要使用美元符,而引用变量来对其进行赋值时则不要使用美元符
  6. 为了避免变量与其他内容混淆,可以使用{}包裹变量,例如echo "your name is ${name}"

1.5 重定向输入和输出

  1. 输出重定向

    • 命令 > 文件,将命令内容保存到指定文件,这个会覆盖原文件
    • 命令 >> 文件,这个不会覆盖原文件,是追加操作。
  2. 输入重定向

    • 命令 < 文件,将文件的内容重定向到命令。

    • 内联输入重定向命令 << 结束标记,可以不使用文件进行重定向,在命令行中指定输入重定向的数据。

      [tom@Centos7 bash_learn]$ wc << EOF
      > hello
      > ouyang
      > 18
      > EOF
      3 3 17

1.6 管道

  1. 将一个命令的输出作为另一个命令的输入,就可以使用管道。命令一 | 命令二
  2. Linux系统实际上会同时运行这两个命令,在系统内部将它们连接起来。在第一个命令产生输出的同时,输出会被立即送给第二个命令。数据传输不会用到任何中间文件或缓冲区

1.7 数学计算

  1. 使用$[运算],只能整数运算
  2. bc计算器
    • 输入bc后,就可以进入计算器页面,可以定义变量,进行计算
    • 在脚本中使用:var1=$(echo "scale=4; 3.44 / 5" | bc)

1.8 退出脚本

  1. shell中运行的每个命令都使用退出状态码(exit status)告诉shell它已经运行完毕。退出状态码是一个0~255的整数值,在命令结束运行时由命令传给shell。可以捕获这个值并在脚本中使用。
  2. $?表示上一个命令的执行结果,0通常代表成功。
  3. 默认情况下,shell脚本会以脚本中的最后一个命令的退出状态码退出。
  4. exit命令允许你在脚本结束时指定一个退出状态码。
    • 直接终止整个shell脚本

1.9 运行脚本的不同方式

  1. 使用路径(绝对路径或相对路径)执行./test.sh
    • 脚本运行在一个新的子shell中,环境是独立的。也就是说,脚本中的变量、函数、工作目录的改变等,只在子 Shell 中生效,不会影响当前 Shell。
  2. 指定shell运行bash test.sh
    • 脚本运行在一个新的子shell中,环境是独立的
  3. 使用source test.sh或者. test.sh
    • 在本shell中执行,环境不是独立的

二、结构化命令

2.1 if-then 语句

  1. 基本语法

    if 命令 # 如果命令的退出状态码是0,则表示成功,就会执行then后面的部分,否则不执行
    then
    命令
    fi
    • 另一种形式
      if 命令; then
      命令
      fi
      # 这种形式更像其他编程语言的if

2.2 if-then-else 语句

  1. 基本语法
    if 命令; then
    命令
    else
    命令
    fi

2.3 if-then-elif-then-else 语句

  1. 基本语法
    if 命令; then
    命令
    elif 命令; then
    命令
    else
    命令
    fi

2.4 test语句

  1. 上述的if后面,紧跟着必须是命令,并且通过命令的状态码,来决定是否执行then语句。
  2. test命令中列出的条件成立,则会返回0,这样一来,就和其他语言的if-else类似了。
  3. 基本语法:test 条件,如果没有条件,则直接返回0
    • 还有另一种方式if [ 条件 ],注意,第一个方括号后和第二个方括号前必须有一个空格
  4. 数值比较
    • n1 -eq n2 检查n1是否与n2相等
    • n1 -ge n2 检查n1是否大于或等于n2
    • n1 -gt n2 检查n1是否大于n2
    • n1 -le n2 检查n1是否小于或等于n2
    • n1 -lt n2 检查n1是否小于n2
    • n1 -ne n2 检查n1是否不等于n2
    • 只能使用整数
  5. 字符串比较
    • str1 = str2 检查str1是否和str2相同
    • str1 != str2 检查str1是否和str2不同
    • str1 < str2 检查str1是否比str2小
    • str1 > str2 检查str1是否比str2大
    • -n str1 检查str1的长度是否非0
    • -z str1 检查str1的长度是否为0
  6. 文件比较
    • -d file 检查file是否存在并是一个目录
    • -e file 检查file是否存在
    • -f file 检查file是否存在并是一个文件
    • -r file 检查file是否存在并可读
    • -s file 检查file是否存在并非空
    • -w file 检查file是否存在并可写
    • -x file 检查file是否存在并可执行
    • -O file 检查file是否存在并属当前用户所有
    • -G file 检查file是否存在并且默认组与当前用户相同
    • file1 -nt file2 检查file1是否比file2新
    • file1 -ot file2 检查file1是否比file2旧
  7. 复合条件测试
    • 并且 [ condition1 ] && [ condition2 ]
      • 短路与,如果前者为假,后者不会执行
    • [ condition1 ] || [ condition2 ]
      • 短路或,如果前者为真,后者不会执行

2.5 if-then的高级特性

  1. 双括号:使用高级数学表达式
    • val++ 后增
    • val-- 后减
    • ++val 先增
    • --val 先减
    • ! 逻辑求反
    • ~ 位求反
    • ** 幂运算
    • << 左位移
    • >>右位移
    • &按位与
    • |按位或
    • &&逻辑与
    • ||逻辑或
  2. 双方括号:字符串比较的高级特性。
    • 提供了模式匹配

2.6 case命令

  1. 基本语法

    • case 变量 in
      模式1 | 模式2)
      命令;;
      模式三)
      命令;;
      *) # 都没匹配上时
      命令
      esac

2.7 for命令

  1. 基本语法

    for var in list
    do
    命令
    done
    • for命令遍历list,每次都会赋给var,list中的分隔符,默认是空格
  2. 更改字段分隔符

    • 有一个特殊的环境变量IFS,内部字段分隔符,定义了shell用作分隔符的一系列字符。
    • 改成换行符IFS='$\n'
    • 如果要指定多个IFS字符,只要将它们在赋值行串起来就行。IFS=$'\n':;"
      • 使用换行符,冒号和分号
  3. 使用通配符读取目录

    • for file in /home/rich/test/* # 读取所有文件(夹)
      do
      if [ -d "$file" ] # 为啥需要用双引号包裹,因为文件名可能有空格
      then
      echo "$file is a directory"
      elif [ -f "$file" ]
      then
      echo "$file is a file"
      fi
      done
  4. C语言风格的for

    • # 感觉很奇怪。。。
      for (( i=1; i <= 10; i++ ))
      do
      echo "The next number is $i"
      done
      $ ./test8
      The next number is 1
      The next number is 2
      The next number is 3
      The next number is 4
      The next number is 5
      The next number is 6
      The next number is 7
      The next number is 8
      The next number is 9
      The next number is 10

2.8 while命令

  1. 基本语法

    while 测试命令
    do
    命令
    done
  2. 多个测试命令

    while 测试命令一
    测试命令二
    do
    命令
    done
    • 多个测试命令都会执行,注意,只以最后一个的执行结果来判断是否进入do

2.9 until命令

  1. 基本语法

    until 测试命令
    do
    命令
    done
  2. 和while相反,只有返回不为0,才会执行

  3. 同样也可以有多个测试命令,只看最后一个的执行结果。

2.10 break命令

  1. 作用:跳出单个循环
  2. 跳出外部循环break n,指定跳出的循环层级,默认不加就是1.

2.11 continue命令

  1. 作用:中止某次循环。
  2. 和break类似,可以使用continue n,指定中止的循环层级

2.12 处理循环的输出

  1. 可以将循环的输出结果重定向到文件中
    for file in /home/rich/*
    do
    if [ -d "$file" ]
    then
    echo "$file is a directory"
    elif
    echo "$file is a file"
    fi
    done > output.txt # 在这里重定向

2.13 使用循环的例子

  1. 查找可执行文件

    • 如何模拟shell,查找PATH中的可执行文件?

    • 首先,PATH中定义了许多可执行文件的路径,使用进行分割,所以我们要修改IFS=:

    • 然后就可以遍历PATH中的所有路径,然后要保证是文件,并且具有执行权限。

    • IFS=:
      for folder in $PATH
      do
      echo "$folder:"
      for file in $folder/*
      do
      if [ -f "$file" ] && [ -x "$file" ]
      then
      echo " $file"
      fi
      done
      done
  2. 批量创建用户

    • 现在需要批量创建用户,有一个文件users.csv,里面的内容是id,名字 姓,也就是说两个字段,一个id,一个name,那么是包含空格的。

    • input="users.csv"
      # 从文件中读取,分隔符改成,
      while IFS=',' read -r userid name
      do
      echo "adding $userid"
      useradd -c "$name" -m $userid
      done < "$input"

三、处理用户输入

3.1 命令行参数

  1. 执行脚本时./test.sh 10 30,其中的1030就是传入的参数
  2. $0是程序名,$1是第一个参数,$2是第二个参数,以此类推,$9是第九个。第十个之后的参数${10}
  3. 获取脚本名,可以使用函数basename,例如,name=$(basename $0)

3.2 特殊参数变量

  1. 统计命令行中输入了多少个参数$#,程序名$0不被计算在内。
  2. $*会将命令行上的所有参数当作一个单词保存,不包括$0
    • 可以使用for循环分割,但是如果某个参数就包含空格时,就会出问题。
  3. $@保存命令行的所有参数,同时保留参数边界。
    • 如果有空格,可以for var in "$@",用双引号包裹即可。

3.3 移动变量

  1. 使用shift命令,每执行一次,可以将所有参数往左移一个,也就是说$2变成$1$3变成$2,注意,不会改变$0,也就是说$1被移没了。
  2. shift n,移动n个
  3. 有啥用?可以依次处理参数,处理完一个后,就可以移出了。

3.4 处理命令行选项和参数getopt

  1. 命令格式getopt optstring parameters

    • 在optstring中列出脚本中用到的每个命令行选项字母,然后如果某个选项需要参数,则加一个:
  2. 例子

    # 需要abcd选项,b选项需要一个参数
    $ getopt ab:cd -a -b test1 -cd test2 test3
    -a -b test1 -c -d -- test2 test3 # --是为了分隔开选项和参数 --后就是参数了
    $
  3. getopt是将空格当作分隔符,也就是说,如果某个参数有引号,即使用双引号包裹起来,也不起作用。

  4. 在脚本中使用set -- $(getopt -q ab:cd "$@")

    • -q是忽略错误
    • 将用户的参数,交给getopt,并且替换成$1$2等等。

3.5 获得用户输入

  1. 使用read 变量1 变量2,将用户输入到变量中。

    • 之后就会等待用户输入,按回车结束输入
    • 用户输入按空格分割分别赋值给变量1、变量2
    • 如果用户输入数目不够,则后面的变量,值为空
    • 如果用户输入的数目过多,最后一个变量,获取剩下的所有值
    • 也可以不指定变量,那么就会保存在REPLY环境变量中。
  2. -p输出提示信息

  3. -t seconds指定超时时间,如果超时都未输入,则会返回一个非0的状态码。

  4. -n5限制输入字符数

  5. -s使用户输入内容和shell背景色一样(输入密码时)。

  6. 通过文件读取

    • count=1
      # 通过管道 然后传给while循环,不断读取 每次读取一行
      cat test | while read line
      do
      echo "Line $count: $line"
      count=$[ $count + 1]
      done
      echo "Finished processing the file"

四、函数

  1. 创建

    function 函数名 { # 函数名和{之间必须有一个空格
    命令
    return 状态码 # 如果没有,以最后一条命令的状态码
    }
  2. 使用:函数名

  3. 使用函数的输出

    • 例子

      function name {
      echo "ouyang"
      }
      your_name=$(name)
      echo $your_name
    • 可以把函数当作一个命令,$(命令)就是获取执行完命令的输出。

  4. 向函数传递参数

    • 例子
      function add {
      # $0就是函数名 #1就是第一个参数
      echo $[$1 + $2]
      }
      res=$(add 1 2) # 类似于向脚本传递参数一样 向函数传递参数
      echo $res
  5. 在脚本中定义的变量是全局变量,也就是说,函数可以直接访问。注意,在函数内部定义的变量,也是全局变量

  6. 可以在变量前加local,变成局部变量,注意,只能在函数中使用。

  7. 创建库

    • 使用source 脚本或者. 脚本时,会在当前shell中执行脚本,所以,我们可以创建一个脚本,里面全是函数,称之为库,然后在另一个脚本,使用source 库,就可以在该脚本中使用库了。
posted @   ouyangxx  阅读(197)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· DeepSeek 开源周回顾「GitHub 热点速览」
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
点击右上角即可分享
微信分享提示