如何检测使用通配符(星号*)作为shell脚本的参数?

时间:2010-09-13 15:22:33

标签: linux unix shell

在我的脚本中,如何区分何时使用星号通配符而不是强类型参数?

# myscript *

来自这个

# myscript p1 p2 p3 ... (where parameters are unknown number)

6 个答案:

答案 0 :(得分:7)

shell扩展了通配符。在运行脚本时,通配符已经扩展,并且脚本无法判断参数是通配符还是显式列表。

这意味着您的脚本需要其他非脚本的帮助。具体来说,在命令行处理之前运行的东西。那个东西是别名。这是你的别名

alias myscript='set -f; globstopper /usr/bin/myscript'

这样做是设置一个名为'myscript'的别名,所以当有人输入'myscript'时,这就是运行的东西。别名做了两件事:首先,它用set -f关闭通配符扩展,然后运行一个名为globstopper的函数,传入脚本的路径,以及其余的命令行参数。

那是什么是globstopper函数?这样:

globstopper() {
  if [[ "$2" == "*" ]]
    then echo "You cannot use a wildcard"
    return
  fi
  set +f
  "$@";
}

这个功能做了三件事。首先,它检查脚本的参数是否是通配符(警告:它只检查第一个参数,它只检查它是否是一个简单的星;延伸它以覆盖更多的情况留下作为练习读者)。其次,它重新开启了通配符扩展。最后,它运行原始命令。

为此,您需要能够在用户的shell中设置别名和shell功能,并要求您的用户使用别名,而不是脚本。但如果你能做到这一点,就应该有效。

我应该补充一点,我在这里严重依赖金碧辉煌的西蒙·塔特姆的文章“Magic Aliases: A Layering Loophole in the Bourne Shell”。

答案 1 :(得分:7)

我有一个类似的问题,但是当用户使用通配符调用脚本时,检测,而不是阻止使用通配符,并通过字符串预扩展。

如果你想要发现,

Tom's solution很棒,但我宁愿阻止。换句话说,如果我有一个名为findin的脚本看起来像

#!/bin/bash
echo "[${1}]"

并使用以下方式运行:

$ findin *

我希望输出只是

[*]

要做到这一点,你可以通过

命名findin
alias findin='set -f; /path/to/findin'

但是,您可以为会话的其余部分设置shell选项。这可能会破坏许多不期望这样的程序(例如ls -lh * .py)。您可以通过键入

来验证这一点
echo $-

在控制台中。如果看到f,则设置该选项。

您可以通过键入

手动清除该选项
set +f

在每个findin实例之后,但这会变得单调乏味。

由于shell脚本产生子shell并且你无法从脚本中清除标志(set + f),我提出的解决方案如下:

g(){ /usr/local/bin/findin "$@"; set +f; }
alias findin='set -f; g'

注意:'g'可能不是该函数的最佳名称,因此我们鼓励您更改它。

最后,您可以通过执行以下操作来概括:

reset_expansion(){ CMD="$1"; shift; $CMD "$@"; set +f; }
alias findin='set -f; reset_expansion /usr/local/bin/findin'

这样,您希望扩展禁用的另一个脚本只需要一个额外的别名,例如

alias newscript='set -f; reset_expansion /usr/local/bin/newscript'

而不是额外的包装函数。

如果时间长于必要的写作,请参阅my post here


答案 2 :(得分:3)

你不能。

这是Unix的优势之一(或者,在某些人看来,弱点)。

请参阅"The UNIX-HATERS Handbook"中的dia骂。

答案 3 :(得分:1)

$arg_num == ***; // detects *(literally anything since it's a global wildcard)
$arg_num == *_*; // detects _

这是一个使用_

的例子
for i in $*
  do
    if [[ "$i" == *_* ]]; 
      then echo $i; 
    fi
  done

输出 ./ bash test * test2 _

_

输出 ./ bash test * test2 使用*********而不是*** *

test
bash
pass.rtf
test2
_

注意:*在bash中是如此全局,以至于它打印出与该描述相匹配的文件,或者在我这个未使用过的桌面上的文件中。我希望我能给你一个更好的答案,但最好选择使用其他*或其他脚本语言。

答案 4 :(得分:1)

附录

我在寻找命令行计算器的解决方法时找到了这篇文章:

alias c='set -f; call_clc'

where "call_clc" is the function: "function call_clc { clc "$*"; set +f; }"

and "clc" is the script
#!/bin/bash
echo "$*" | sed -e 's/ //g' >&1 | tee /dev/tty | bc

我需要'set -f'和'set +f'才能使'c 4 * 3'等输入正常工作, 因此前后有白色空间的星号, 为了防止狂欢的崩溃。

更新:上一个版本“alias c='set -f; clc "$*"; set+f;'”无效 因为某些原因,在两次调用命令“c 4 * 4”后给出了正确的结果。 有人知道为什么会这样吗?

答案 5 :(得分:0)

如果这是您认为必须做的事情,或许:

# if the number of parms is not the same as the number of files in cwd
# then user did not use *
dir_contents=(*)
if [[ "${#@}" -ne "${#dir_contents[@]}" ]]; then 
    used_star=false
else
    # if one of the params is not a file in cwd
    # then user did not use *
    used_star=true
    for f; do [[ ! -a "$f" ]] && { used_star=false; break; }; done
fi
unset dir_contents

$used_star && echo "used star" || echo "did not use star"

如果用户实际使用星号或者用户以任何顺序手动输入目录内容,这将会回显“使用过的星号”。