【问题标题】:Why does history require a numeric value for grep?为什么历史需要 grep 的数值?
【发布时间】:2019-10-21 22:27:38
【问题描述】:

我正在尝试创建一个自定义函数 (hisgrep) 以从历史记录中 grep。

我之前有它工作过,当时代码基本上是“历史 | grep $1”,但我希望实现能够 grep 多个关键字。 (例如,“hisgrep docker client”将等于“history | grep docker | grep client”)。

我的问题是,当我尝试执行此操作时,出现以下错误:“-bash: history: |: numeric argument required.”

我尝试将最终调用命令的方式从 $cmd 更改为 $cmd,但没有任何效果。

代码如下:

#!/bin/bash

function hisgrep() {
    cmd='history'
    for arg in "$@"; do
        cmd="$cmd | grep $arg"
    done
    `$cmd`
}

【问题讨论】:

  • eval "$cmd" 有机会工作(但在命令中添加grep "$1"grep "$<n>" 而不是grep <arg>
  • @Ry- 但这不会成功,所以我必须知道它总是会收到多少个参数?
  • 请看BashFAQ/050

标签: bash function history


【解决方案1】:

遗憾的是,bash 没有所谓的“foldl”或类似功能。

你可以这样做:

histgrep() {
    local str;
    # save the history into some list
    # I already filter the first argument, so the initial list is shorter
    str=$(history | grep -e "$1");
    shift;
    # for each argument
    for i; do
       # pass the string via grep
       str=$(<<<"$str" grep "$i")
    done
    printf "%s\n" "$str"
}

注意事项:

  • 执行 cmd="$cmd | grep $arg" 然后执行 `$cmd` 看起来不安全。
  • 记得引用你的变量。
  • 使用https://www.shellcheck.net/ 检查您的脚本。
  • 反引号`是deprecated。使用$() 命令替换。
  • 同时使用函数和括号function func() 是不可移植的。只需func()

对于不安全的版本,你需要通过 eval 传递它(evalevil),通过智能使用 printf 可以缩短为:

histgrep() { eval "history $(printf "| grep -e '%s' " "$@")"; }

但我认为通过在eval 调用中扩展命令替换后的参数,我们可以做得更安全:

histgrep() { eval "history $(printf '| grep -e "$%s" ' $(seq $#))"; }

这里的eval 会看到history | grep -e "$1" | grep -e "$2" | ...,我认为这看起来实际上很安全。

【讨论】:

  • 手动折叠怎么样? multigrep() { if [ $# -eq 0 ]; then cat; else local p="$1"; shift; grep -e "$p" | multigrep "$@"; fi; },然后是history | multigrep foo bar
  • 递归看起来不错。 multigrep() { if [ $# -lt 1 ]; then grep "$@"; else ... 你甚至不能滥用猫
  • 这不会破坏multigrep(没有参数)吗?
【解决方案2】:

它不起作用,因为| 被解释为history 命令的参数。

【讨论】:

    猜你喜欢
    • 2021-06-26
    • 2013-02-11
    • 2021-11-13
    • 1970-01-01
    • 2011-07-12
    • 1970-01-01
    • 1970-01-01
    • 2020-09-25
    相关资源
    最近更新 更多