我有这个代码:
#!/bin/bash
num=${1:-undefined}
cmd=$(which {banner,echo} | head -n1)
until [[ "$num" =~ ^[0-9]+$ ]]; do
read -p "Type a number: " num
done
for ((num;num>=0;num--)); do
$cmd $num
sleep 1
done
但我认为有更好的方法来了解程序是否已安装。我已经尝试过这些。但我认为尚不清楚:
which banner && cmd=banner || cmd=echo
cmd=$(eval 'which '{banner,echo}'||' :)
cmd=$(which {banner,echo} | head -n1)
cmd=$(which banner || which echo)
我比其他的更喜欢 3 个,但是我认为有人可以帮助我找到一个好的方法来做到这一点。我接受使用type
,hash
或command
如果需要的话。
这是一个紧凑的代码挑战,所以我想制作一个单行代码,使其简短,并尝试不重复变量名称或任何命令(除了||
,&&
等等)
答案1
基于函数的方法可以产生最清晰的代码。至少有两种可能的变体,其中一种是 FloHimself 在上面的评论中建议的(这不是我喜欢的方式):
function myprint
{
if type banner >/dev/null
then banner "$@"
else echo "$@"
fi
}
myprint "Hello World!"
或者 - 我更喜欢的变体 - 使用条件函数定义:
if type banner >/dev/null
then function myprint { banner "$@" ;}
else function myprint { echo "$@" ;}
fi
myprint "Hello World!"
它没有if
每次调用的条件开销,并且也不需要重复字符串或变量表达式(如另一个建议中所示)。
答案2
最简单的方法就是运行
command1 "hello world" || command2 "hello world"
如果第一个命令不存在,则左侧的命令||
将失败,因此将运行右侧的命令。我不明白为什么你需要先测试。就去做吧,如果失败了,就做点别的事。
您可以通过忽略由于缺少命令而导致的错误消息并避免重复字符串来使情况稍微好一些:
string="Hello world!"
banner "$string" || echo "$string"
如果您确实需要检查命令是否存在,我会这样做:
command="banner"
type "$command" 2>/dev/null || command="echo"
"$command" "Hello world!"
答案3
在 中zsh
,$commands
特殊关联数组将命令映射到其路径,您可以执行以下操作:
cmd=${commands[figlet]-${commands[banner]-echo}}
用于$cmd
设置为figlet
实用程序的路径(如果存在),或者设置为banner
其他路径,或者设置为echo
(内置)(如果两者都不figlet
存在banner
)。
$cmd
您可以定义一个命令来代替变量 ( ):
hash cmd=${commands[figlet]-${commands[banner]-$commands[echo]}}
尽管在这里您最终会得到独立的echo
实用程序而不是内置实用程序。另请注意,该散列命令会在hash -r
aka上消失rehash
。