Linux Shell脚本:仅在程序存在时运行,如果不存在则将其忽略


15

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

例:

#!/usr/bin/env bash
echo "foo"
figlet "Starting"
echo "moo"
figlet "Working"
echo "foo moo"
figlet "Finished"

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

什么是实用的方法



@sudodus:只需忽略“ figlet”(及其参数)命令即可。当然,继续执行。
Sopalajo de Arrierez,

2
这个问题的标题使我陷入各种形而上的问题
g_uint

2
您要忽略所有错误吗?只需使用figlet ... || true
Giacomo Alzetta

如果您不在乎退出代码,则可以使用快捷方式figlet || true,但是在您的情况下,您可能更希望使用shell函数,如果没有可以打印的Banner则回显纯文本。
eckes

Answers:


30

我的解释将使用与该工具相同的包装函数;在该函数中,执行真实的工具(如果存在):

figlet() {
  command -v figlet >/dev/null && command figlet "$@"
}

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

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

if ! command -v figlet > /dev/null; then figlet() { :; }; fi

如果figlet即使没有安装figlet,也要打印参数,请按以下方式调整Olorin的建议:

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

1
哪里command来的?我的装置(Red Hat 6.8 Enterprise和Cygwin64)中没有它。
eewanco

1
@eewanco,请参见unix.stackexchange.com/a/85250/117549;长话短说,它内置在bash中,可能是您的shell。
杰夫·谢勒

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

@eewanco type -a command将显示给您。which显示在您的可执行文件$PATH,而不是内置插件,关键字,函数或别名。
l0b0

@ l0b0:在具有bash的RedHat系列上,并且默认配置文件which 确实找到了适用的别名,因为它which为运行自己而添加了别名,/usr/bin/which并通过管道将它的外壳别名列表以管道形式发送给(!)(当然\which,不显示别名,并且仅使用不显示别名的程序。)
dave_thompson_085

14

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

if type figlet >/dev/null 2>&1
then
    echo Figlet is installed
fi

6

