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



最好使用awk, sed或类似的,我如何实现append_last_line,例如。在最后一个后面添加一些内容函数体的行,可以在给定文件中的任何地方?

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

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

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

第n个呢?行吗?

# 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"
}

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

$ cat tst.sh
#!/usr/bin/env bash
(( $# > 3 )) && { tgtLineNr=$1; shift; }
tgtFunc=$1
newText=$2
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 = $0 }
}
(state == "gotFuncName") && /^{/ {
state = "gotFuncBeg"
lineNr = 0
}
/^[[:alpha:]_][[:alnum:]_]*[[:space:]]*(/ {
thisFunc = $0
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"
}

让你开始:

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;
}
$0 ~ name "[[:space:]]*()"{                     # match the function followed by ()
while ($0 !~ "{") { 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。

谢谢大家的精彩脚本!我一定会深入研究的。

我最后根据@rturrado

的评论写了这个脚本
$ cat append-nth-line.sh
#!/bin/bash
IFS=''
function_name=$1
nth=$2
append=$3
out=$4
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或类似

使用艾德:

set -- filename funcname -1 '  echo "quux"'
printf '%sn' '/^'"$2"'[ (]/;#' '/^}/;#' "$3"a "$4" . w q | ed -rs "$1"

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

用-1表示最后一行,-3表示第三行

这可能适合您(GNU sed &bash):

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

最新更新