我在shell脚本中有一行看起来像这样:
java -jar "$dir/"*.jar
,因为我只想执行该文件夹中恰好命名的jar文件。但这并不像我预期的那样有效。我收到错误消息:
Error: Unable to access jarfile [folder-name]/*.jar
正在采取' *'从字面上看,而不是做我想要的替代品。我该如何解决这个问题?
编辑:它现在正在运作。我只是有错误的文件夹前缀:/对于任何人想知道,这是正确的方法。答案 0 :(得分:4)
您只需要设置failglob
:
shopt -s failglob
以避免在给定文件夹中没有匹配时显示文字*.jar
。
PS:如果无法匹配任何*.jar
,则会产生错误:
-bash: no match: *.jar
答案 1 :(得分:3)
OP的问题本身不是通配符 - 要使glob(模式)生效,特殊模式字符(例如*
)必须< em> unquoted ,即使在部分单引号或双引号的字符串中也能正常工作,正如OP在他的问题中所做的那样:
"$dir/"*.jar # OK, because `*` is unquoted
相反,问题是bash
- 有点令人惊讶 - 保留模式未展开的默认行为(保留原样), if它碰巧不匹配任何 ,实际上导致一个不代表任何实际文件系统项的字符串。
"$dir"
碰巧扩展到了一个不包含*.jar
个文件的目录,因此传递给java
的结果字符串以 literal <结尾/ em> *.jar
('<value of $dir>/*.jar'
),由于未提及实际的.jar
文件,导致问题中引用了错误。Shell选项管理globbing(更正式地称为路径名扩展):
set -f
(shopt -so noglob
)完全关闭了globbing ,因此通常会导致字符串被视为glob的不带引号的字符串(字符)被视为文字shopt -s nullglob
改变默认行为,将不匹配的glob扩展为空字符串 。shopt -s failglob
将默认行为更改为 报告错误,并在不匹配的glob 的情况下将退出代码设置为1 ,甚至不执行手头的命令 - 见下面的陷阱。{ shopt -o; shopt; } | fgrep glob
。有关说明,请在man bash
。注意:全局设置shell选项会影响当前的shell ,这是有问题的,因为第三方代码通常做出 - 合理 - 假设默认值有效。因此,通过使用子shell((...)
)暂时更改shell选项(更改,执行操作,恢复)或本地化更改其效果是一种很好的做法。
shopt -s nullglob
for
的循环中的匹配项 - 它确保永远不会输入循环,如果没有匹配项:shopt -s nullglob # expand non-matching globs to empty string
for f in "$dir/"*.jar; do
# If the glob matched nothing, we never get here.
# !! Without `nullglob`, the loop would be entered _once_, with
# !! '<value of $dir>/*.jar'.
done
shopt -s nullglob # expand non-matching globs to empty string
wc -c "$dir/"*.jar # !! If no matches, expands to just `wc -c`
如果glob不匹配任何内容,只执行wc -c
,不失败,而是开始读取stdin
输入(交互运行时,这将只是等待交互式输入行,直到用 Ctrl-D 终止。
shopt -s failglob
set -e
结合使用时导致脚本自动中止的情况,以防全局不匹配:set -e # abort automatically in case of error
shopt -s failglob # report error if a glob matches nothing
java -jar "$dir/"*.jar # script aborts, if this glob doesn't match anything
|| <command in case of failure>
成语结合使用时:shopt -s failglob # report error if a glob matches nothing
# !! DOES NOT WORK AS EXPECTED.
java -jar "$dir/"*.jar || { echo 'No *.jar files found.' >&2; exit 1; }
# !! We ALWAYS get here (but exit code will be 1, if glob didn't match anything).
因为在failglob
打开的情况下,如果globbing失败,bash
永远不会执行手头的命令,||
子句也不会执行,并且整体继续执行。
虽然失败的glob会导致退出代码设置为1
,但您无法区分由于不匹配的glob而导致的失败与命令报告的失败(之后)成功的全球化。)
替代解决方案,无需更改shell选项:
稍加努力,您可以自行检查不匹配的球:
广告自组织强>:
glob="$dir/*.jar"
[[ -n $(shopt -s nullglob; echo $glob) ]] ||
{ echo 'No *.jar files found.' >&2; exit 1; }
java -jar $glob
$(shopt -s nullglob; echo $glob)
设置nullglob
,然后使用echo
扩展glob,以便子shell返回匹配的文件名,或者如果没有匹配则返回 empty 字符串;由于命令替换($(...)
),该输出被传递给-n
,它测试字符串是否为空,以便整个[[ ... ]]
条件的退出代码反映某些匹配(退出代码0
)与否(退出代码1
)。
请注意,命令替换中的任何命令都在子shell 中运行,这可确保shopt -s nullglob
的效果仅适用于该子shell,因此不会改变全局状态
还要注意变量赋值glob="$dir/*.jar"
中的整个右边是如何双引号的,以说明当变量为稍后引用,而不是定义。 未引用的引用到$glob
以后确保整个字符串被解释为glob。
使用小辅助功能:
# Define simple helper function.
exists() { [[ -e $1 ]]; }
glob="$dir/*.jar"
exists $glob || { echo 'No *.jar files found.' >&2; exit 1; }
java -jar $glob
辅助函数利用shell在函数调用时应用globbing ,并将globbing(路径名扩展)的结果作为参数传递。该函数然后简单地测试第一个结果参数(如果有的话)是否引用现有的项,并相应地设置退出代码(无论nullglob
是否恰好在效果与否。)