一种常见的方法是使用test -xaka [ -x。这是从/etc/init.d/ntpLinux系统上获取的示例:

if [ -x /usr/bin/lockfile-create ]; then
    lockfile-create $LOCKFILE
    lockfile-touch $LOCKFILE &
    LOCKTOUCHPID="$!"
fi

此变体依赖于知道可执行文件的完整路径。在这里,/bin/lesspipe我找到了一个示例,该示例通过结合-xwhich命令来解决此问题:

if [ -x "`which bunzip`" ]; then bunzip -c "$1"
else echo "No bunzip available"; fi ;;

这样,这将工作没有提前知道哪里在PATHbunzip可执行文件。


4
不要使用which。即使which有效,使用test -x其输出也是很愚蠢的:如果从获得路径which,它就会存在。
吉尔(Gilles)'所以

1
@Gilles:从技术上讲,test -x它所做的不仅仅是检查文件是否存在(这就是test -e目的)。它还检查文件是否设置了执行权限。
comfreak

@comfreak也是which
吉尔(Gilles)'所以

5

在脚本的开头,检查是否figlet存在,如果不存在,则定义一个不执行任何操作的shell函数:

type figlet >/dev/null 2>&1 || figlet() { :; }

type检查是否figlet以shell内置,函数,别名或关键字的形式存在,并>/dev/null 2>&1丢弃stdin和stdout,以便不获取任何输出,如果不存在,则将其figlet() { :; }定义figlet为不执行任何操作的函数。

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

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

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

值得一提的是,由于您没有提到正在使用的外壳,因此我相信这是POSIX兼容的,因此它可以在大多数外壳上使用。


我喜欢这个答案的简单性。如果您正在使用bash,也可以替换type figlet >/dev/null 2>&1hash figlet 2>/dev/null。(操作员说“如果它在我的路径中”。)

5

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

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

$FIGLET "Hello, world!"

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

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

如果您不知道特定的路径,则可以尝试多个elif(请参见上文)尝试已知的位置,或者仅使用PATH总是解析命令:

if command -v figlet >/dev/null
then
    SAY=figlet
elif command -v banner >/dev/null
then
    SAY=banner
else
    SAY=echo
fi

通常,在编写脚本时,我更喜欢只在我指定的特定位置调用命令。我不喜欢最终用户可能PATH在他们自己身上投入的不确定性/风险~/bin

例如,如果我正在为其他人编写一个复杂的脚本,该脚本可能会根据我正在调用的特定命令的输出来删除文件,那么我不想意外地从它们~/bin中提取可能是或不是命令的东西我期望。


3
如果figlet/usr/local/bin/home/bob/stuff/programs/executable/figlet呢?
吉尔斯(Gilles)'所以

3
type -p figlet > /dev/null && figlet "foo"

bash type命令查找命令,函数,别名,关键字或内置函数(请参阅参考资料help type)并打印出位置或定义。它还返回代表搜索结果的返回码。如果找到,则为true(0)。因此,我们在这里的尝试是figlet在路径中查找(-p意味着仅查找文件,而不是内置文件或函数,并且也禁止显示错误消息),丢弃输出(就是> /dev/null这样),并且如果返回true(&&) ,它将执行figlet

如果figlet在固定位置,则更简单:

[ -x /usr/bin/figlet ] && /usr/bin/figlet "foo"

在这里我们使用 test命令(aka [)查看/usr/bin/figlet可执行文件(-x)和可执行文件()&&。我认为此解决方案比使用typebashism 更容易携带。

您可以创建一个为您执行此操作的函数:

function x() {
    if type -p "$1" >/dev/null; then
        cmd="$1"
        shift
        "$cmd" "$@"
    fi
}

(由于可能存在空格,因此引号是必需的)

然后,您只需执行以下操作:

x figlet "foo"

0

o /,我会说类似

#!/usr/bin/env bash
# if figlet is installed :
if [ "$(which figlet 2>/dev/null)" ]; then
       # do what you wanted to do
       echo "foo"
       figlet "Starting"
       echo "moo"
       figlet "Working"
       echo "foo moo"
       figlet "Finished"
# if not
else
       # exit program with an error
       echo "please install figlet"
       exit 1
fi

0

您可以执行测试执行,取消任何输出以及测试成功/失败代码。选择参数给figlet使该测试便宜。-?或--help或--version是显而易见的可能性。

if figlet --help >/dev/null 2>&1 ; then
    # figlet is available
    echo "foo"
    figlet "starting"
    #etc
else
    rc=$?
    echo "figlet is not installed or not working correctly (return code ${rc})"
fi

为了回应以下评论而添加:如果您真的想测试figlet的存在,而不是它的可用性,那么您可以

figlet --help >/dev/null 2>&1 
rc=$?
if [ $rc -eq 127 ] ; then # 127 is "command not found" on linux bash 4.4.23(1)
    echo "command figlet not found"
else
    figlet "whatever" # use figlet
fi

这里的问题是,figlet可能会由于未安装而导致失败,因此仅测试退出代码是不够的。
克里斯,

如果您真的只想测试其安装,则想测试是否$?等于127(在我的linux系统上)。127是“找不到命令”。但是我的想法是,对于合理的命令,如果command --help失败了,那么安装就很麻烦了,以至于它可能根本不存在!
nigel222 '19

1
126是“找到命令但不能执行”,因此您也想对此进行测试。不幸的是,您不能依赖于--help有空。Posix实用程序没有它,而posix指南实际上建议不要使用它。例如,at --help失败at: invalid option -- '-',并且130系统上的退出状态为。
克里斯,

同样,当然,如果figlet可以状态127退出也可能是一个问题。
克里斯,

公平点大约为126。大多数 理智的实用程序都有某种轻量级的无害命令选项,例如-? --help --version...,或者您可以找出退出的内容figlet -0 --illegal,并将其视为成功的指标(只要不是127,我将其归类)作为破坏活动)。
nigel222 '19
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.