【问题标题】:bash scripting: build a command then executebash 脚本:构建命令然后执行
【发布时间】:2015-09-25 17:08:23
【问题描述】:

我正在尝试创建一个函数,该函数基本上接受f hello there my friend 之类的参数,并使用find 在目录中搜索任何这些字符串的所有出现,因此它将是find | grep 'hello\|there\|my\|friend'。我是 shell 脚本的新手,但我的代码如下:

function f { 
  cmd="find | grep '"
  for var in "$@"
  do 
    cmd="$cmd$var\\|"
  done
  cmd="${cmd%\\|}'"
  echo "$cmd"
  $cmd 
}

当我执行命令时,我得到这个:

# f hello there my friend
find | grep 'hello\|there\|my\|friend'
find: `|': No such file or directory
find: `grep': No such file or directory
find: `\'hello\\|there\\|my\\|friend\'': No such file or directory

为什么它不起作用,我怎样才能使它起作用?我想这与未将字符串转换为命令有关,但我对 shell 脚本的工作原理知之甚少。

【问题讨论】:

  • 等等...您要在文件名文件内容中搜索这些字符串吗?
  • @gniourf_gniourf 文件名。搜索内容是通过grep -r
  • 确定文件名:那么您是在寻找完整路径还是只是文件名?因为如果目录名称与字符串匹配,您的方法将包含给定目录中的所有文件。

标签: bash shell


【解决方案1】:

看起来你的命令语法是正确的。要在 bash 中从脚本运行命令并捕获结果,请使用以下语法:

cmd_string="ls"
result=$($cmd_string)
echo $result

【讨论】:

    【解决方案2】:

    我来到这里是因为当我想构建一个命令行并且我的参数包含空格时,它是唯一的 google SO。

    解决方案是使用 bash 数组:

    #! /bin/bash
    # using /bin/sh here might work if sh is broken or mapped to bash but this is not compatible with a real sh.
    MY_ARGS=( "--HI" "--there" "--With spaces" )
    if [ "X$OPTIONAL" != "X" ] ; then MY_ARGS+=( "$OPTIONAL" );fi
    MY_ARGS+=( "$@" )
    
    my_command "${MY_ARGS[@]}"
    

    改编自:http://mywiki.wooledge.org/BashFAQ/050

    【讨论】:

      【解决方案3】:

      与其将find 的输出通过grep 连接起来,不如使用find 的全部功能。您需要构建一个包含选项的 array

      -false -o -name '*string1*' ... -o -name '*stringn*'
      

      传递给find(其中string1 ... stringn 是作为参数传递的字符串):

      f() {
         local i args=( -false )
         for i; do
            args+=( -o -name "*$i*" )
         done
         find "${args[@]}"
      }
      

      我们使用-false 作为初始化器,因此构建选项数组很简单;这还有一个好处(或缺陷,取决于您的观点),如果没有给出任何选项,那么find 会提前退出,而不递归地列出目录的所有内容。

      使用grep,您可以使用正则表达式来获得更强大的匹配能力;这里我们使用find-name 选项,所以我们只能使用基本的glob:*?[...]。如果您的 find 支持 -regex 选项(GNU find 支持),并且如果您真的需要正则表达式,那么修改之前的函数很简单。


      另一种可能性是使用 Bash 的扩展 glob:

      f() (
         IFS='|' eval 'glob="$*"'
         shopt -s globstar extglob nullglob
         IFS=
         printf '%s\n' **/*@($glob)*
      )
      

      这里有几点需要注意:

      • 整个函数都包含在一个子shell 中——这不是错字。这是为了简化一些事情:不需要使用局部变量,也不需要保存 shell 选项以在函数结束时恢复它们。
      • 第一行使用 evil eval 但以一种安全的方式:它实际上是将位置参数的元素与IFS 的第一个字符连接起来的惯用方式(这里是一个管道字符)。
      • 我们需要将IFS设置为空字符串,以避免在glob **/*@($glob)*中分词。
      • glob **/*@($glob)* 使用 globstarextglob @($glob)(没有引号,这不是拼写错误)。请参阅参考手册中的Pattern Matching

      此函数使用 Bash 的扩展 glob,它不同于(但不如正则表达式强大)正则表达式(但这对于大多数情况来说应该足够了)。

      【讨论】:

        【解决方案4】:

        不要将整个命令放在一个字符串中;只需为grep 构建参数

        f () { 
          local grep_arg=''
          delim=''
          for var in "$@"; do
              grep_arg+="$delim$var"
              delim='\|'
          done
          echo "find | grep '$grep_arg'"
          find | grep "$grep_arg"
        }
        

        【讨论】:

        • 这是附加到grep_arg 每次我运行它(即grep_arg 的值仍然存在)。为什么会这样?
        • 对不起,变量在shell中默认是全局的。我会更新
        【解决方案5】:

        最通用的方式:你可以使用像“|”这样的符号(管道)和其他变量进入输入并从标准输出中获取结果。

        function runcmd(){
          eval "$1"
        }
        
        result=$(runcmd "Any command ${here}")
        

        【讨论】:

        • 用你在旅游回答开始时解释的管道添加一个例子可能很有趣?
        猜你喜欢
        • 1970-01-01
        • 2014-12-13
        • 1970-01-01
        • 1970-01-01
        • 2015-05-28
        • 2017-01-04
        • 2012-04-13
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多