Linux处理用户输入

目录

一、传递参数

[1.1 读取参数](#1.1 读取参数)

[1.2 读取脚本名](#1.2 读取脚本名)

二、跟踪参数

三、移动参数

四、处理选项

[4.1 查找选项](#4.1 查找选项)

[4.1.1 处理简单选项](#4.1.1 处理简单选项)

[4.1.2 分离参数和选项](#4.1.2 分离参数和选项)

[4.1.3 处理含值的选项](#4.1.3 处理含值的选项)

五、选项标准化

[5.1 使用 getopt 命令](#5.1 使用 getopt 命令)

[5.1.1 命令格式](#5.1.1 命令格式)

[5.1.2 在脚本中使用getopt](#5.1.2 在脚本中使用getopt)

[5.2 使用getopts命令](#5.2 使用getopts命令)

六、获取用户输入

[6.1 read读取](#6.1 read读取)

[6.2 从文件中读取](#6.2 从文件中读取)


一、传递参数

向 shell 脚本传递数据的最基本方法是使用命令行参数 。bash shell 会将所有的命令行参数都指派给称作位置参数的特殊变量。这也包括shell脚本名称。位置变量的名称都是标准数字,$0 对应脚本名,$1 对应第一个命令行参数,$2 对应第二个命令行参数,以此类推,直到 $9,第9个之后,必须在变量名两侧加上花括号,比如 ${10} 。

1.1 读取参数

如果需要 输入更多的命令行参数,则参数之间必须用空格分开

bash 复制代码
[root@iZbp1ir1vzqwzkdy7mvjthZ ~]# cat position.sh
#! /bin/bash

value=$[ ${10} * ${11} ]
echo "第10个参数是${10}"
echo "第11个参数是${11}"
echo $value
[root@iZbp1ir1vzqwzkdy7mvjthZ ~]# ./position.sh 1 2 3 4 5 6 7 8 9 10 12
第10个参数是10
第11个参数是12
120
[root@iZbp1ir1vzqwzkdy7mvjthZ ~]#

1.2 读取脚本名

可以使用位置变量 $0 获取在命令行中运行的脚本名。

bash 复制代码
[root@iZbp1ir1vzqwzkdy7mvjthZ ~]# cat position.sh
#! /bin/bash

value=$[ ${10} * ${11} ]
echo "第10个参数是${10}"
echo "第11个参数是${11}"
echo $value
echo "脚本名是$0"
[root@iZbp1ir1vzqwzkdy7mvjthZ ~]# bash position.sh 1 2 3 4 5 6 7 8 9 10 12
第10个参数是10
第11个参数是12
120
脚本名是position.sh
[root@iZbp1ir1vzqwzkdy7mvjthZ ~]#

但是这会有一个问题,如果使用另一个命令运行shell脚本,则命令名会和脚本名混在一起

bash 复制代码
[root@iZbp1ir1vzqwzkdy7mvjthZ ~]# cat position.sh
#! /bin/bash

value=$[ ${10} * ${11} ]
echo "第10个参数是${10}"
echo "第11个参数是${11}"
echo $value
echo "脚本名是$0"
[root@iZbp1ir1vzqwzkdy7mvjthZ ~]# bash position.sh 1 2 3 4 5 6 7 8 9 10 12
第10个参数是10
第11个参数是12
120
脚本名是position.sh
[root@iZbp1ir1vzqwzkdy7mvjthZ ~]# ./position.sh 1 2 3 4 5 6 7 8 9 10 12
第10个参数是10
第11个参数是12
120
脚本名是./position.sh
[root@iZbp1ir1vzqwzkdy7mvjthZ ~]#

这还不是唯一的问题,如果运行脚本时使用的是绝对路径,那么位置变量 $0 就会包含整个路径

bash 复制代码
[root@iZbp1ir1vzqwzkdy7mvjthZ ~]# pwd
/root
[root@iZbp1ir1vzqwzkdy7mvjthZ ~]# ~/position.sh
/root/position.sh: line 3: *  : syntax error: operand expected (error token is "*  ")
第10个参数是
第11个参数是

脚本名是/root/position.sh
[root@iZbp1ir1vzqwzkdy7mvjthZ ~]#

basename 命令可以返回不包含路径的脚本名

bash 复制代码
[root@iZbp1ir1vzqwzkdy7mvjthZ ~]# cat position.sh
#! /bin/bash

value=$[ ${10} * ${11} ]
echo "第10个参数是${10}"
echo "第11个参数是${11}"
echo $value
name=$(basename $0)
echo "脚本名是$name"
[root@iZbp1ir1vzqwzkdy7mvjthZ ~]# ./position.sh 1 2 3 4 5 6 7 8 9 10 12
第10个参数是10
第11个参数是12
120
脚本名是position.sh
[root@iZbp1ir1vzqwzkdy7mvjthZ ~]#

利用此技术可以编写一个脚本,生成能标识脚本运行时间的日志信息

bash 复制代码
[root@iZbp1ir1vzqwzkdy7mvjthZ ~]# cat position.sh
#! /bin/bash

value=$[ ${10} * ${11} ]
echo "第10个参数是${10}"
echo "第11个参数是${11}"
echo $value
name=$(basename $0)
echo "脚本名是$name"
echo "The $name ran at $(date)" >> ~/scripttrack.log
[root@iZbp1ir1vzqwzkdy7mvjthZ ~]# date
Fri May 10 15:42:54 CST 2024
[root@iZbp1ir1vzqwzkdy7mvjthZ ~]# ./position.sh 1 2 3 4 5 6 7 8 9 10 12
第10个参数是10
第11个参数是12
120
脚本名是position.sh
[root@iZbp1ir1vzqwzkdy7mvjthZ ~]# ll
total 12
-rwxr--r-- 1 root root  210 May 10 15:42 position.sh
drwxr-xr-x 2 root root 4096 May  9 23:36 script
-rw-r--r-- 1 root root   52 May 10 15:43 scripttrack.log
[root@iZbp1ir1vzqwzkdy7mvjthZ ~]# cat scripttrack.log
The position.sh ran at Fri May 10 15:43:01 CST 2024
[root@iZbp1ir1vzqwzkdy7mvjthZ ~]#

二、跟踪参数

特殊变量 #** 含有脚本运行时携带的命令行参数的个数,**{!#} 代表最后一个位置变量,\*** 和 **@ 各自包含了所有的命令行参数。\*** 将所有参数视为一个整体,**@ 会将所有命令行参数视为同一字符串中的多个独立的单词,可以用 for 遍历所有参数。

bash 复制代码
[root@iZbp1ir1vzqwzkdy7mvjthZ ~]# cat params.sh
#! /bin/bash
echo
echo "\$*用法:$*"
count=1
for param in "$*"
do
  echo "\$* #$count = $param"
  count=$[ $count + 1 ]
done

########
echo
echo "\$@用法:$@"
count=1
for param in "$@"
do
  echo "\$@ #count = $param"
  count=$[ $count +1 ]
done
[root@iZbp1ir1vzqwzkdy7mvjthZ ~]# bash params.sh 1 2 3

$*用法:1 2 3
$* #1 = 1 2 3

$@用法:1 2 3
$@ #count = 1
$@ #count = 2
$@ #count = 3
[root@iZbp1ir1vzqwzkdy7mvjthZ ~]#

三、移动参数

shift命令会根据命令行参数的相应位置进行移动,默认情况下会将每个位置的变量值都向左移动一个位置。因此,变量 $3 的值会移入 $2 ,变量 $2 的值会移入 $1 ,而变量 $1 的值会被删除(变量 $0 是脚本名,不会变)。这是遍历命令行参数的另一种好方法,尤其在不知道到底有多少参数的时候。

bash 复制代码
[root@iZbp1ir1vzqwzkdy7mvjthZ ~]# cat shift.sh
#! /bin/bash
echo
echo "shift的用法"
count=1
while [ -n "$1" ]
do
  echo "#$count = $1"
  count=$[ $count + 1 ]
  shift
done
[root@iZbp1ir1vzqwzkdy7mvjthZ ~]# bash shift.sh ab cd 12 34 df

shift的用法
#1 = ab
#2 = cd
#3 = 12
#4 = 34
#5 = df
[root@iZbp1ir1vzqwzkdy7mvjthZ ~]#

四、处理选项

4.1 查找选项

在命令行中,选项紧跟在脚本名之后,就跟其他命令行参数一样 。你可以像处理命令行参数一样处理命令行选项

4.1.1 处理简单选项

在提取单个参数时,使用 case 语句来判断某个参数是否为选项

bash 复制代码
[root@iZbp1ir1vzqwzkdy7mvjthZ ~]# vim option.sh
[root@iZbp1ir1vzqwzkdy7mvjthZ ~]# cat option.sh
#! /bin/bash
echo
while [ -n "$1" ]
do
  case "$1" in
        -a) echo "a是一个选项";;
        -b) echo "b是一个选项";;
        -c) echo "c是一个选项";;
        *) echo "$1不是一个选项";;
  esac
  shift
done
echo
exit
[root@iZbp1ir1vzqwzkdy7mvjthZ ~]# bash option.sh -b -a e -f

b是一个选项
a是一个选项
e不是一个选项
-f不是一个选项

[root@iZbp1ir1vzqwzkdy7mvjthZ ~]#

4.1.2 分离参数和选项

经常会碰到同时使用选项和参数的情况。在Linux系统中,处理这个问题的标准做法是使用特殊字符将两者分隔,该字符告诉脚本选项何时结束,普通字符何时开始。在Linux系统中这个特殊字符是双连字符(--)。

bash 复制代码
[root@iZbp1ir1vzqwzkdy7mvjthZ ~]# cat option.sh
#! /bin/bash
echo
while [ -n "$1" ]
do
  case "$1" in
        -a) echo "-a是一个选项";;
        -b) echo "-b是一个选项";;
        -c) echo "-c是一个选项";;
        --) shift
            break;;
        *) echo "$1不是一个选项";;
  esac
  shift
done
#
echo
count=1
for param in "$@"
do
  echo "参数#$count = $param"
  count=$[ $count + 1 ]
done
echo
exit
[root@iZbp1ir1vzqwzkdy7mvjthZ ~]# bash option.sh -b -a -d -- 1 21 6

-b是一个选项
-a是一个选项
-d不是一个选项

参数#1 = 1
参数#2 = 21
参数#3 = 6

[root@iZbp1ir1vzqwzkdy7mvjthZ ~]#

在遇到双连字符时,脚本会用break命令跳出while循环。由于提前结束了循环,因此需要再加入另一个shift命令将双连字符移除位置变量

4.1.3 处理含值的选项

有些选项需要一个额外的参数值,像下面这样

bash 复制代码
[root@iZbp1ir1vzqwzkdy7mvjthZ ~]# cat option.sh
#! /bin/bash
echo
while [ -n "$1" ]
do
  case "$1" in
        -a) echo "-a是一个选项";;
        -b) echo "-b是一个选项"
            echo "参数$2"
            shift ;;
        -c) echo "-c是一个选项";;
        --) shift
            break;;
        *) echo "$1不是一个选项";;
  esac
  shift
