シェルスクリプトを導入する方法ユーザ入力を受け取ります
よりエキサイティング
- より多くの技術のブログ、会場とIT人材育成と生涯のキャリアの高度なプラットフォーム-オンライントレーニング
ツアー
- スクリプトを実行すると、コマンドラインパラメータは、たとえば、スクリプトに渡すことができ、
./param.sh 1 2 3
shift
左の位置まで右から渡されたコマンド・パラメータは、イテレータのように、shift n
コマンドパラメータは、位置の数は、デフォルトを移動指定することができます1$#
パラメータの総数への直接アクセスをスクリプトに渡すことができ、${!#}
あなたは直接スクリプト内で最後のパラメータに渡されることができます$*
あなたは、スクリプトに直接渡されたすべてのパラメータを得ることができますが、文字列全体の内容を取得します$@
このスクリプトは、すべてのパラメータに、その後の直接アクセスが渡され、文字列パラメータトラバースすることができ、コンテンツを取得することができます- スクリプトを実行する際のオプションは、たとえば、スクリプトに渡すことができ、
./option.sh -a -b -c
getopt
コマンドが着信ルールパラメータとオプション指定したスクリプトを使用することができ、スクリプトの基本的な構文は次のとおりです。set -- $(getopt ab:cd "$@")
getopts
だけでなく、ルールパラメータとスクリプトのオプションを指定するには、コマンドを渡すことができ、また、引数のスペース、引用符をサポートし、スクリプト内の基本的な構文は次のとおりです。getopts ab:cd opt
read
ユーザ入力コマンドを受信するために、ユーザは、スペースで区切られたコンテンツを入力するように、単一入力または複数の入力で指定された変数名によって受信することができますread -p
注文入力プロセスは、スクリプトが1つの文に文と文を受け取るための入力を求めるプロンプトが表示されます簡素化することができますread -t
入力されたタイムアウト時間を指定するためのコマンドread -n
入力コマンドを受信することができる文字の数は、次のステップが自動的に実行される文字の到達の際に数を指定しますread -s
コマンドは、ユーザー入力を非表示にすることができます
14.1コマンドラインパラメータ
- パラメータはシェルスクリプトデータ最も基本的な方法に渡されるコマンドラインを使用します
14.1.1パラメータを読み込み、
- シェルスクリプトデータにコマンドラインパラメータを渡すことによってマークされます位置パラメータ(位置パラメータ)
- シェルスクリプトを実行するとき0名を使用しているため、1から始まるインデックス位置パラメータ
- 例えば
./input.sh 1 2
、その後input.sh
、$0
値は./input.sh
、$1
値が1で、$2
値2
- 例えば
- 10日の初めから直接使用することはできませんので、最高10以下にコマンドライン引数を経由して渡されたパラメータの数
$1
、このような形式のパラメータを取得するために、および使用する必要${10}
なパラメータを得るために、この形式を - 以下に示すように、それを証明する簡単な例を書きます
14.1.2使用ベース名はスクリプト名を読んで
- よるが、
$0
実行時にスクリプト名を取得することができますが、スクリプトが運ぶ場合は、対応するパスが実行され、それはまたに表示されます$0
パラメータ - 使用は
basename
除外することができ$0
、以下に示すように、純粋なスクリプト名で、その結果、スクリプト名のパラメータ外部コンテンツを
14.1.3テストパラメータ
- コマンドラインパラメータはオプションであるため、スクリプトを実行する場合、以下に示すように、最善であるかを決定するためのスクリプトコマンドラインパラメータで使用され、添加することができる添加しなくてもよいです
- 使用して
test
、コマンドラインパラメータでコマンドを評価され、直接使用することはできません$1
、我々は必要な"$1"
正しいパラメータを取得します
- 使用して
14.2特殊変数パラメータ
14.2.1パラメトリック統計
- 使用
$#
下図のように出力することができるスクリプトは、実行時にパラメータの数が渡さ
- 今、
$#
あなたがして、理論的に使用し、渡された引数の数を返すことができ${$#}
ますが、最後の引数の内容を取得することができますが、それはありません - 迅速に至る最後のパラメータのコンテンツのニーズを取得し
${!#}
、以下に示すように、
14.2.2グラブすべてのデータ
- 使用して、
$*
コマンドラインから渡されたすべてのパラメータを取得することができ、全体的に、これらのパラメータは、文字として保存されます- 将传入的所有参数作为一个值
- 使用
$@
也可以获取通过命令行传入的所有参数,这些参数则会被作为一个可以循环的字符串被保存- 将传入的每个参数作为单独的值
- 写一个简单的例子演示一下,如下图
- 在对两者采用了相同遍历方式的操作之后,通过
$*
得到的是一个参数,而$@
得到的是 5 个参数
- 在对两者采用了相同遍历方式的操作之后,通过
14.3 使用 shift 命令移动变量
- 使用
shift
命令时,默认会将每个参数变量向左移动一个位置,也就是参数$3 -> $2
- 类似于 Java 中的迭代器
- 而除了参数
$0
,其他的参数的位置在被后续参数替换后,自身就不复存在了- 因为
$0
保存着程序名,所以不会改变
- 因为
- 写一个例子简单演示一下,如下图
- 在
while
循环中每次获取的参数都是$1
,但获取到的内容却能够发生变化 - 这就是因为每次循环后都通过
shift
命令把后续的参数向左移动了一位
- 在
14.3.1 使用 shift 命令移动指定数量的变量
- 使用
shift num
命令可以一次移动多个位置的变量,如下图
14.3.2 使用 shift 命令移动多个位置时,传入的参数必须是位置数的倍数
- 上从图可知,使用
shift 2
命令将参数的位置移动两位,然后脚本在执行的时候传入了 6 个参数 - 这个时候如果只传入 5 个参数,那么脚本就会无限循环,如下图
14.4 处理选项
- 执行脚本时,除了能往脚本中传入命令行参数,还能 在单破折号后面跟字母 往脚本中传递 选项
14.4.1 查找选项
14.4.1.1 处理简单选项
- 当脚本在执行时传入了选项,通过
case
命令进行判断是最好的处理方式,如下图- 为了让传入的多个选项能在一次执行过程中都被执行到,需要先使用一个
while
+shift
对选项进行遍历 - 在每次遍历时都通过
case
命令对选项的具体参数进行判断 - 同时还通过
*)
提供一个默认判断
- 为了让传入的多个选项能在一次执行过程中都被执行到,需要先使用一个
14.4.1.2 分离参数和选项
- 可以通过 双破折号( – ) 将参数和选项进行分离,这样才执行脚本时就可以同时传入两者
- 这里的说法 不是指直接将参数和选项进行混排 ,而是 先传入参数后传入选项,或者先传入选项后传入参数 ,在这两者之间通过双破折号进行分离,如下图
- 才判断选项的
case
命令中,新增了一个--)
分支,用于判断分支的存在 - 当从传入的内容中检索到这个分支时,会先执行一个
shift
,目的是将双破这行这个分隔符跳过去 - 之后再执行一次
break
是为了跳出while
循环,进行后续属于参数遍历的for
循环 - 可以看到,当直接执行
./option-param.sh -a -b -e a b e
时,由于没有检测到--)
分支,所以后续的参数也被识别成了错误的选项 - 但是当使用双破折号将选项和参数分离后,就能顺利遍历到选项和参数了
- 才判断选项的
- 从上述这个例子,其实我们可以思考一下,双破折号真的是作为一个 强语法的特殊符号 来被 shell 识别的吗?很显然不是!
- 我们可以对上述脚本稍作修改,就可以得出下图的效果
- 将上图中的
--)
分支判断部分修改为下图中的-e)
分支判断 - 然后执行上图中第一次执行导致错误结果的语句,却可以得到和上图第二次正确结果一致的输出
- 这说明 双破折号作为参数和选项分隔符只是一个规范上的约定,并不是强语法的特殊符号
- 将上图中的
14.4.1.3 处理带值的选项
- 最复杂的情况就是参数和选项的混合搭配,但是在 shell 脚本中,当参数和选项混排后,对应的参数会被认为是前一个选项的值
- 例如
./option.sh -a str1 -b str2 -c
,这里的str1
就会被认为是-a
选项的值,需要在-a
选项的分支中进行操作 - 写一个简单的例子演示一下,如下图
-a
后续的参数识别之后会执行一次shift
命令的目的是为了将参数跳过
14.4.2 使用 getopt 命令
14.4.2.1 基础语法
getopt
命令用于将通过简化形式传入脚本的命令行参数和选项进行规范化- 比如在执行脚本时使用
./option.sh -abc
,如果脚本中存在getopt
命令,就可以被转换为-a -b -c
- 这样就可以被脚本正确的识别,同时用户的输入也得到了简化
- 比如在执行脚本时使用
- 在脚本执行时,会先将这些传入脚本的命令行参数和选项自动转换为适当的格式,让脚本在使用这些参数和选项时更加方便
- 比如它可以定义哪些字母选项是有效的,哪些字母选项需要指定参数值
getopt
命令可以在命令行执行执行,虽然这样做没有什么实际意义,但是可以帮助我们熟练一下语法,如下图ab:cd
就是通过getopt
命令指定的参数选项规则,表示当前可以传入abcd
选项,而且选项b
后面存在一个参数-ab test -cd
就是实际传入的参数内容,第二行的输出结果就是对这些传入的内容进行格式化后的结果
- 默认如果传入的内容和规则不匹配,会抛出错误,可以通过
-q
选项忽略错误,如下图
14.4.2.2 在脚本中使用
getopt
命令自然是要在脚本中使用才有意义,而且在脚本中使用的语法基本都是固定的getopt
命令是用来规范参数和选项的,所以肯定是要放在脚本的最前端,这个也是毋庸置疑- 一般只需要在脚本前端加上
set -- $(getopt -q ab:cd "$@")
即可ab:cd
是可变化的部分,指的是具体参数和选项的匹配方式
- 写一个简单的例子演示一下,如下图
- 这个例子是在 centOS 环境下运行的,因为 macOS 中的
set
命令无法按预期执行出效果
- 这个例子是在 centOS 环境下运行的,因为 macOS 中的
- 上图中
case
命令的--)
分支判断是必须添加的,否则在执行命令时最后一个分支会被多判断一次 - 至于具体是为什么多判断一次,可以在执行脚本前添加
sh -x
来对脚本进行 DEBUG ,如下图- 首先在原来的脚本中注释掉
--)
分支 - 然后在与之前相同的脚本执行语句之前加上
sh -x
,之后就可以很清楚的看到脚本的执行过程 - 在执行过程的第一处可以看到,传入的
-ab hello -cd
被解析后,最后多出来一个--
- 于是在脚本的最后,这个
--
就被作为传入的选项执行了 - 这就是为什么如果必须
case
命令添加--)
分支,并且通过shift
+break
命令跳过的原因
- 首先在原来的脚本中注释掉
14.4.3 使用更高级的 getopts
getopts
命令是getopt
命令的复数版本( 其实说是升级版更贴切 )- 关键在于
getopts
可以处理 带空格和引号的参数 ,而getopt
不行
- 关键在于
getopts
比getopt
更优秀的地方在于,每次调用时,只会处理一个参数,并且在处理完所有参数后会返回一个非 0 的退出状态码- 而
getopt
是将参数和选项生成为一个完整的输出
- 而
getopts
也可以忽略错误信息,只需要在参数选项规则之前加上一个冒号即可- 如果某个选项后面需要跟一个参数,这个参数会被存储在
OPTARG
变量中 - 还有一个变量是
OPTIND
,用于存放getopts
当前正在处理的参数位置 - 写一个简单的例子统一演示一下,如下图
- 可以对比一下两个命令在处理相同内容时的区别,如下图
- 首先,
getopts
在处理参数时可以直接加入到while
的条件判断中,这让语法变的更简单易懂 - 其次,
getopts
的case
判断也更简单,每个分支前不需要使用-a)
,而是直接a)
即可 - 用于跳过
--
选项的分支判断在getopts
中也不需要了 - 最后最关键的是,在
while
的每次循环结束之前,不需要使用shift
命令来对参数进行位置移动
- 首先,
- 如果在参数中添加空格,
getopts
也可以很好的处理,如下图
- 如果在参数选项中传入了与定义的规范不匹配的选项,会被输出一个问号,如下图
- 可以看到,
-d
选项因为没有找到对应的分支判断,所以被识别为其他选项,但输出内容中可以正确的显示出来 - 但是
-e
选项因为不在getopts
定义的规范中,所以最后只输出来一个问号
- 可以看到,
- 如果说选项处理完毕后,还需要处理单独的参数列表,则需要在处理参数之前,使用
shift $[ $OPTIND - 1 ]
来跳过参数之前的所有选项,如下图shift $[ $OPTIND - 1 ]
的意思如果不理解的话,这里简单解释一下- 首先,
$[ $OPTIND - 1 ]
的意思是用getopts
当前执行的选项的位置数减一,为什么要减一,因为命令行参数的位置从 0 开始的 - 所以就相当于是使用
shift
将传入的参数内容中被getopts
执行过的选项全部跳过
- 如果还是不理解,可以将上述脚本的
shift $[ $OPTIND - 1 ]
语句注释后再使用相同命令执行,如下图- 可以看到,所有的选项都被当做参数输出了
- 可以看到,所有的选项都被当做参数输出了
14.5 将选项标准化
- 脚本的选项虽然没有一个强制的语法规范,但有不少建议性规范,如果能遵守这些规范,会让你编写的脚本更通用,其他人在上手使用你的脚本时,学习门槛也更低
- 下图中提供一些选项的常用含义
14.6 获得用户输入
14.6.1 使用 read 命令进行基本的读取
- 使用
read
命令可以接收用户输入,输入的内容会被放置在后续的变量中,如下图echo -n
表示输入内容后不换行- 可以看到,通过键盘输入的 asing1elife 被存入到变量 name 中
- 使用
read -p
命令可以实现合并输入前提醒语句的效果,如下图- 少了
echo -n
语句,却可以达到一样的输出效果
- 少了
- 如果想讲输入的内容分配给多个变量,只需要将内容使用空格进行分隔,并且在
read
命令后使用多个变量进行接收,如下图
- 如果在输入的时候使用空格分隔了输入内容,但是在
read
命令后没有使用数量与之对应的变量分别接收,那么剩余的输入内容都会被直接存放到最后一个变量中,如下图- 可以看到,在脚本
read-p.sh
中,只有一个变量 name 用于接收输入内容 - 那么所有的输入内容都被存放到这个变量中
- 在脚本
read-multi.sh
中,输入内容通过逗号分隔后有 6 个,但是变量只有 3 个,所以从第 3 个输入开始,之后的所有内容都被存放到了第三个变量中
- 可以看到,在脚本
- 如果不想在输入命令的最后显式的指定一个变量用于内容接收,也可以直接使用特殊环境变量
REPLY
来获取输入内容,如下图REPLY
变量其实就相当于一个默认的接收变量被隐式的放在了read
命令的最后,所以会默认接收所有的输入内容
14.6.2 超时
- 为了防止用户一直不输入,而导致脚本的执行流行被阻断,可以使用
read -t second
来指定一个定时器 - 当输入时间超过指定的时间后,
read
命令会返回一个非 0 的退出状态码,如下图
14.6.2.1 字数控制
- 使用
read -nNum
可以控制输入的字符数,例如read -n1
就是当输入 1 个字符时就开始判断,如下图- 当输入一个字符时,不管输入的是什么,也不需要按回车键,脚本都会直接开始分支判断
- 当输入一个字符时,不管输入的是什么,也不需要按回车键,脚本都会直接开始分支判断
14.6.3 隐藏方式读取
- 使用
read -s
命令可以避免输入的内容出现在显示器上,最典型的例子就是输入密码,如下图- 实际上,数据还是会显示,只是文本的颜色被设置成和终端的背景色一致,所以看不出来
- 实际上,数据还是会显示,只是文本的颜色被设置成和终端的背景色一致,所以看不出来
14.6.4 从文件中读取
- 使用
read
命令可以读取文件中的数据,每次调用都会读取一行文本,当文件中没有剩余内容时,会返回非 0 的退出状态码,如下图- 首先使用
cat read-s.sh
读取文件内容 - 然后通过管道将数据直接传递给
read
命令 read
再将每次读取的行为通过while
进行循环
- 首先使用