处理用户输入

  • bash shell提供了一些不同的方法从用户处获得数据,包括命令行参数、命令行选项、直接从键盘读取。

命令行参数

读取参数

在Shell脚本中,命令行参数可以通过特殊变量 $1$2$3$n来获取,其中 $1表示第一个参数,$2表示第二个参数,以此类推,$n表示第 n 个参数。

#!/bin/bash

# 演示读取命令行位置参数
echo "[读取中] 接收到的前三个参数如下:"
echo "参数 1:$1"
echo "参数 2:$2"
echo "参数 3:$3"
  1. 利用 for 循环和命令行参数计算阶乘:

    #!/bin/bash
    
    # 接收第一个参数作为计算对象
    n=$1
    fact=1
    
    for((i=1; i<=$n; i++))
    do
       fact=$((fact * i))  
    done
    echo "[计算结果] $n 的阶乘等于 $fact"
    exit 0
  2. 将带有空格的文本字符串传递给脚本时需要注意用引号,引号在进行传递时只表明数据的起止位置,不作为数据的一部分:

    #!/bin/bash
    
    # 使用 $1 读取被引号包裹的整体参数
    echo "你好 $1,很高兴见到你!"
  3. 当参数超过 9 个时需要修改变量名,变量名两侧加上花括号 {}

    #!/bin/bash
    # 处理大量位置参数(超过 9 个)
    #
    total=$((${10} * ${11}))
    echo "第 10 个参数是:${10}"
    echo "第 11 个参数是:${11}"
    echo "两者相乘的结果为:$total"

    运行脚本:

    第 10 个参数是:10
    第 11 个参数是:11
    两者相乘的结果为:110

G

读取脚本名

可以用 $0 参数获取 shell 在命令行启动的脚本名:

#!/bin/bash
# 演示获取启动当前脚本时使用的路径/名称
#
echo "启动该脚本时使用的完整参数 $0 为:$0"
#

当传给 $0 变量的实际字符串不仅仅是脚本名,而是完整的脚本路径时,变量 $0 就会使用整个路径:

ubuntu@sh:~$ bash ~/temp.sh
启动该脚本时使用的完整参数 $0 为:/home/ubuntu/temp.sh

basename 命令会返回不包含路径的纯脚本名:

#!/bin/bash
# 演示使用 basename 提取纯脚本名称
#
name=$(basename $0)

echo "提取后的纯脚本名称为:$name"
#

作业1:编写并运行以下脚本:

基于脚本名实现不同功能的脚本:

#!/bin/bash
# 演示多功能脚本(根据链接名称执行不同运算)
#
name=$(basename $0)
#
if [ $name = "addem" ]
then
    total=$(($1 + $2))
    echo "[计算] 执行加法运算"
elif [ $name = "multem" ]
then
    total=$(($1 * $2))
    echo "[计算] 执行乘法运算"
fi
#
echo "计算结果为:$total"
$ cp temp.sh addem
$ ln -s temp.sh multem

$ bash addem 2 5
[计算] 执行加法运算
计算结果为:7

$ bash multem 2 5
[计算] 执行乘法运算
计算结果为:10

测试参数

在脚本内使用参数时需要考虑到当用户未输入或输入错误参数时的情况:

#!/bin/bash

# 获取位置参数
n=$1
fact=1

if [ $n -eq 0 ]; then
  echo "[提示] 0 的阶乘始终为 1"
  exit 0
elif [ $n -lt 0 ]; then
  echo "[错误] 输入无效。必须提供正整数参数。"
  exit 1
fi

for ((i=1; i<=$n; i++)); do
  fact=$((fact * i))
done

echo "[计算结果] $n 的阶乘等于 $fact"
exit 0

特殊参数变量

参数统计

使用特殊参数变量可以帮助我们更方便地检查用户传递给脚本的参数。特殊变量 $# 含有脚本运行时携带的命令行参数的总个数。

#!/bin/bash
# 演示统计参数个数
#
echo "[统计] 本次脚本运行共接收到 $# 个位置参数。"

执行结果:

ubuntu@sh:~$ bash temp.sh
[统计] 本次脚本运行共接收到 0 个位置参数。

ubuntu@sh:~$ bash temp.sh 1 2 3 4 5
[统计] 本次脚本运行共接收到 5 个位置参数。

ubuntu@sh:~$ bash temp.sh "Hello world"
[统计] 本次脚本运行共接收到 1 个位置参数。

作业2:在上文阶乘脚本的基础上完善以下功能
在使用参数前测试参数总数:

#!/bin/bash

