Linux Shell 中的用户输入处理

本篇概要:

  • 1. 命令行参数;
  • 2. 命令行参数的处理;
    • 2.1 错误检测:条件判断(参数验证);
    • 2.2 移动变量:shift 命令;
    • 2.3 选项处理;
      • 2.3.1 处理简单选项;
      • 2.3.2 分离参数和选项;
      • 2.3.3 处理带值选项;
      • 2.3.4 getopt 命令;
      • 2.3.5 getopts 命令;
      • 2.3.6 选项的标准化。
  • 3. 在脚本运行时获取输入。

# 在使用 Linux 的命令时,比如使用 ls 命令,通常会给命令传递一定的数据
ls -a		# “-a”显示所有目录
ls -a c*	# “c*”特定的数据来进行命令中数据的筛选

# “-a”选项和“-c”参数都是传递给 ls 命令的数据
# 对于 ls 命令来说,这是两种类型用户的输入,一种是选项、一种是参数
# 选项通常用来定义命令的行为
# 而参数用来向命令传递特定的数据

cat
# cat 启动后,用户可以通过输入一定的数据然后得到命令的反馈
# 这也是在命令中用户输入的形式

# 以上三种,前两种是在命令启动时用户将数据传递给了命令
# 后一种是命令启动之后(也就是命令运行时)进行数据的传递
# 这也是 Shell 编程中需要进行用户输入处理的两种主要方式
  • 命令行参数(选项、参数)
  • 运行时输入

1. 命令行参数;

  • 在给一个命令传递参数时,参数之间是通过空格进行分隔的。
  • 在脚本中,使用一个叫“位置参数”的特殊变量进行命令行参数的读取
  • 位置参数,就是将一个“$”和参数的位置顺序的一个数字($+position)结合起来。
  • 比如说 $0$1$2 分别代表了不同位置上的参数
    • $0 指的是调用 Shell 脚本程序的程序名
    • $1$2$3$9 是命令行传递进来的参数。前 9 个参数可以这样进行获取。当传递大于 9 个参数时,比如第十个位置的参数,就需要
    • ${10} 为参数的序号加一个大括号。
cd /data/shellscript	# 演示文件所在文件夹
mkdir userinput
cd userinput

touch add
chmod +x add	# 赋予执行权限
vim add
# 写入以下内容
#! /bin/bash

# 获得传递进来的两个命令行参数
echo "The command name is:$0"
echo "The #1 param is:$1"
echo "The #2 param is:${2}"
echo "The #2 param is:${10}"
SUM=$[ $1+${2}+${10} ]
echo "The result is $SUM"

# 保存退出执行
./add 1 2 3 4 5 6 7 8 9 10
# 输出
The command name is:./add	# 包含路径信息的命令名
The #1 param is:1
The #2 param is:2
The #2 param is:10
The result is 13

# 读取程序名: basename
touch calc
chmod +x calc	# 赋予执行权限
vim calc
# 写入以下内容
#! /bin/bash

name=`basename $0`

if [ $name = "add" ]
then
	result=$[ $1+$2 ]
elif [ $name = "min" ]
then
	result=$[ $1-$2 ]
fi
echo "the $0 result is $result "

# 保存退出

mv add ad_old
ln -s calc add		# 建立软连接,calc 连接到 add 上
ln -s calc min
ls -l				# 返回
# 相当于使用了同一个脚本文件创建了两个执行了不同命令的脚本
lrwxrwxrwx. 1 root root   4 7月  28 20:28 add -> calc
-rwxr-xr-x. 1 root root 221 7月  28 20:11 add_old
-rwxr-xr-x. 1 root root 162 7月  28 20:27 calc
lrwxrwxrwx. 1 root root   4 7月  28 20:28 min -> calc

./add 20 10			# 返回:the ./add result is 30 
./min 20 10			# 返回:the ./min result is 10
# 同一个脚本执行了两种命令,这两种命令通过软连接来建立并且在脚本内部进行判断和执行的
  • 特殊变量
    • 参数计数:$#,返回所有的参数数量
    • 所有参数:$*,将所有的参数作为一个参数调用
    • 参数列表:$@,返回所有参数,是以列表形式调用
