shell脚本编程笔记(五)—— 输入处理

一、 命令行参数

特殊环境变量 含义
$1 $2..$9,${10}... 表示脚本的第n个参数

$0

basename $0

脚本名(含路径)

脚本名(不含路径)

$#

${!#}

参数总数

最后一个参数值

$*

将所有参数作为一个字符串保存

$@ 将n个参数作为n个字符串保存

向shell脚本传递数据最基本的方法是使用命令行参数,在脚本运行时指定参数,例如:

./add.sh 10 30
./output.sh 'Hello World' #带空格的参数需加引号,单双引号均可

shell提供位置参数(一组特殊环境变量集合),分别用$1 $2..$9表示第一至第九个参数,10之后写法为${10}。

有两个比较特殊的参数$0和$#,$0表示执行的脚本名(含路径),$#表示参数的个数

如果只想获取脚本名不需要路径,可以使用basename命令

当脚本需要输入参数才能正常工作时,应该使用 if [ -n $1] 或者 if [ $# = 2] 判断参数数量是否正确。

你可能会想,既然$#表示参数的个数,${$#}是不是就代表最后一个参数的值?实际上不是,shell不允许在{}中使用$符。正确的写法应该是${!#}。

想要获取所有命令行参数,可以使用$*或$@,$*将所有参数视为一个字符串,$@则将每个参数视为一个字符串。

#!/bin/bash
echo "print each param from \"\$*\""
for var in "$*"
do
    echo "$var"
done
echo "print each param from \"\$@\""
for var in "$@"
do
    echo "$var"
done

运行脚本

./test.sh a b c d
print each param from "$*"
a b c d
print each param from "$@"
a
b
c
d

二、 移动变量 shift

shift命令默认会将每个变量向左移一位(相当于shift 1),$3->$2,$2->$1,并删除原$1的值。当你不确定到底会有几个参数时,这是个好办法,你可以只操作第一个变量,移动参数,然后继续操作新的第一个变量。

#!/bin/bash

# 找出文件(可指定多个文件名)中长度最长的单词,$1为文件名
while [[ -n $1 ]]  # 参数不为空,即还有待查找文件
do
    if [[ -r $1 ]];then # 文件存在且有读权限
        max_word=
        max_len=0
        for i in $(strings $1) # strings程序(包含在binutils包中)为每一个文件产生一个可读的文本格式的words列表
        do
            len=$(echo $i | wc -c) # wc -c统计字符数,即计算每个单词长度
            if ((len > max_len));then
                max_len=$len
                max_word=$i
            fi
        done
        echo "$1:'$max_word' ($max_len characters)"
    fi
    shift # 参数向左偏移,即开始查找下一个文件
done

也可以利用 shift n 指定每个变量向左移几位

#! /bin/bash

while [ -n "$1" ] # 加双引号表示强制变量为字符串格式,对于字符串的比较,变量取值一定要加双引号
do
echo $1
shift 2
done

 

三、 选项处理

1. 单独选项

有时脚本后不仅有参数,还有选项,例如

./mypara.sh -a -b param1 -d

最简单的可以使用case处理选项,确定哪些选项后可能有参数,会有几个参数,在对应case的中处理参数。

#! /bin/bash

# 假设选项有-a -b -c,仅-b后会有1个参数
while [ -n "$1" ]
do
  case "$1" in
     -a) echo "Found the -a option";;
     -b) param="$2"
         echo "Found the -b option,with parameter $param"
         shift ;; #参数多占一位,需要挪走
     -c) echo "Found the -c option";;
     --) shift
      break ;;
      *) echo "$1 is not the an option";;
  esac
  shift
done

 

2. 合并选项与getopt命令

合并选项例如 ll -rth,这时前面的方法就没法解决问题,需要使用到 getopt 命令。

getopt 能够识别一系列任意形式的选项和参数,并自动将它们转为适当格式。

getopt optstring parameters

optstring 是其中的关键,它定义了命令行有效的选项字母,以及哪些选项需要参数(在字母后加:),例如:

getopt ab:cd -a -b test1 -cd test2 test3
#选项有-a -b -c -d,-b后有:说明-b后面会有参数

输出会是转换后的格式,其中test2 test3被识别为额外参数,用--分隔开