done
#
echo
count=1
for param in "$@"
do
  echo "参数#$count = $param"
  count=$[ $count + 1 ]
done
echo
exit
[root@iZbp1ir1vzqwzkdy7mvjthZ ~]# bash option.sh -b test -a -d -- 1 21 6

-b是一个选项
参数test
-a是一个选项
-d不是一个选项

参数#1 = 1
参数#2 = 21
参数#3 = 6

[root@iZbp1ir1vzqwzkdy7mvjthZ ~]#

在这个例子中,case语句定义了3个要处理的选项。-b选项还需要一个额外的参数。由于要处理的选项位于 $1 ,因此额外的参数值就应该位于 $2 ,因为选项占用了两个位置,所以还需要使用 shift 命令多移动一次。

五、选项标准化

现在shell脚本已经拥有处理命令行选项的基本能力了,但是当你想合并多个选项时,脚本就不管用了。

5.1 使用 getopt 命令

5.1.1 命令格式

getopt 命令可以接受一系列任意形式的命令行选项和参数,并自动将其转换为适当格式。getopt 格式如下:getopt optstring parameters

首先,在 optstring 中列出要在脚本中用到的每个命令行选项字母。然后,在每个需要参数值的选项字母后面加一个冒号。如下:

bash 复制代码
[root@iZbp1ir1vzqwzkdy7mvjthZ ~]# getopt ab:cd -a -b value -cd 1 2
 -a -b value -c -d -- 1 2
