linux文件后面带个星号,关于linux:如何检测使用通配符(星号*)作为shell脚本的参数?...

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

这个

# myscript *

由此

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

我有一个类似的问题,但是我没有检测用户何时使用通配符调用脚本,而只是想防止使用通配符,并传递字符串预扩展。

如果您想检测,Tom的解决方案很好,但是我宁愿阻止。换句话说,如果我有一个名为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'

而不是其他包装函数。

如果需要更长的篇幅,请参阅此处的我的文章。

外壳会扩展通配符。到脚本运行时,通配符已经展开,并且脚本无法分辨参数是通配符还是显式列表。

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

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

"$@";

}

此功能执行三件事。首先,它检查脚本的参数是否为通配符(caveat:仅检查第一个参数,并且仅检查是否为简单的星形;将其扩展为涵盖更多情况作为练习)读者)。其次,它重新打开通配符扩展。最后,它运行原始命令。

为此,您确实需要能够在用户的shell中设置别名和shell函数,并要求您的用户使用别名而不是脚本。但是,如果您能够做到这一点,它应该可以工作。

我应该补充一点,我在这里主要依靠辉煌的西蒙·塔瑟姆(Simon Tatham)的文章"魔术别名:伯恩壳中的分层漏洞"。

西蒙·塔瑟姆(Simon Tathams)的论文很有趣,但它的前提是错误的:伯恩(Bourne)炮弹支持别名。真正的Bourne外壳不支持别名。只有高度扩展的Bourne外壳才可以使用,而无论它们仍然是Bourne外壳还是POSIX外壳或当时的其他东西,都是有争议的。

我谨此提出,该解决方案不是通用解决方案。在某些情况下,它似乎可以工作,但是usrbinmyscript仍然无法确定它最初是不是用通配符调用的。

@乔纳森:你说的完全正确,脚本说不清。但是脚本和别名的结合可以-我相信您总是可以使用这种方法来检测参数中的通配符;你能建议一个你做不到的情况吗?也就是说,如果您执行类似echo * |的操作。 xargs myscript,那么即使在原则上我也认为没有任何方法可以检测到。或echo *>一切;回声"时间过去"; xargs myscript

使用csh代替启用别名的Bourne / Korn / Bash Shell时?或者,当脚本由绝对路径名直接调用时。我不确定非交互式外壳是否一定要在设置别名的位置读取控制文件-部分取决于放置它的位置。 Perl脚本不会注意到它。我想知道make和类似程序。

你不能

它是Unix的优点之一(或从某些方面来说是缺点)。

请参见" UNIX-HATERS手册"中的" diatribe"。

您之所以不能这样做,是因为在脚本启动时,外壳程序已经扩展了通配符。

好吧,那我猜是那些将其视为弱点的人之一:)

哦,可以!遵循的答案...

附录

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

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"后会给出正确的结果。

有人知道为什么会这样吗?

关于您以前的尝试为何无效的原因:别名无法接受参数,因此在使用别名时" $ *"可能扩展为零。函数似乎是首选解决方案-例如,请参见此处。我仍然不知道为什么在两次调用后它仍然可以工作。 Havent尝试过。

$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测试的输出* test2 _

_

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

test

bash

pass.rtf

test2

_

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

只是注意是./bash测试 * test2 _被转义了*被读为" *"字符串而不是通配符

如果您觉得必须这样做,也许:

# 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"

pedantically,如果用户实际使用星号,或者用户以任何顺序手动输入目录内容,这将回显"二手星号"。