验证 Bash 脚本的参数

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/699576/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-09 18:05:36  来源:igfitidea点击:

Validating parameters to a Bash script

validationbashshell

提问by nickf

I came up with a basic one to help automate the process of removing a number of folders as they become unneeded.

我想出了一个基本的方法来帮助自动化删除一些不需要的文件夹的过程。

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

This is evoked like so:

这是这样唤起的:

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

The problem is that if you forget to type in the id-number(as I did just then), then it could potentially delete a lot of things that you really don't want deleted.

问题是,如果你忘记输入id-number(就像我刚才那样),那么它可能会删除很多你真的不想删除的东西。

Is there a way you can add any form of validation to the command line parameters?In my case, it'd be good to check that a) there is one parameter, b) it's numerical, and c) that folder exists; before continuing with the script.

有没有办法可以向命令行参数添加任何形式的验证?就我而言,最好检查一下 a) 有一个参数,b) 它是数字,以及 c) 该文件夹是否存在;在继续脚本之前。

回答by Brian Campbell

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

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

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

edit: I missed the part about checking if the directories exist at first, so I added that in, completing the script. Also, have addressed issues raised in comments; fixed the regular expression, switched from ==to eq.

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

This should be a portable, POSIX compliant script as far as I can tell; it doesn't use any bashisms, which is actually important because /bin/shon Ubuntu is actually dashthese days, not bash.

据我所知,这应该是一个可移植的、符合 POSIX 标准的脚本;它不使用任何 bashisms,这实际上很重要,因为/bin/sh在 Ubuntu 上实际上是dash这些天,而不是bash.

回答by lhunath

The shsolution by Brian Campbell, while noble and well executed, has a few problems, so I thought I'd provide my own bashsolution.

由 的sh解决方案Brian Campbell虽然高尚且执行良好,但存在一些问题,因此我想我会提供自己的bash解决方案。

The problems with the shone:

问题sh之一:

  • The tilde in ~/foodoesn't expand to your homedirectory inside heredocs. And neither when it's read by the readstatement or quoted in the rmstatement. Which means you'll get No such file or directoryerrors.
  • Forking off grepand such for basic operations is daft. Especially when you're using a crappy shell to avoid the "heavy" weight of bash.
  • I also noticed a few quoting issues, for instance around a parameter expansion in his echo.
  • While rare, the solution cannot cope with filenames that contain newlines. (Almost no solution in shcan cope with them - which is why I almost always prefer bash, it's far more bulletproof & harder to exploit when used well).
  • 波浪号~/foo不会扩展到您在heredocs 中的主目录。并且无论是在read语句中读取还是在rm语句中引用时。这意味着你会得到No such file or directory错误。
  • 分叉grep等基本操作是愚蠢的。特别是当您使用蹩脚的外壳来避免 bash 的“重”重量时。
  • 我还注意到一些引用问题,例如围绕他的echo.
  • 虽然很少见,但该解决方案无法处理包含换行符的文件名。(几乎没有任何解决方案sh可以应对它们 - 这就是为什么我几乎总是更喜欢bash,它更防弹并且使用得当时更难利用)。

While, yes, using /bin/shfor your hashbang means you must avoid bashisms at all costs, you can use all the bashisms you like, even on Ubuntu or whatnot when you're honest and put #!/bin/bashat the top.

虽然,是的,/bin/sh用于你的 hashbang 意味着你必须bash不惜一切代价避免 isms ,你可以使用所有bash你喜欢的 isms ,即使在 Ubuntu 或诸如此类的东西上,当你诚实并放在首位时#!/bin/bash

So, here's a bashsolution that's smaller, cleaner, more transparent, probably "faster", and more bulletproof.

所以,这里有一个bash更小、更干净、更透明、可能“更快”、更防弹的解决方案。

[[ -d  &&  != *[^0-9]* ]] || { echo "Invalid input." >&2; exit 1; }
rm -rf ~/foo/""/bar ...
  1. Notice the quotes around $1in the rmstatement!
  2. The -dcheck will also fail if $1is empty, so that's two checks in one.
  3. I avoided regular expressions for a reason. If you must use =~in bash, you should be putting the regular expression in a variable. In any case, globs like mine are always preferable and supported in far more bash versions.
  1. 各地注意引号$1rm声明!
  2. -d检查也将失败,如果$1是空的,所以,在一个人的两个检查。
  3. 我避免使用正则表达式是有原因的。如果必须=~在 bash 中使用,则应将正则表达式放入变量中。在任何情况下,像我这样的 glob 总是更可取,并且在更多的 bash 版本中得到支持。

回答by Johannes Schaub - litb

I would use bash's [[:

我会使用 bash 的[[

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

I found this faqto be a good source of information.

我发现这个常见问题是一个很好的信息来源。

回答by Boiler Bill

Not as bulletproof as the above answer, however still effective:

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

#!/bin/bash
if [ "" = "" ]
then
  echo "Usage: 
if [ -z  ] ; then
  echo "First parameter needed!" && exit 1;
fi

if [ -z  ] ; then
  echo "Second parameter needed!" && exit 2;
fi
<id number to be cleaned up>" exit fi # rm commands go here

回答by shmichael

Use set -uwhich will cause any unset argument reference to immediately fail the script.

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

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

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

回答by whaley

The man page for test (man test) provides all available operators you can use as boolean operators in bash. Use those flags in the beginning of your script (or functions) for input validation just like you would in any other programming language. For example:

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

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

回答by guns

Use '-z' to test for empty strings and '-d to check for directories.

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

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

回答by MattK

You can validate point a and b compactly by doing something like the following:

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

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

$ ./testparams.sh 1234
1234

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

Which gives us ...

这让我们...

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

This method should work fine in sh.

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

回答by AndrewD

one liner Bash argument validation, with and without directory validation

单行 Bash 参数验证,带和不带目录验证

Here are some methods that have worked for me. You can use them in either the global script namespace (if in the global namespace, you can't reference the function builtin variables)

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

quick and dirty one liner

快速而肮脏的一个班轮

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

output:

输出:

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

Fancier - supply function name and usage

Fancier - 提供函数名称和用法

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

output:

输出:

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

Add complex validation logic without cluttering your current function

添加复杂的验证逻辑而不会使您的当前功能混乱

Add the following line within the function or script that receives the argument.

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

validate() {

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

You can then create a validation function that does something like

然后,您可以创建一个验证函数,它执行类似的操作

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

and a die function that aborts the script on failure

以及在失败时中止脚本的 die 函数

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

For additional arguments, just add an additional line, replicating the format.

对于其他参数,只需添加额外的行,复制格式。

$ rm -rf ~/*/folder/*

回答by Xarses

Old post but I figured i could contribute anyway.

旧帖子,但我想无论如何我都可以做出贡献。

A script is arguably not necessary and with some tolerance to wild cards could be carried out from the command line.

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

  1. wild anywhere matching. Lets remove any occurrence of sub "folder"

    $ rm -rf ~/foo{1,2,3}/folder/{ab,cd,ef}
    
  2. Shell iterated. Lets remove the specific pre and post folders with one line

    $ var=bar rm -rf ~/foo{1,2,3}/${var}/{ab,cd,ef}
    
  3. Shell iterated + var (BASH tested).

    $ rm -rf ~/*/folder/*
    
  1. 狂野的任何地方匹配。让我们删除任何出现的子“文件夹”

    $ rm -rf ~/foo{1,2,3}/folder/{ab,cd,ef}
    
  2. 壳迭代。让我们用一行删除特定的前后文件夹

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

    ##代码##