覆盖现有 shell 命令的最佳方法是什么?

What is the best way to override an existing shell command?

如果需要修改现有的 shell 命令(内置或其他),最好的方法是什么?

声明一下,我知道尝试这样的程序存在许多潜在风险,但这正是我问这个问题的原因。

我想创建一个 shell 函数(称为 ping)来代替常见的 ping command. For the sake of this topic, let's say that it should at the very least be compatible in both Bash and Zsh shell 环境。

具体来说,我想允许 ping 接受主机名中的完整 URL(带有协议、尾随 slash/pathname、查询参数等)。字符串操作不是让我难过的,而是如何正确执行原始 ping 可执行文件而不调用 ping.

以外的函数

例如,下面两个命令应该产生相同的结果:

# EXAMPLE 2 (What `ping` will accept)
ping who.is

# EXAMPLE 1 (What I would like to be able to do with `ping`)
ping https://who.is/my/ping/?example=this

垫片可能如下所示(如果您的真实 ping/usr/bin 中):

#!/usr/bin/env bash
uri_re='^[[:alnum:]]+://([^/]+)/'

if [[  =~ $uri_re ]]; then
  exec /usr/bin/ping "${BASH_REMATCH[1]}" "${@:2}"
else
  exec /usr/bin/ping "$@"
fi

把它放在像 /opt/overrides/bin 这样的地方,然后把它放在 PATH 中比 /usr/bin 更早的地方(所以 PATH=/opt/overrides/bin:/bin:/usr/bin:/usr/local/bin 之类的)。


或者,对于将适用于所有 POSIX 兼容 shell(以及 zsh,即使它不尝试成为一个)的可移植函数:

ping() {
  local _ping_addr >/dev/null 2>&1 ||: "ignore failure on shells that don't support local"
  case  in
    *://*/)
      _ping_addr=${1#*://}
      _ping_addr=${_ping_addr%%/*}
      shift
      command ping "$_ping_addr" "$@"
      ;;
    *)
      command ping "$@" ;;
  esac
}

我想解决@blizzrdof77's 转述如下):

"I would like to create a shell function (called 'ping') to use in place of the common <a href="https://www.shellscript.sh/tips/ping/" rel="nofollow noreferrer">ping</a> command."


这个插入式便携 shell function [ℹ] works well on Mac & Linux in zsh,bash,sh, and all POSIX-compliant shells.

# ---------------------------------------------------------
#  Better `ping` support for various URL formats/protocols
#
#  @param  - hostname
#  EXAMPLE USAGE: `ping http://google.com/`
# ---------------------------------------------------------

ping() {
    local pingdomain=""
    shopt -s nocasematch # allow lowercase
    pingdomain=${pingdomain/#*:\/\/} # strip protocol
    pingdomain=${pingdomain/#*:*@/} # strip leading 'user:pass@'
    pingdomain=$(echo "${pingdomain//"?"//}") # remove '?'
    pingdomain="$(echo "$pingdomain" | cut -d/ -f 1)" # clear last '/'
    command ping $pingdomain
}