touch test_param
chmod +x test_param	# 赋予执行权限
vim test_param
# 写入以下内容
#! /bin/bash

echo $#
echo $*
echo $@

for var in "$*"
do
	#“\” 不让$转义
	echo "\$* param = $var"
done

for var in "$@"
do
	echo "\$@ param = $var"
done

# 保存退出执行
./test_param 1 2 3 4 5
# 返回
5
1 2 3 4 5
1 2 3 4 5
$* param = 1 2 3 4 5
$@ param = 1
$@ param = 2
$@ param = 3
$@ param = 4
$@ param = 5

2. 命令行参数的处理;

2.1 错误检测:条件判断(参数验证);

vim calc
# 写入以下内容
#! /bin/bash

# 参数验证
if [ $# -lt 2 ]
then
	echo "please input at least 2 params"
	exit
fi

name=`basename $0`

if [ $name = "add" ]
then
	result=$[ $1+$2 ]
elif [ $name = "min" ]
then
	result=$[ $1-$2 ]
fi
echo "the $0 result is $result "

# 保存退出

# 以上只是一个简单的参数验证的例子
# 实际中逻辑会复杂的多
# 可以用正则表达式对参数做严格的限定

# 为什么要对参数做限制?因为用户的输入都是不可靠的
# 为了避免在程序中进行不必要的操作,所以需要在开始时对传入的参数进行一定的错误处理
# 这样可以显著的提高程序的可靠性

2.2 移动变量:shift 命令;

  • 当通过命令行传递的参数很多时,处理起来就很麻烦
  • shift 命令可以进行多参数的处理,它会不断的向前移动位置参数
# 此脚本用来计算一系列给定的数字参数的和
# 如果我们要在脚本中使用位置参数进行一一的访问时,会非常的麻烦
touch sum
chmod +x sum	# 赋予执行权限
vim sum
# 写入以下内容
#! /bin/bash

result=0
# 参数的长度是否非 0
while [ -n "$1" ]
do
	result=$[ $result + $1 ]
	# 通过一个循环来依次读取每个参数
	# 并且在循环中一个参数读取后,使用 shift 命令移动参数
	# 每次执行 shift 命令,相应的位置参数的值会减 1
	# 也就是,他会将所有的参数向前移动
	# 原来在第一个位置的参数就会被扔掉
	# 就是说,当我们在执行一次 shift 的时候
	# 原来的“$2”就变成了“$1”,原来的“$1”就不能访问了
	# 因此在这个循环中,每次使用“$1”就可以访问到一个新的参数值
	# 这样“$1”就可以遍历所有的参数
	shift
done

echo "The numbers of SUM is: $result"

# 保存退出执行
./sum 1 2 3 4 5 6 7 8 9	# 返回
The numbers of SUM is: 45

2.3 选项处理;

  • 在 Linux 命令中,选项是一种对命令行为进行设置的一种通用方式,它通常的形式就是一个“-”带上一个字母
  • 很多情况下,选项也会有一个参数

2.3.1 处理简单选项;

touch test_param2
chmod +x test_param2	# 赋予执行权限
vim test_param2
# 写入以下内容
#! /bin/bash

while [ -n "$1" ]
do
	# case 匹配不同的选项值
	# 所有被举出的选项就会被命中
	case "$1" in
	-a) echo 'option -a' ;;
	-b) echo 'option -b' ;;
	-c) echo 'option -c' ;;
	esac
	# 遍历每一个位置参数
	shift
done

# 保存退出执行
./test_param2 -a -b -c	 # 输出
option -a
option -b
option -c

# 以上是处理选项最简单的方式
# 在实际使用中,会遇到命令同时接收选项和参数

2.3.2 分离参数和选项;

# 在命令行的处理中,要将选项和参数进行分离
# 有一个标准处理参数和选项的方法:
# 使用一个特殊字符将选项与参数隔离开
vim test_param2
# 修改内容
#! /bin/bash

