代码之家  ›  专栏  ›  技术社区  ›  iconoclast

bash或zsh中的“alias方法链”

  •  0
  • iconoclast  · 技术社区  · 7 年前

    这是(或 ,至少是)ruby中的一个常见模式,但我不知道如何在zsh或bash中实现。

    假设我有一个名为“whoosiwhatsit”的shell函数,我想在一个特定的项目中重写它,同时用不同的名称保持原始的可用性。

    如果我不知道,我可能会尝试创建一个别名来指向whoosiwhatsit,然后创建一个新的“whoosiwhatsit”函数来使用该别名。当然可以,因为别名将引用新函数。

    有什么方法可以完成我所说的吗?

    3 回复  |  直到 7 年前
        1
  •  2
  •   Charles Duffy    7 年前

    别名很弱。不过,你可以通过函数来实现这一点。考虑以下工具:

    #!/usr/bin/env bash
    
    PS4=':${#FUNCNAME[@]}:${BASH_SOURCE}:$LINENO+'
    
    rename_function() {
      local orig_definition new_definition new_name retval
      retval=$1; shift
      orig_definition=$(declare -f "$1") || return 1
      new_name="${1}_"
      while declare -f "$new_name" >/dev/null 2>&1; do
        new_name+="_"
      done
      new_definition=${orig_definition/"$1"/"$new_name"}
      eval "$new_definition" || return
      unset -f "$orig_definition"
      printf -v "$retval" %s "$new_name"
    }
    
    # usage: shadow_function target_name shadowing_func [...]
    # ...replaces target_name with a function which will call:
    # shadowing_func target_renamed_to_this number_of_args_in_[...] [...] "$@"
    shadow_function() {
      local shadowed_func eval_code shadowed_name shadowing_func shadowed_func_renamed
      shadowed_name=$1; shift
      shadowing_func=$1; shift
      rename_function shadowed_func_renamed "$shadowed_name" || return
      if (( $# )); then printf -v const_args '%q ' "$@"; else const_args=''; fi
      printf -v eval_code '%q() { %q %q %s "$@"; }' \
        "$shadowed_name" "$shadowing_func" "$shadowed_func_renamed" "$# $const_args" 
      eval "$eval_code"
    }
    

    …以及以下这些工具的应用示例:

    whoosiwhatsit() { echo "This is the original implementation"; }
    
    override_in_directory() {
      local shadowed_func=$1; shift
      local override_cmd_len=$1; shift
      local override_dir=$1; shift
      local -a override_cmd=( )
      local i
      for (( i=1; i<override_cmd_len; i++)); do : "$1"
        override_cmd+=( "$1" ); shift
      done
      : PWD="$PWD" override_dir="$override_dir" shadowed_func="$shadowed_func"
      : override_args "${override_args[@]}"
      if [[ $PWD = $override_dir || $PWD = $override_dir/* ]]; then
          [[ $- = *x* ]] && declare -f shadowed_func >&2 # if in debugging mode
          "${override_cmd[@]}"
      else
          "$shadowed_func" "$@"
      fi
    }
    
    ask_the_user_first() {
      local shadowed_func=$1; shift;
      shift # ignore static-argument-count parameter
      if [[ -t 0 ]]; then
        read -r -p "Press ctrl+c if you are unsure, or enter if you are"
      fi
      "$shadowed_func" "$@"
    }
    
    shadow_function whoosiwhatsit ask_the_user_first
    
    shadow_function whoosiwhatsit \
      override_in_directory /tmp echo "Not in the /tmp!!!"
    
    shadow_function whoosiwhatsit \
      override_in_directory /home echo "Don't try this at home"
    

    最终的结果是 whoosiwhatsit 函数,当stdin是tty时,在用户执行任何操作之前询问用户,并在其中一种情况下运行时中止(使用不同的消息) /tmp /home 是的。


    也就是说,我不宽恕这种做法。将上述内容视为一项智力练习。:)

        2
  •  2
  •   Stephen C    7 年前

    在bash中,有一个内置变量叫做 BASH_ALIASES 这是一个包含当前别名的关联数组。更新时语义有点不一致(rtm!)但是如果你把自己限制在阅读上 巴什别名 ,您应该能够编写一个实现别名链接的shell函数。

        3
  •  1
  •   that other guy    7 年前

    通过可选地调用其被重写的内置或命令的函数来创建单个级别的重写是很常见且受支持的:

    # Make all cd commands auto-exit on failure
    cd() { builtin cd "$@" || exit; }
    
    # Make all ssh commands verbose
    ssh() { command ssh -vv "$@"; }
    

    它没有超出一个链接的范围,但它完全是posix,并且在实践中通常比在bash中编写ruby更好。