如何在命令中传递参数而不运行参数内容?

如何在命令中传递参数而不运行参数内容?

好日子。

我有一个小问题。我在 bash 脚本中有这个函数

function denyCheck(){
    file=$(readlink -f $(which $(printf "%s\n" "$1")))
    if [[ -x $file ]];then
           return false
    else
           return true
    fi
}
denyCheck "firefox"

这个函数我向她传递了一个字符串,什么是命令,这解析了命令所在的原始路径(例如:/usr/lib/firefox/firefox.sh),如果文件可执行,则返回 true,否则返回 false。

但问题是...参数(在示例中为“firefox”)将其作为命令运行并打开浏览器firefox。

我该如何做才能使参数不运行它?

非常感谢。

答案1

更应该是:

cmd_canonical_path() {
  local cmd_path
  cmd_path=$(command -v -- "$1") &&
    cmd_path=$(readlink -e -- "$cmd_path") &&
    [ -x "$cmd_path" ] &&
    REPLY=$cmd_path
} 2> /dev/null

然后使用例如:

if cmd_canonical_path firefox; then
  printf '%s\n' "firefox found in $REPLY executable"
else
  printf >&2 '%s\n' "no executable file found for firefox"
fi

在 shell 中,所有命令都有一个退出状态,指示成功 (0) 或失败(任何其他数字,其值可以帮助区分不同类型的失败)。

例如,如果执行命令的进程具有文件(或目录)的执行权限,则[命令在传递-x和作为参数时将返回 true。/path/to/some/file]/path/to/some/file

函数还可以有退出状态,这是它返回之前运行的最后一个命令的退出状态(或者return在调用时传递到的数字)。

命令的成功或失败状态由 shell 结构(例如if//语句或其布尔运算符 和 )until进行检查。while||&&命令如果您愿意,可以是 shell 的布尔值。

cmd1 && cmd2 && cmd3返回 success / true 是 的全部cmd1cmd2并且cmd3成功。如果失败cmd2甚至不会执行cmd1,对于cmd3.

cmd_canonical_path因此,如果command -v -- "$1"(用于查找命令并输出其路径的标准内置 1 命令;而是which一些类似的命令,但仅适用于 csh 用户),则上述成功那么readlink -e(其工作方式类似于readlink -f规范化路径,但如果不能,则会返回失败,同时readlink -f会尽最大努力)成功那么当前进程有该文件的执行权限,然后(并且只有到那时)该路径才成功分配给REPLY4变量(普通分配总是成功的)。

cmd && cmd && cmd如果没有,该函数将返回失败:该链中失败的第一个命令的退出状态。

您还会注意到,$REPLY如果无法确定命令的规范路径或者您无法执行该命令,则该命令将被保留且不会被修改。

您的方法还有一些其他问题:

  • $(printf '%s\n' "$1")没有多大意义。在 sh/bash 中,它与$1(至少与默认值)相同,$IFS并且同样错误,因为它没有被引用。如果要将第一个位置参数的内容传递给命令,语法为cmd "$1"
  • 当您希望传递给命令的某些任意数据被视为非选项参数时,您需要在--标记选项结束的参数之后传递它:which -- "$1", readlink -f -- "$(...)"
  • return接受一个(可选)数字作为参数(函数的退出代码),而不是true/ false(你似乎也将它们颠倒了,因为你说如果文件可执行,函数将返回 true )。如果未传递数字,函数的退出状态将是函数中运行的最后一个命令的退出状态。所以if cmd; then return 0; else return 1; fi最好写成 just cmd; return。虽然在这里你甚至不需要return因为cmd[[ -x $file ]]在你的情况下)是你的函数中的最后一个命令。
  • 您盲目地将 的输出传递给which(错误地,因为您忘记了--并引用了$(...)),而readlink没有首先检查它是否成功。

最后一点,如果您使用zsh代替bash,您可以这样做:

firefox_path==firefox
firefox_canonical_path=$firefox_path:P

获取firefox命令的路径。=firefox扩展为命令的路径firefox,如果找不到命令,则中止 shell 并显示错误。您可以拦截该错误always堵塞如果需要的话。该:P修饰符realpath()对其所应用的参数执行等效操作,类似于 GNU 的操作readlink -f。无需检查执行权限,因为zsh's=cmd运算符只会扩展到可执行路径。


¹ 由于它是 shell 的内置组件,因此您可以在 shell 的手册中找到它的文档。对于bash,请参见info bash command示例。

² 需要注意的是,command -v cmd输出cmdcmd一个函数或 shell 内置函数,这可能是一个问题。

³ 规范的绝对路径以 开头/,没有符号链接组件(它们都已解析为目标),没有./组件,没有两个或多个s..的序列。/

4 $REPLY通常用作函数或 shell 内置函数返回值的默认变量(尽管这只是一种约定)。或者,您的函数可以输出结果,调用者将运行firefox_path=$(cmd_canonical_path firefox).

答案2

这:

function denyCheck(){

func()是标准函数声明和 ksh 的混合function func。 Bash 支持这种组合,其他 shell 可能不支持,但没有理由这样做。就写denyCheck()吧。

这里,

file=$(readlink -f $(which $(printf "%s\n" "$1")))

$(printf ...)是不必要的,而且由于它没有被引用,实际上会适得其反。该命令替换的结果将受到分词,而引用的"$1"不是,您可以使用它来代替。

which被认为存在一些问题,标准 POSIX 替代方案是command -v,请参阅为什么不用“哪个”呢?那该用什么呢?

两者之间的最大区别在于,它command是一个内置的 shell,并且知道例如别名(如果您从交互式 shell 运行它),而它which不知道(csh 除外,但您没有使用它)。这可能是好事,也可能不是好事:例如command -v ls可能会 return alias ls='ls -vF --color=auto',这不是您可以使用的路径。

在任何情况下,您都应该引用命令替换以避免分词问题,并添加--以标记选项的结尾。这是为了防止罕见的路径包含空格或以-.那么那就是:

file=$(readlink -f -- "$(command -v -- "$1")")  # or
file=$(readlink -f -- "$(which -- "$1")") 

然后,

return false

只是一个错误:该return命令需要一个数字。但是您可以运行名为的命令false来获取错误的返回状态。所以要么使用 return 1或者只是false. (由于您只是反转测试结果,因此您也可以使用 just! [[ -x "$file" ]]而不是整个if语句,将其写出来可能会让下一个读者更清楚。)

所以:

denycheck() {
    file=$(readlink -f -- "$(command -v -- "$1")")
    if [[ -x "$file" ]]; then
        return 1  # command found and executable, falsy result
    else
        return 0  # not executable, truthy result
    fi
}

相关内容