while [ -n "$1" ]
do
	case "$1" in
	-a) echo 'option -a' ;;
	-b) echo 'option -b' ;;
	-c) echo 'option -c' ;;
	# 遇到“双重横杠”时结束选项的判断
	# 也就是说在这里,可以使用 break 语句进行循环的跳出
	# 在 break 之前,要进行一个 shift 操作
	# 那是因为如果 break 了,后面循环中的 shift 不会被执行
	# 所以在这里执行了 shift,就相当于就将后面的参数又赋给了“$1”
	# 那我们在这个循环的后面,都可以正常处理剩余的参数了
	--) shift
		break;;
	esac
	shift
done

# 查看剩余的其它参数
echo "params are $*"

# 保存退出执行
./test_param2 -a -b -c -- param1 param2 
# 输出
option -a
option -b
option -c
params are param1 param2

2.3.3 处理带值选项;

# 命令中的选项也有可能包含相应的值
vim test_param2
# 修改内容
#! /bin/bash

while [ -n "$1" ]
do
	case "$1" in
	-a) echo 'option -a' ;;
	# “-b”选项会有一个参数的值
	# 当前在匹配“-b”时用到的是“$1”,
	# “-b”的值就在“$1”后面,那自然就是“$2”
	# 将“$2”赋给一个变量
	# 但是这样做还不够!
	# 因为实际上在这里引用了两个变量
	# 但是在做完 case 判断后, shift 只用了一次
	# 因此还需要做一次 shift 操作
	-b) value="$2"
		echo "option -b, value is $value" ;;
		shift;;
	-c) echo 'option -c' ;;
	--) shift
		break;;
	esac
	shift
done

echo "params are $*"

# 保存退出执行
./test_param2 -a -b 100 -c -- param1 param2 
# 输出
option -a
option -b, value is 100
option -c
params are param1 param2

# 但是实际使用中,会遇到选项合并的情况
# 比如 ./test_param2 -ab
# 那刚才的方式就行不通了
# 有一个工具可以帮助处理选项的值

2.3.4 getopt 命令;

# getopt 能正确将选项和参数区分
# 这里得到的结果就是之前说到的,选项和参数分离的方式
getopt abcd -b -acd					
# 输出: 
-b -a -c -d --

getopt abcd -b -acd param1 param2	
# 输出: 
-b -a -c -d -- param1 param2

# 参数后面加冒号,就是带值选项
getopt ab:cd -b value -acd param1 param2	
# 输出:
-b value -a -c -d -- param1 param2

vim test_param2
# 修改内容
#! /bin/bash

# 先使用 set -- 命令可以将 getopt 的结果重新赋给命令行参数
# 后面就可以继续使用位置参数来访问已经解析后的包含选项和参数的串
# 在调用 getopt 命令时,使用了“-q”选项,
# 不会输出 getopt 所遇到的错误
# 后面的参数使用了“¥@”,它是原来的命令行参数的一个集合
set -- `getopt -q ab:c "$@"`

while [ -n "$1" ]
do
	case "$1" in
	-a) echo 'option -a' ;;
	# “-b”选项会有一个参数的值
	# 当前在匹配“-b”时用到的是“$1”,
	# “-b”的值就在“$1”后面,那自然就是“$2”
	# 将“$2”赋给一个变量
	# 但是这样做还不够!
	# 因为实际上在这里引用了两个变量
	# 但是在做完 case 判断后, shift 只用了一次
	# 因此还需要做一次 shift 操作
	-b) value="$2"
		echo "option -b, value is $value" ;;
		shift;;
	-c) echo 'option -c' ;;
	--) shift
		break;;
	esac
	shift
done

echo "params are $*"

# 保存退出执行
./test_param2 -ac -b value param1 param2 
# 返回
option -a
option -c
option -b, value is 'value'
params are 'param1' 'param2'

# 如果选项中的值包含了空格
./test_param2 -ac -b "value 123" param1 param2 
# 返回
option -a
option -c
option -b, value is 'value'
params are 'param1' 'param2'
# 此时并没有成功的解析所有选项值
# 也就是说 getopt 不能解析带空格的选项值

2.3.5 getopts 命令;

touch test_param3
chmod +x test_param3	# 赋予执行权限
vim test_param3
# 写入以下内容
#! /bin/bash