如何在脚本中使用getopt 命令?可以将getops命令输出(格式化后的参数)传给set,set -- 命令会将命令行参数替换成set命令的命令行值。

#/bin/bash
###################################
# Extract command line options & values with getopt

set -- $(getopt -q ab:cd "$@")
 
while [ -n "$1" ]
do
  case "$1" in
  -a) echo "Found the -a option" ;;
  -b) param="$2"
      echo "Found the -b option, with parameter value $param"
      shift ;;
  -c) echo "Found the -c option" ;;
  --) shift
      break ;;
   *) echo "$1 is not option";;
esac

  shift
done

#输出额外参数
count=1
for param in "$@"
do 
  echo "Parameter #$count: $param"
  count=$[ $count + 1 ]
done

你会发现这个脚本跟前面整体差别不大,但它提供了合并选项的处理。

但是,getopt 命令并不擅长处理带空格和引号的参数值,例如

./mypara.sh -a -b param1 -c "test1 test2" test3

可以看到它并没有将"test1 test2"当作整体处理,只是用空格作分隔符。

 

3. 更强大的getopts命令

getopts基本上是一个增强版:

  • getopt 命令处理选项和命令行后只生成一个格式化的输出,需要用set转换,而getopts不再需要。
  • getopt 命令不擅长处理带空格和引号的参数值,而getopts可以
# 用法与getopt基本相同
getopts optstring variables
# 要忽略错误消息,需在optstring前加冒号:,即
getopts :optstring variables
# variables中会保存当前参数

注意getopts解析后的命令行选项不带-,使用case匹配时也不需加,$OPTARG中存储选项后参数

#!/bin/bash
###################################
# simple demonstration of the getopts command
#
echo 
while getopts :ab:c opt
do
  case "$opt" in
  a) echo "Found the -a option" ;;
  b) echo "Found the -b option, with parameter value $OPTARG" ;; # $OPTARG中存储选项后参数
  c) echo "Found the -c option" ;;
  *) echo "Unknown option: $opt" ;;
  esac
done

可以将选项和参数合在一起,中间不加空格;还可以将所有未定义参数统一输出成问号?

 

四、 获得用户输入

read命令可从标准输入(键盘)或另一个文件中接收输入,并保存到一个变量,下面来看其常用用法。

1. 基本读取

最简单的用法,-p会显示指定的输入提示符

#! /bin/bash

read -p "Input your name: " name
echo "Hello $name"

可以输入多个参数

#! /bin/bash

read -p "Input your name and age: " name age
echo "Hello $name,age $age"

如果变量数>输入参数,会从前往后分配,后面的变量为空;如果变量数<输入参数,多余的参数会全存在最后一个变量

若不指定参数,read会将接收到的数据存入特殊变量REPLY中

#! /bin/bash

read -p "Input your name: "
echo "Hello $REPLY"

 

2. 超时设置 -t

如果用户一直不输入,read默认会一直等,-t选项可以设置定时器指定等待秒数,超时后read命令会返回非0退出状态码。

#! /bin/bash

if read -t 5 -p "Input your name: "
then
   echo "Hello $REPLY"
else
   echo #避免Timeout直接输出在提示语句后面
   echo "Timeout"
fi

 

3. 输入指定字符数后自动退出 -n

read命令的 “-n数字” 选项可指定在用户输入指定字符数后自动退出,无需按回车。

#! /bin/bash

read -n1 -p "Please input a character: "
echo
echo "Your input is $REPLY"

 

4. 隐藏输入 -s

有时用户希望将输入传入脚本但又不在屏幕上显示,典型情况就是输密码。read命令的-s选项就可以做到(实际也会显示,只是read命令将其改为了与屏幕底色相同)。

#! /bin/bash

read -s -p "Please input your password: "
echo
echo "Is your password really $REPLY"

 

5. 从文件中读取

每次调用read命令会从文件中读取一行文本,当文件中再没有内容时,read会以非0状态码退出。最常见的方法是cat文件,将结果通过管道传给通过while命令的read命令。

#! /bin/bash
# reading data from a file

count=1 
cat breaktest.sh | while read line
do
  echo "Line $count: $line"
  count=$[ $count + 1 ]
done
echo "--- end of file ---"

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章