Linux shell 脚本:只在存在时运行程序,如果不存在则忽略它

Sop*_*rez 18 shell scripting executable

我编程的Linux shell脚本,将只有在合适的工具,比如它的执行过程中打印状态横幅figlet,被安装(是这样的:到达系统的路径)。

例子:

#!/usr/bin/env bash
echo "foo"
figlet "Starting"
echo "moo"
figlet "Working"
echo "foo moo"
figlet "Finished"
Run Code Online (Sandbox Code Playgroud)

我想为我的脚本工作无差错,即使figlet在没有安装。

什么是实用的方法?

Jef*_*ler 35

我的解释是使用与工具名称相同的包装函数;在该函数中,如果存在,则执行真正的工具:

figlet() {
  command -v figlet >/dev/null && command figlet "$@"
}
Run Code Online (Sandbox Code Playgroud)

然后你可以figlet arg1 arg2...在你的脚本中保持不变。

@Olorin 提出了一个更简单的方法:仅在我们需要时(如果该工具不存在)定义一个包装函数:

if ! command -v figlet > /dev/null; then figlet() { :; }; fi
Run Code Online (Sandbox Code Playgroud)

如果您希望在figlet未安装 figlet 的情况下打印参数,请按如下方式调整 Olorin 的建议:

if ! command -v figlet > /dev/null; then figlet() { printf '%s\n' "$*"; }; fi
Run Code Online (Sandbox Code Playgroud)

  • `command` 是内置的 POSIX Bourne shell。它通常执行提供的命令,但 `-v` 标志使它的行为更像 `type`,另一个内置的 shell。 (4认同)

roa*_*ima 14

你可以测试看看是否figlet存在

if type figlet >/dev/null 2>&1
then
    echo Figlet is installed
fi
Run Code Online (Sandbox Code Playgroud)


kas*_*erd 6

一种常见的方法是使用test -xaka [ -x。下面是一个取自/etc/init.d/ntpLinux 系统的示例:

if [ -x /usr/bin/lockfile-create ]; then
    lockfile-create $LOCKFILE
    lockfile-touch $LOCKFILE &
    LOCKTOUCHPID="$!"
fi
Run Code Online (Sandbox Code Playgroud)

此变体依赖于了解可执行文件的完整路径。在/bin/lesspipe我找到了一个例子,它通过组合-x和which命令来解决这个问题:

if [ -x "`which bunzip`" ]; then bunzip -c "$1"
else echo "No bunzip available"; fi ;;
Run Code Online (Sandbox Code Playgroud)

这样就可以在不事先知道可执行文件中的位置PATH的情况下工作bunzip。

  • [不要使用`which`](http://unix.stackexchange.com/questions/85249/why-not-use-which-what-to-use-then)。即使 `which` 有效,在其输出上使用 `test -x` 也是愚蠢的:如果你从 `which` 获得路径,它就存在。 (4认同)

rra*_*nza 5

另一种选择——我在项目自动配置脚本中看到的一种模式:

if [ -x /usr/bin/figlet ]
then
    FIGLET=/usr/bin/figlet
else
    FIGLET=:
fi

$FIGLET "Hello, world!"
Run Code Online (Sandbox Code Playgroud)

在您的特定情况下,您甚至可以这样做,

if [ -x /usr/bin/figlet ]
then
   SAY=/usr/bin/figlet
elif [ -x /usr/local/bin/figlet ]
then
   SAY=/usr/local/bin/figlet
elif [ -x /usr/bin/banner ]
then
   SAY=/usr/bin/banner
else
   SAY=/usr/bin/echo
fi

$SAY "Hello, world!"
Run Code Online (Sandbox Code Playgroud)

如果您不知道具体路径,您可以尝试多个elif(见上文)来尝试已知位置,或者只使用PATH始终解析命令:

if command -v figlet >/dev/null
then
    SAY=figlet
elif command -v banner >/dev/null
then
    SAY=banner
else
    SAY=echo
fi
Run Code Online (Sandbox Code Playgroud)

一般来说,在编写脚本时,我更喜欢只在我指定的特定位置调用命令。我不喜欢最终用户可能放入PATH他们的~/bin.

例如,如果我正在为其他人编写一个复杂的脚本,这些脚本可能会根据我正在调用的特定命令的输出删除文件,我不想意外地从他们~/bin那里拿起一些可能是也可能不是命令的东西我期望。

  • 如果 `figlet` 在 `/usr/local/bin` 或 `/home/bob/stuff/programs/executable/figlet` 中会怎样? (3认同)

Chr*_*ris 5

在脚本开始时,检查是否figlet存在,如果不存在,定义一个什么都不做的 shell 函数:

type figlet >/dev/null 2>&1 || figlet() { :; }
Run Code Online (Sandbox Code Playgroud)

type检查是否figlet作为 shell 内置、函数、别名或关键字存在,>/dev/null 2>&1丢弃 stdin 和 stdout,因此您不会得到任何输出,如果它不存在,则figlet() { :; }定义figlet为一个什么都不做的函数。

这样您就不必编辑使用 的脚本的每一行,也不必在figlet每次figlet调用时检查它是否存在。

如果您愿意,可以添加诊断消息:

type figlet >/dev/null 2>&1 || { echo 'figlet not installed.' ; figlet() { :; } ; }
Run Code Online (Sandbox Code Playgroud)

作为奖励,由于您没有提到您使用的是哪个 shell,我相信这是 POSIX 兼容的,因此它应该适用于大多数 shell。