验证Bash脚本的参数

时间:2009-03-31 00:29:45

标签: validation bash shell

我想出了一个基本的文件,以帮助自动删除多个文件夹,因为它们不再需要。

#!/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)该文件夹存在;在继续编写脚本之前。

10 个答案:

答案 0 :(得分:148)

#!/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,这实际上很重要,因为这些天Ubuntu上的/bin/sh实际上是dash,而不是bash

答案 1 :(得分:20)

sh Brian Campbell解决方案虽然高尚且执行得很好,但有一些问题,所以我想我会提供自己的bash解决方案。

sh的问题:

  • ~/foo中的代字号未扩展到heredocs内的homedirectory。当它被read语句读取或在rm语句中引用时都不会。这意味着您将收到No such file or directory错误。
  • 分离grep这样的基本操作是愚蠢的。特别是当你使用蹩脚的外壳来避免重击“重”时。
  • 我还注意到一些引用问题,例如他echo中的参数扩展问题。
  • 虽然很少见,但该解决方案无法处理包含换行符的文件名。 (sh中几乎没有任何解决方案可以应对它们 - 这就是为什么我几乎总是喜欢bash,它更具防弹性,并且在使用时更难以利用。)

虽然,是的,使用/bin/sh代表你的主题意味着你必须不惜一切代价避免bash主义,你可以使用你喜欢的所有bash主义,甚至在Ubunutu或什么时候使用你是诚实的,并把#!/bin/bash放在最顶层。

所以,这是一个bash解决方案,它更小,更清洁,更透明,可能“更快”,更具防弹性。

[[ -d $1 && $1 != *[^0-9]* ]] || { echo "Invalid input." >&2; exit 1; }
rm -rf ~/foo/"$1"/bar ...
  1. 请注意$1声明中rm周围的引号!
  2. 如果-d为空,则$1检查也会失败,因此在一个检查中进行了两次检查。
  3. 我出于某种原因避免使用正则表达式。如果必须在bash中使用=~,则应将正则表达式放在变量中。在任何情况下,像我这样的球体总是更受欢迎,并且支持更多的bash版本。

答案 2 :(得分:14)

我会使用bash的[[

if [[ ! ("$#" == 1 && $1 =~ ^[0-9]+$ && -d $1) ]]; then 
    echo 'Please pass a number that corresponds to a directory'
    exit 1
fi

我发现this faq是一个很好的信息来源。

答案 3 :(得分:12)

不像上述答案那样具有防弹性,但仍然有效:

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

# rm commands go here

答案 4 :(得分:10)

使用set -u会导致任何未设置的参数引用立即使脚本失败。

http://www.davidpashley.com/articles/writing-robust-shell-scripts.html

答案 5 :(得分: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

答案 6 :(得分:7)

test(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

答案 7 :(得分: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中应该可以正常工作。

答案 8 :(得分: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'}

答案 9 :(得分:1)

老帖子,但我认为无论如何我都可以做出贡献。

可以说一个脚本不是必需的,并且可以从命令行执行对外卡的一些容忍。

  1. 在任何地方匹配。让我们删除任何子文件夹

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

    $ 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}