[root@iZbp1ir1vzqwzkdy7mvjthZ ~]#

如果 optstring 未包含制定的选项,则会输出一条错误信息,如果想忽略,可以使用 -q 选项。

5.1.2 在脚本中使用getopt

1)可以在脚本中使用 getopt 格式化脚本所携带的任何命令行选项或参数。难点在于使用 getopt 格式化版本替换已有的命令行选项和参数。这得求助于 set 命令;

2)set 命令有个选项是双连字符(--),可以将位置变量的值替换为 set 命令所指定的值;

3)具体做法是将脚本的命令行参数传递给getopt命令,然后将getopt命令的输出传递给set命令,用getopt格式化后的命令行参数来替换原始的命令行参数,如下所示:

bash 复制代码
set -- $(getopt -q ab:cd "$@")

现在,位置变量原先的值会被getopt命令的输出替换掉,后者已经是格式化好的命令行参数。

bash 复制代码
[root@iZbp1ir1vzqwzkdy7mvjthZ ~]# cat option.sh
#! /bin/bash

set -- $(getopt -q ab:cd "$@")
echo
while [ -n "$1" ]
do
  case "$1" in
        -a) echo "-a是一个选项";;
        -b) echo "-b是一个选项"
            echo "参数$2"
            shift ;;
        -c) echo "-c是一个选项";;
        --) shift
            break;;
        *) echo "$1不是一个选项";;
  esac
  shift