if [ $# -ne 1 ]; then
  echo "Usage: $0 number"
  exit 1
fi

n=$1
fact=1

if [ $n -eq 0 ]; then
  echo "0 的阶乘是 1"
  exit 0
elif [ $n -lt 0 ]; then
  echo "输入无效。数字必须是正整数。"
  exit 1
fi

for ((i=1; i<=$n; i++)); do
  fact=$((fact * i))
done

echo "$n 的阶乘是 $fact"
exit 0

抓取所有的数据

$*变量会将命令行上提供的所有参数当作一个单词保存。这个单词包含了命令行中出现的每
一个参数值。基本上$*变量会将这些参数视为一个整体,而不是多个个体。
另一方面,$@变量会将命令行上提供的所有参数当作同一字符串中的多个独立的单词。这样
你就能够遍历所有的参数值,得到每个参数。这通常通过for命令完成。

#!/bin/bash
# 演示 $* 与 $@ 的区别

echo "[测试] 使用 \$* 遍历所有参数:"
count=1
for param in "$*"
do
    echo "识别为一个整体: \$* 参数 #$count = $param"
    count=$(($count + 1))
done

echo "[测试] 使用 \$@ 遍历所有参数:"
count=1
for param in "$@"
do
    echo "识别为独立个体: \$@ 参数 #$count = $param"
    count=$(($count + 1))
done

移动变量

bash shell的shift命令能够用来操作命令行参数。在使用shift命令时,默认情况下它会将每个参数变量向左移动一个位置。所以,变量$3的值会移到$2中,变量$2的值会移到$1中,而变量$1的值则会被删除(注意,变量$0的值,也就是程序名,不会改变)。

这是遍历命令行参数的另一个好方法,尤其是在你不知道到底有多少参数时。你可以只操作第一个参数,移动参数,然后继续操作第一个参数。

作业 1:编写并执行以下求和脚本(利用 shift 遍历参数):

#!/bin/bash

# 初始化求和变量
sum=0

while [ -n "$1" ]
do
    echo "[处理中] 正在累加参数:$1"
    sum=$((sum + $1))
    shift # 将参数列表左移,原 $2 变为 $1
done

echo "[计算结果] 所有参数之和为:$sum"
exit 0
ubuntu@sh:~$ bash temp.sh 1 2 3
[处理中] 正在累加参数:1
[处理中] 正在累加参数:2
[处理中] 正在累加参数:3
[计算结果] 所有参数之和为:6

处理选项

选项是跟在单破折线后面的单个字母,它能改变命令的行为。

处理简单选项

#!/bin/bash
# 演示使用 case 处理简单的命令行选项

while [ -n "$1" ]
do
    case "$1" in
        -a) echo "[解析] 检测到 -a 选项" ;;
        -b) echo "[解析] 检测到 -b 选项" ;;
        -c) echo "[解析] 检测到 -c 选项" ;;
        *)  echo "[报错] $1 不是一个合法的选项" ;;
    esac
    shift # 移动到下一个参数
done

done


### 分离参数和选项、处理带值选项

```bash
#!/bin/bash
# extracting options and parameters
echo
while [ -n "$1" ]
do
    case "$1" in
        -a) echo "Found the -a option"
            ;;
        -b) echo "Found the -b option"
            ;;
        -c) echo "Found the -c option" 
            coption=$(($2 * $3))
            echo $coption
            shift
            shift
            ;;
        --) shift
            break
            ;;
        *) echo "$1 is not an option";;
    esac
    shift
done
#
count=1
for param in $@
do
    echo "Parameter #$count: $param"
    count=$(($count + 1))
done

利用选项和参数实现阶乘和求和的计算工具

作业2:编写并执行以下脚本:

#!/bin/bash

if [ $# -lt 2 ]
then
    echo "[帮助] 求和与阶乘小工具"
    echo "用法说明:"
    echo "  -a <数字1> <数字2> ...   进行累加求和"
    echo "  -f <数字>              进行阶乘计算"
    exit 1
fi

case "$1" in
    -a)
        # 从第二个参数开始累加
        while [ -n "$2" ]
        do
            sum=$((sum + $2))
            shift
        done
        echo "[计算结果] 累加之和为:$sum"
        exit 0
        ;;
    -f)
        if [ "$2" -eq 0 ]
        then
            echo "[计算结果] 0 的阶乘始终为 1"
            exit 0
        fi
        fact=1
        for((i=1; i<=$2; i++))
        do
            fact=$((fact * i))
        done
        echo "[计算结果] $2 的阶乘为:$fact"
        exit 0
        ;;
    *)
        echo "[报错] 未知的参数选项"
        echo "用法示例: $0 -a 1 2 3 或 $0 -f 5"
        ;;
esac
exit 0

标准化选项

5.处理用户输入-2023-06-16-21-20-42