验证Bash脚本的参数


95

我想出了一个基本的文件夹,可以帮助您自动删除一些不需要的文件夹。

#!/bin/bash
rm -rf ~/myfolder1/$1/anotherfolder
rm -rf ~/myfolder2/$1/yetanotherfolder
rm -rf ~/myfolder3/$1/thisisafolder

像这样引起:

./myscript.sh <{id-number}>

问题是,如果您忘记键入id-number (就像我当时所做的那样),那么它可能会删除很多您确实不想删除的内容。

有没有一种方法可以向命令行参数添加任何形式的验证?以我为例,最好检查一下:a)是否有一个参数,b)是数字,c)文件夹是否存在;在继续执行脚本之前。

Answers:


158
#!/bin/sh
die () {
    echo >&2 "$@"
    exit 1
}

[ "$#" -eq 1 ] || die "1 argument required, $# provided"
echo $1 | grep -E -q '^[0-9]+$' || die "Numeric argument required, $1 provided"

while read dir 
do
    [ -d "$dir" ] || die "Directory $dir does not exist"
    rm -rf "$dir"
done <<EOF
~/myfolder1/$1/anotherfolder 
~/myfolder2/$1/yetanotherfolder 
~/myfolder3/$1/thisisafolder
EOF

编辑:我首先错过了有关检查目录是否存在的部分,所以我在完成脚本的过程中添加了该部分。此外,还解决了评论中提出的问题;修复了正则表达式,从切换==eq

据我所知,这应该是可移植的,符合POSIX的脚本;它不使用任何bashisms,这实际上很重要,因为/bin/sh在Ubuntu上实际上已经dash不是了bash


记住设置+ e并使用'-eq'而不是'=='进行整数比较

将其更改为-eq; + e在这里给您带来什么?
布赖恩·坎贝尔

我在回答中发现了两件事,您可能也想在其中解决:首先,SO提灯器为$#疯狂(将其作为注释器处理)。我做了“ $#”来解决它。第二,正则表达式也匹配“ foo123bar”。我通过做^ [0-9] + $来解决它。您还可以通过用grep的-x选项修复
litb -约翰内斯绍布

1
@ojblass我错过了他要问的一项测试。添加表示还添加了要测试的目录,这极大地扩大了答案的大小,因为它们不适合一行。您能建议一种更紧凑的测试每个目录是否存在的方法吗?
布赖恩·坎贝尔

1
根据下面的@Morten Nielsen的回答,grep'$ [0-9] + ^'确实看起来很奇怪。它不是'^ [0-9] + $'吗?
马丁·贾库比克2011年

21

尽管shby 的解决方案Brian Campbell虽然高贵且执行得当,但仍存在一些问题,所以我想我应该提供自己的bash解决方案。

一个的问题sh

  • 波浪号~/foo不会扩展到heredocs中的主目录。而且无论是在read语句中读取它还是在rm语句中引用它时,都不会。这意味着您会得到No such file or directory错误。
  • 分叉grep等基本操作是愚蠢的。尤其是在使用a脚的外壳来避免bash的“沉重”重量时。
  • 我还注意到了一些引用问题,例如围绕他的参数扩展echo
  • 尽管很少见,但该解决方案无法处理包含换行符的文件名。(几乎没有任何解决方案sh可以应付这些问题-这就是为什么我几乎总是偏爱bash它,它在防弹性方面要好得多,并且如果使用得当,则更难利用)。

虽然是的,使用/bin/shhashbang意味着您必须bash不惜一切代价避免使用ism ,但您可以使用所有bash喜欢的ism ,即使是在Ubuntu上,也可以诚实使用#!/bin/bash

因此,这是一种bash更小,更清洁,更透明,可能“更快”且更防弹的解决方案。

[[ -d $1 && $1 != *[^0-9]* ]] || { echo "Invalid input." >&2; exit 1; }
rm -rf ~/foo/"$1"/bar ...
  1. 各地注意引号$1rm声明!
  2. -d检查也将失败,如果$1是空的,所以,在一个人的两个检查。
  3. 由于某种原因,我避免使用正则表达式。如果必须=~在bash中使用,则应将正则表达式放入变量中。无论如何,像我这样的glob始终是首选,并在更多bash版本中得到支持。

1
那么,那块重$1 != *[^0-9]*击是特定的吗?
grinch 2014年


13

不如上面的答案那么防弹,但仍然有效:

#!/bin/bash
if [ "$1" = "" ]
then
  echo "Usage: $0 <id number to be cleaned up>"
  exit
fi

# rm commands go here


9

测试(man test)的手册页提供了所有可用的运算符,您可以在bash中将它们用作布尔运算符。就像在其他任何编程语言中一样,在脚本(或函数)的开头使用这些标志进行输入验证。例如:

if [ -z $1 ] ; then
  echo "First parameter needed!" && exit 1;
fi

if [ -z $2 ] ; then
  echo "Second parameter needed!" && exit 2;
fi

8

使用“ -z”测试空字符串,使用“ -d”检查目录。

if [[ -z "$@" ]]; then
    echo >&2 "You must supply an argument!"
    exit 1
elif [[ ! -d "$@" ]]; then
    echo >&2 "$@ is not a valid directory!"
    exit 1
fi

2
为什么需要双[[]]?
vehomzzz 2012年

5

您可以通过执行以下操作来紧凑地验证点a和b:

#!/bin/sh
MYVAL=$(echo ${1} | awk '/^[0-9]+$/')
MYVAL=${MYVAL:?"Usage - testparms <number>"}
echo ${MYVAL}

这给了我们...

$ ./testparams.sh 
Usage - testparms <number>

$ ./testparams.sh 1234
1234

$ ./testparams.sh abcd
Usage - testparms <number>

此方法在sh中应该可以正常工作。


2

一个衬里Bash参数验证,带有和不带有目录验证

这是一些对我有用的方法。您可以在全局脚本名称空间中使用它们(如果在全局名称空间中,则不能引用函数内置变量)

快速又脏的一根内胆

: ${1?' You forgot to supply a directory name'}

输出:

./my_script: line 279: 1: You forgot to supply a directory name

Fancier-提供功能名称和用法

${1? ERROR Function: ${FUNCNAME[0]}() Usage: " ${FUNCNAME[0]} directory_name"}

输出:

./my_script: line 288: 1:  ERROR Function: deleteFolders() Usage:  deleteFolders directory_name

添加复杂的验证逻辑,而不会影响当前功能

在接收自变量的函数或脚本中添加以下行。

: ${1?'forgot to supply a directory name'} && validate $1 || die 'Please supply a valid directory'

然后,您可以创建一个类似以下内容的验证函数

validate() {

    #validate input and  & return 1 if failed, 0 if succeed
    if [[ ! -d "$1" ]]; then
        return 1
    fi
}

一个die函数,该函数会在失败时中止脚本

die() { echo "$*" 1>&2 ; exit 1; }

对于其他参数,只需添加其他行,即可复制格式。

: ${1?' You forgot to supply the first argument'}
: ${2?' You forgot to supply the second argument'}

1

旧职位,但我认为我仍然可以贡献力量。

可以说脚本不是必需的,并且可以从命令行执行对通配符的一定容忍度。

  1. 在任何地方都可以匹配。让我们删除所有出现的子“文件夹”

    $ rm -rf ~/*/folder/*
  2. 外壳迭代。让我们用一行删除特定的前和后文件夹

    $ rm -rf ~/foo{1,2,3}/folder/{ab,cd,ef}
  3. Shell迭代+ var(已通过BASH测试)。

    $ var=bar rm -rf ~/foo{1,2,3}/${var}/{ab,cd,ef}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.