# getopts 命令不需要进行参数变量的指定
# 可以直接通过 getopt 获取
# getopts 后面紧跟的是用来解析选项的串
# 在 while 语句中,会把 getopts 解析出来的所有选项赋给 opt
# 然后在循环中使用 opt 来进行解析后选项的访问
# 与 getopt 不同, getopts 解析出的选项已经没有了“-”
# 直接通过 a,b,c 进行访问
while getopts ab:c opt
do
	case "$opt" in
	a) echo 'option -a' ;;
	# getopts 命令包含两个重要环境变量
	# 一个是 $OPTARG,也就是选项的参数
	# 通过 $OPTARG 这个变量就可以访问到解析后的对应的选项的参数值
	b) echo "option -b value is: $OPTARG" ;;
	c) echo 'option -c' ;;
	*) echo "unknown option : $opt" ;;
	esac
done
# 解析了所有的选项之后,会使用另一个环境变量 $OPTIND
# 这是一个计数,存放的是所进行解析的选项的数量
# 用 shift 移动所有的参数选项
# 做完移动后,当前的“$1”就指向了后面的参数部分
shift $[ $OPTIND -1 ]

# 后面就可以使用循环来解析选项之后的所有的参数
count=1
for param in "$@"
do
	echo "param #$count is : $param"
	count=$[ $count + 1 ]
done

# 保存退出执行
./test_param3 -ac -b "value 123" "param1 123" param2 
option -a
option -c
option -b value is: value 123
param #1 is : param1 123
param #2 is : param2

2.3.6 选项的标准化。

选项 说明
-a 显示所有对象
-c 生成一个计数
-d 指定一个目录
-e 扩展一个对象
-f 指定读入数据的文件
-h 显示命令的帮助信息
-i 忽略文本大小写
-l 产生输出的长格式版本
-n 使用非交互模式
-o 指定将所有输出重定向的输出文件
-q 以安静模式运行
-r 递归处理目录和文件
-s 以安静模式运行
-v 生成详细输出
-x 排除某个对象
-y 对所有问题回答 yes

3. 在脚本运行时获取输入。

  • 使用 read 命令
## 1. 基本读取
touch read_test1
chmod +x read_test1	# 赋予执行权限
vim read_test1
# 写入以下内容
#! /bin/bash

echo "Please type your input"
# 使用 read 读取一个变量,后面跟一个变量名
# read 命令就会将实际的用户输入存放到这个变量中
# 可以用 echo 输出变量
read input
echo "You typed: $input"

# 保存退出执行
./read_test1
# 返回
Please type your input
hello world
You typed: hello world

## 2. 处理超时
vim read_test1
# 修改内容
#! /bin/bash

# -t 设定超时,后面跟一个数值(秒)
# -p 输入提示
if read -t 5 -p "Please type your input:" input
then
	echo "You typed: $input"
else
	echo "timeout"
fi
# 保存退出执行

## 3. 隐藏方式读取
touch read_test2
chmod +x read_test2	# 赋予执行权限
vim read_test2
# 写入以下内容
#! /bin/bash

echo "PLease input a password"
read -s passwd
echo "Your password is : $passwd"

# 保存退出执行
./read_test2
PLease input a password

Your password is : asdf

## 4. 从文件中读取
# 之前都是通过标准输入与 read 命令进行交互
# 实际上 read 也接受从文件输入
# 可以使用文件重定向或者管道实现
# 文件重定向实现:
touch read_test3
chmod +x read_test3	# 赋予执行权限
vim read_test3
# 写入以下内容
#! /bin/bash

exec 0<read_test1
count=1

while read line
do
	echo "#$count:$line"
	count=$[ $count + 1 ]
done
# 保存退出执行
./read_test3
# 返回
#1:#! /bin/bash
#2:
#3:# -t 设定超时,后面跟一个数值(秒)
#4:# -p 输入提示
#5:if read -t 5 -p "Please type your input" input
#6:then
#7:echo "You typed: $input"
#8:else
#9:echo "timeout"
#10:fi

# 管道实现:
vim read_test3
# 修改内容
#! /bin/bash

count=1

cat read_test1 | while read line
do
	echo "#$count:$line"
	count=$[ $count + 1 ]
done
# 保存退出执行
./read_test3




你可能感兴趣的:(Linux,Shell,Linux,Shell)