done
#
echo
count=1
for param in "$@"
do
  echo "参数#$count = $param"
  count=$[ $count + 1 ]
done
echo
exit
[root@iZbp1ir1vzqwzkdy7mvjthZ ~]# bash option.sh -ab bvalue 12 3

-a是一个选项
-b是一个选项
参数bvalue

参数#1 = 12
参数#2 = 3

[root@iZbp1ir1vzqwzkdy7mvjthZ ~]#

5.2 使用getopts命令

getopt 命令存在一个问题,如下代码所示:就是不擅长处理带空格和引号的参数值。它会将空格当做参数值的分隔符,而不是根据双引号将二者当做一个参数。这是 getopts就派上用场了。

bash 复制代码
[root@iZbp1ir1vzqwzkdy7mvjthZ ~]# bash option.sh -ab bvalue 12 3 -c

-a是一个选项
-b是一个选项
参数bvalue
-c是一个选项

参数#1 = 12
参数#2 = 3

[root@iZbp1ir1vzqwzkdy7mvjthZ ~]# bash option.sh -ab bvalue "12 3" -c

-a是一个选项
-b是一个选项
参数bvalue
-c是一个选项

参数#1 = 12
参数#2 = 3

getopts 命令格式如下:

bash 复制代码
getopts optstring variable

1) optstring 值与 getopt 命令中使用的类似。有效的选项字母会在 optstring 中列出,如果选项要求参数,就在其后面加一个冒号。不想显示错误消息的话,可以在 optstring 之前加一个冒号。getopts 会将当前参数保存在命令行中定义的varibales中。

2)getopts 会用到两个环境变量。如果选项需要加带参数值,那么 OPTARG环境变量保存的就是这个值。OPTIND 环境变量保存着参数列表中 getopts 正在处理的参数位置。这样在处理完当前选项之后就能继续处理其他命令行参数了。

bash 复制代码
[root@iZbp1ir1vzqwzkdy7mvjthZ ~]# cat getopts.sh
#! /bin/bash
echo
while getopts :ab:cd opt
do
  case "$opt" in
        a) echo "-a是选项";;
        b) echo "-b是一个选项"
           echo "-b选项的参数是$OPTARG";;
        c) echo "-c是一个选项";;
        *) echo "未知选项$opt";;
  esac
