【问题标题】:bash functions with loops and pipes带有循环和管道的 bash 函数
【发布时间】:2014-05-01 15:06:28
【问题描述】:

我有一个 bash 脚本,它将文件的内容通过管道传输到一系列用户定义的函数中,每个函数在标准输入上执行 sed 操作,将输出发送到标准输出。

例如:

#!/bin/bash

MOD_config ()
{
  sed 's/config/XYZ/g'
}

MOD_ABC ()
{
  sed 's/ABC/WHOA!/g'
}

cat demo.txt \
 | MOD_config \
 | MOD_ABC

到目前为止一切顺利。一切都很好。

现在我想允许通过脚本的命令行指定额外的模式更改对。例如,我想允许用户运行任何这些:

demo.sh                           # changes per script (MOD_config and MOD_ABC)
demo.sh CDE 345                   # change all 'CDE' to '345'
demo.sh CDE 345 AAAAA abababa     #  .. also changes 'AAAAA' to 'abababa'

所以我尝试将其添加到脚本中:

USER_MODS ()
{

  if [ $# -lt 1]; then

    #just echo stdin to stdout if no cmd line args exist
    grep .

  else

    STARTING_ARGC=$#
    I=1

    while [ $I -lt $STARTING_ARGC ]; then

      sed "s/$1/$2/g"

      shift
      shift
      I=`expr $I + 2`

    done

  fi

}

cat demo.txt \
  | MOD_config \
  | MOD_ABC \
  | USER_MODS

这种方法只有在我没有命令行参数或者只有两个时才有效。但是,在命令行中添加额外的 args 没有任何效果。

不确定如何将 while 循环的一次迭代的标准输出发送到下一次迭代的标准输入。我认为这是我的问题的症结所在。

有解决办法吗?还是我应该采取完全不同的方法?

【问题讨论】:

    标签: bash sed pipe user-defined-functions


    【解决方案1】:

    要获得动态的管道列表,您需要一个递归解决方案。有一个函数应用一组修改,然后用更少的两个参数调用自己。如果函数没有参数,那么只需调用cat 将标准输入复制到标准输出不变。

    mod() {
        if (($# >= 2)); then
            search=$1
            replace=$2
            shift 2
    
            sed "s/$search/$replace/g" | mod "$@"
        else
            cat
        fi
    }
    
    # Apply two base modifications, plus any additional user mods ("$@")
    mod config XYZ ABC 'WHOA!' "$@"
    

    【讨论】:

      【解决方案2】:

      备注:使用超过 2 个参数时,您的 seds 会被执行,但在第一个参数已经消耗完所有输入之后。相反,您想建立一个 sed 命令链。

      #!/bin/bash
      
      mod_config() { sed 's/config/XYZ/g'; }
      mod_abc() { sed 's/ABC/WHOA!/g'; }
      
      user_mods() {
        local IFS=';'
        local sed_subs=()
        while (($#>=2)); do
          sed_subs+=( "s/$1/$2/g" )
          shift 2
        done
        # at this point you have an array of sed s commands (maybe empty!).
        # Just join then with a semi colon using the IFS already set
        sed "${sed_subs[*]}"
      }
      
      cat demo.txt \
        | mod_config \
        | mod_abc \
        | user_mods "$@"   # <--- don't forget to pass the arguments to your function
      

      并祈祷您的用户不会输入会混淆 sed 的内容,例如斜线!

      (对不起,我把你所有的变量都小写了。大写太丑了)。

      【讨论】:

        【解决方案3】:

        试试这个,使用递归调用每次调用 USER_MODS 的替换对列表。

        #!/bin/bash
        
        MOD_config ()
        {
          sed 's/config/XYZ/g'
        }
        
        MOD_ABC ()
        {
          sed 's/ABC/WHOA!/g'
        }
        
        USER_MODS ()
        {
        
          if [ $# -lt 1 ]; then
        
            #just echo stdin to stdout if no args exist
            grep .
        
          else
        
              # grap the next two arguments
              arg1=$1
              arg2=$2
              # remove them from the argument list
              shift
              shift
              # do the replacement for these two and recursivly pipe to the function with
              # the new argument list
              sed "s/$arg1/$arg2/g" | USER_MODS $@
        
          fi
        }
        
        cat demo.txt \
         | MOD_config \
         | MOD_ABC \
         | USER_MODS $@
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2020-04-19
          • 1970-01-01
          • 1970-01-01
          • 2011-08-26
          • 1970-01-01
          • 2014-08-06
          • 2020-08-12
          • 1970-01-01
          相关资源
          最近更新 更多