追加到函数的最后(或第 n)行(使用 awk/sed)

Append to last (or n-th) line of function (using awk/sed)

最好使用 awk、sed 或类似的工具,我该如何实现 append_last_line,例如。在函数体的 last 行附加一些东西,它可以在给定文件的任何地方?

# file.sh
foo ()
{
  echo "bar"
}

$ append_last_line 'foo' 'echo "baz"' file.sh

# file.sh
foo ()
{
  echo "bar"
  echo "baz"
}

n-th 行呢?

# file.sh
foo ()
{
  echo "bar"
  echo "baz"
}

$ append_nth_line 1 'foo' 'echo "qux"' file.sh

# file.sh
foo ()
{
  echo "baz"
  echo "qux"
  echo "bar"
}

让您入门的东西:

awk -v name=foo -v num=2 -v what='echo "qux"' '
   # Print the line and read next line and handle getline error
   function print_getline() {
        print;
        if (getline <= 0) exit 1;
   }
   [=10=] ~ name "[[:space:]]*()"{                     # match the function followed by ()
          while ([=10=] !~ "{") { print_getline(); }   # wait for {
          while (num--) { print_getline(); }       # omit num count lines
          print what;                              # add the line you want to add
   }
   { print }   # print all other lines
' <<EOF
# file.sh
foo ()
{
  echo "bar"
  echo "baz"
}
EOF

将输出:

# file.sh
foo ()
{
  echo "bar"
echo "qux"
  echo "baz"
}

网上对awk的介绍很多。 awk 是一门伟大的语言。但是,如果您打算编写完整的 shell 语言解析器来处理所有极端情况(即 echo "foo () {" 不是函数定义...),请考虑使用更强大的语言,如 perl 或 python.

显然,您为此编写的任何脚本都是脆弱的,因为您需要一个用于编写目标文件的语言的解析器才能稳健地完成这项工作,但这里有一些东西可以用您想要的示例来做提供:

$ cat tst.sh
#!/usr/bin/env bash

(( $# > 3 )) && { tgtLineNr=; shift; }
tgtFunc=
newText=
shift 2

awk -v tgtLineNr="$tgtLineNr" -v tgtFunc="$tgtFunc" -v newText="$newText" '

    state == "gotFuncBeg" {
        if ( /^}/ ) { state = "gotFuncEnd" }
        if ( (lineNr++ == tgtLineNr) || ((state == "gotFuncEnd") && !tgtLineNr) ) {
            sub(/[^[:space:]].*/,"",indent)
            print indent newText
        }
        if ( NF ) { indent = [=10=] }
    }

    (state == "gotFuncName") && /^{/ {
        state = "gotFuncBeg"
        lineNr = 0
    }

    /^[[:alpha:]_][[:alnum:]_]*[[:space:]]*\(/ {
        thisFunc = [=10=]
        sub(/[[:space:]]*\(.*/,"",thisFunc)
        state = (thisFunc == tgtFunc ? "gotFuncName" : "")
    }

    { print }
' "$@"

$ cat file
# file.sh
foo ()
{
  echo "bar"
  echo "baz"
}

$ ./tst.sh 'foo' 'echo "qux"' file
# file.sh
foo ()
{
  echo "bar"
  echo "baz"
  echo "qux"
}

$ ./tst.sh 1 'foo' 'echo "qux"' file
# file.sh
foo ()
{
  echo "bar"
  echo "qux"
  echo "baz"
}

感谢您提供的精彩脚本,伙计们!我一定会深入研究它们。

我最终根据来自 @rturrado

编写了这个脚本
$ cat append-nth-line.sh
#!/bin/bash
IFS=''

function_name=
nth=
append=
out=

declare function_start_line
declare function_end_line

search="on"
i=1

while read -r line; do
  echo $line >>tmp1
  if [[ $(echo $line | xargs) =~ ^($function_name[[:space:]]*?\(\)) ]]; then
    function_start_line=$i
  fi
  if [ $function_start_line ]; then
    if [[ $(echo $line | xargs) =~ ^} && $search = "on" ]]; then
      function_end_line=$i
      search="off"
    fi
  fi
  i=$(($i + 1))
done

if [[ "$nth" =~ ^- ]]; then
  find=$(($function_end_line + $nth))
else
  find=$(($function_start_line + $nth))
fi

i=1

cat tmp1 |
  while read -r line; do
    echo $line >>tmp2
    if [[ $i = $find ]]; then
      indent=$(echo $line | grep -o ^'[[:space:]]*')
      if [[ ! "$nth" =~ ^- && $(echo $line | xargs) =~ \{|then|else|do ]]; then
        indent="$indent  "
      fi
      echo "$indent$append" >>tmp2
    fi
    i=$(($i + 1))
  done

rm tmp1
chmod +x tmp2
mv tmp2 $out

$ cat file.sh
foo()
{
  echo "bar"
}

$ append_nth_line.sh "foo" -1 'echo "baz"' file.sh  < file.sh 
$ cat file.sh
foo()
{
  echo "bar"
  echo "baz"
}

$ append_nth_line.sh "foo" 1 'echo "qux"' file.sh  < file.sh 
$ cat file.sh
foo()
{
  echo "qux"
  echo "bar"
  echo "baz"
}

我还有一些极端情况需要解决,但这个或您的脚本就可以了。

欢迎反馈。

… sed or similar

使用

set -- filename funcname -1 '  echo "quux"'
printf '%s\n' '/^'""'[ (]/;#' '/^}/;#' ""a "" . w q | ed -rs ""

查找 funcname,然后查找尾随 },在 } (-1) 前一行,最后保存编辑后的文件。

最后一行调用 -1,倒数第三行调用 -3 ...

这可能对你有用(GNU sed & bash):

# append_last_line 'foo' 'echo "baz"' file.sh
append_last_line (){ sed '/^''\s*()/{:a;h;n;/^}$/!ba;x;s/\S.*/'""'/p;x}' ""; }

# append_nth_line 2 'foo' 'echo "baz"' file.sh N.B. n must be natural number > 0
append_nth_line (){ sed -E '/^''\s*\(\)/{n;n;:a;N;/^}/M!ba;s/(((\s*)[^\n]*(\n)){''})/&'""'/}' ""; }