done
exit
[root@iZbp1ir1vzqwzkdy7mvjthZ ~]# bash getopts.sh -ab2

-a是选项
-b是一个选项
-b选项的参数是2
[root@iZbp1ir1vzqwzkdy7mvjthZ ~]#

1) 在解析命令行选项时,getopts 会移除起始的连字符,所以 case 语句中不用连字符;

2)可以在参数值中加空格;

3)可以将选项字母和参数值写在一起,两者之间不加空格;

4)在处理每个选项时,getopts 会将OPTIND 环境变量的值增1.处理完选项后,可以使用shift命令和OPTIND值来移动参数。

bash 复制代码
[root@iZbp1ir1vzqwzkdy7mvjthZ ~]# cat getopts.sh
#! /bin/bash
echo
while getopts :ab:cd opt
do
  case "$opt" in
        a) echo "-a是选项";;
        b) echo "-b是一个选项"
           echo "-b选项的参数是$OPTARG";;
        c) echo "-c是一个选项";;
        *) echo "未知选项$opt";;
  esac
done
#
shift $[ $OPTIND -1 ]
echo
count=1
for param in "$@"
do
  echo "参数#$count = $param"
  count=$[ $count + 1 ]
done
exit

六、获取用户输入

尽管命令行选项和参数是从脚本用户处获取输入的一种重要方式,但有时候脚本还需要更多

的交互性。为此,bash shell 提供了 read 命令。

6.1 read读取

bash 复制代码
read -p "Please enter your age:" age
read -t 5 -p "Please enter your age:" age # 超时
read -s "Enter your password:" password # 无显示输出

6.2 从文件中读取

bash 复制代码
[root@iZbp1ir1vzqwzkdy7mvjthZ ~]# cat read.sh
#! /bin/bash
echo
count=1
cat ~/getopts.sh | while read line
do
  echo "#$count = $line"
  count=$[ $count + 1 ]
done
echo "处理完文本的所有行"
exit
[root@iZbp1ir1vzqwzkdy7mvjthZ ~]# bash read.sh

#1 = #! /bin/bash
#2 = echo
#3 = while getopts :ab:cd opt
#4 = do
#5 = case "$opt" in
#6 = a) echo "-a是选项";;
#7 = b) echo "-b是一个选项"
#8 = echo "-b选项的参数是$OPTARG";;
#9 = c) echo "-c是一个选项";;
#10 = *) echo "未知选项$opt";;
#11 = esac
#12 = done
#13 = #
#14 = shift $[ $OPTIND -1 ]
#15 = echo
#16 = count=1
#17 = for param in "$@"
#18 = do
#19 = echo "参数#$count = $param"
#20 = count=$[ $count + 1 ]
#21 = done
#22 = exit
处理完文本的所有行
[root@iZbp1ir1vzqwzkdy7mvjthZ ~]#

while 循环会持续通过read命令处理文件中的各行,直到read命令以非0退出状态码退出

相关推荐
学Linux的语莫29 分钟前
Ansible使用简介和基础使用
linux·运维·服务器·nginx·云计算·ansible
踏雪Vernon39 分钟前
[OpenHarmony5.0][Docker][环境]OpenHarmony5.0 Docker编译环境镜像下载以及使用方式
linux·docker·容器·harmonyos
学Linux的语莫1 小时前
搭建服务器VPN,Linux客户端连接WireGuard,Windows客户端连接WireGuard
linux·运维·服务器
legend_jz1 小时前
【Linux】线程控制
linux·服务器·开发语言·c++·笔记·学习·学习方法
Komorebi.py1 小时前
【Linux】-学习笔记04
linux·笔记·学习
黑牛先生1 小时前
【Linux】进程-PCB
linux·运维·服务器
友友马1 小时前
『 Linux 』网络层 - IP协议(一)
linux·网络·tcp/ip
猿java2 小时前
Linux Shell和Shell脚本详解!
java·linux·shell
A.A呐3 小时前
【Linux第一章】Linux介绍与指令
linux
Gui林3 小时前
【GL004】Linux
linux