【问题标题】:bash: override echo to print a custom prefix in a scriptbash:覆盖 echo 以在脚本中打印自定义前缀
【发布时间】:2012-01-12 09:07:52
【问题描述】:

我有一些冗长的脚本相互调用,我希望它们的输出更具描述性。这个想法是为每个脚本自定义 echo 命令,如下所示。

我的问题是,如何使用echo 使其非递归?

这是script1.sh

#!/bin/bash
#Original version:
#function echo(){ echo $(basename $0 .sh): $1; }
#Version after fixes
function echo(){ builtin echo -n "$(basename $0 .sh): ">&2; builtin echo $@ ; } 
echo Info
./script2.sh

这是 script2.sh

#!/bin/bash
#Original version:
#function echo(){ echo $(basename $0 .sh): $1; }
#Version after fixes
function echo(){ builtin echo -n "$(basename $0 .sh): ">&2; builtin echo $@ ; } 
echo Info
exit 0

所以输出应该是:

>./script1.sh
script1: Info
script2: Info

--- 编辑

奖金:

>./script1.sh 2> /dev/null 
Info
Info

【问题讨论】:

  • unset -f echo 后面应该使用
  • 您也可以将该函数声明放入它自己的文件中,然后将source 放入需要它的脚本中。

标签: bash overloading verbosity


【解决方案1】:

使用builtin 关键字:

function echo(){ builtin echo $(basename $0 .sh): $1; }

这里是帮助页面:

$ help builtin
builtin: builtin [shell-builtin [arg ...]]
    Execute shell builtins.

    Execute SHELL-BUILTIN with arguments ARGs without performing command
    lookup.  This is useful when you wish to reimplement a shell builtin
    as a shell function, but need to execute the builtin within the function.

    Exit Status:
    Returns the exit status of SHELL-BUILTIN, or false if SHELL-BUILTIN is
    not a shell builtin..

【讨论】:

  • +1;但最好不要写$1,而是写"$@"。 (请注意,此功能会禁用 echo 的所有特殊功能,例如 -E-n。)
  • @lesmana:很好,就像它旨在解决这个问题一样:D 谢谢!
  • @ruakh -E-n 等选项不一定会被禁用。他们是specified for the built in function,他们为我工作。例如:builtin echo -e "\e[1;31m red"... 但我认为如果在 POSIX 模式下运行或使用--enable-xpg-echo-default 编译的 bash,-e 选项将被禁用;但是,在这种情况下,-e 选项仅被禁用,因为它已经打开。
  • @u8it:我想你一定误解了我的评论。您的 builtin echo -e "\e[1;31m red" 示例不相关,因为此答案中的函数将改为运行 builtin echo name_of_script -e "\e[1;31m red"
  • Gotcha,参考包装函数本身。尽管如此,包装器并没有真正禁用这些功能。它们可以通过 POSIX 模式或--enable-xpg-echo-default 等其他方式启用,但此包装器不会将它们作为参数处理......并且澄清内置函数支持这些标志仍然相关。
【解决方案2】:

builtin 仅适用于 echo 等 shell 内部命令。我知道最初的问题与echo 特别相关,但由于这是“bash 覆盖功能”的热门谷歌热门,我想我会为其他命令添加我的解决方案。当我运行ssh 时,我正在编写一个包装器来更改终端名称,因此我定义了一个名为ssh 的函数,并且需要从内部调用实际的二进制文件。这可能不是最好的方法,但它可以做到:

function ssh {
    $(which ssh) $@
}

【讨论】:

  • 执行此操作的标准方法是command ssh "$@"(其中command 绕过任何shell 函数以支持要么 内置shell外部程序)。
【解决方案3】:

我想出了 I/O 重定向作为解决方案。 Bash Redirections Using Exec 文章启发了我。它具有以下含义:

  • 重定向脚本的标准或错误输出,无论哪个命令打印到它,例如echo、printf 等。
  • 子外壳或源脚本无需重复工作。
  • 重定向扩展到任何被此脚本、其下标或源脚本调用的可执行文件。
  • 可以通过创建多个管道来组合过滤器,其中一个管道将其输出重定向到下一个管道的输入,依此类推。

在我使用的bash shell 脚本中:

alias start_log_ctx="start_filter_by_pipe_regex 2 s/.*/${0##*/}\(pid=$$\): \0/"
function start_filter_by_pipe_regex {
  declare -r a="$(mktemp -u)" #1
  mknod "$a" p                #2
  sed <"$a" ">&$1" "$2" &     #3
  exec "$1">"$a"              #4
}

alias stop_log_ctx_by_job="stop_filter_by_pipe_job 2"
def stop_filter_by_pipe_job {
  exec "$1">&- #1
  wait "$2" #2
}

本质上,start_log_ctx 别名使用 sed 来逐行 (.*) 过滤脚本的 I/O,该 I/O 会转到错误输出 (2) 并预先添加脚本的基本名称 (${0##*/} ) 和进程 ID (pid=$$) 到每一行。 start_filter_by_pipe_regex 函数的工作原理如下:

  1. 存储绝对路径,但不要创建 (-U) 临时文件。
  2. 在[1.]的绝对路径下创建管道。
  3. 启动 sed 作为后台过滤器 (&amp;)。让它从 [2.] 的管道中读取 (&lt;) 并将其标准输出 (&gt;) 重定向到作为 1. 参数给出的文件描述符。将作为 2. 参数给出的过滤器传递给sed
  4. 将作为 1. 参数给出的文件描述符重定向 (&gt;) 到 [2.] 的管道。

stop_log_ctx 别名关闭错误输出的文件描述符 (2) 并等待先前启动的 sed 停止。它在关闭文件描述符时隐式执行此操作。 stop_filter_by_pipe_job 函数执行以下操作:

  1. 关闭作为 1. 参数给出的文件描述符,从而停止 sed
  2. 等待由 2. 参数给出的 bash 作业标识的后台 sed 停止。

在我的主要功能开始时,我通常执行 declare -r a="$(start_log_ctx)" 并以 stop_log_ctx_by_job "$a" 结束。

【讨论】:

  • 也可以使用mkfifo "$a" 代替mknod "$a" p
猜你喜欢
  • 1970-01-01
  • 2022-01-04
  • 2021-04-08
  • 1970-01-01
  • 2021-01-25
  • 2018-09-26
  • 1970-01-01
相关资源
最近更新 更多