Bash / Sed - 多行sed操作打印行无序

时间:2017-03-03 18:25:04

标签: bash sed

使用sed编辑日志文件时遇到了一些麻烦。我把它构建成一个函数,它应该用另一个函数的输出替换两个搜索字符串之间的文本。它几乎正常工作,但是不按顺序将行打印到日志文件中。对于我的生活,我无法弄清楚为什么,并且我在尝试修复它时做出的大多数调整实际上都不太令人满意。

我的sed功能:

log_edit(){
"$3" > temp.txt
sed -i -n "/$1/{
:loop
n
/$2/!b loop
x
r temp.txt
G
s/$2/\n\n&/
}
p" "$FILE"
rm temp.txt
}

我正在使用" === text ==="将分隔符作为我的开始和停止字符串传递给函数,并使用相同的函数在第一个位置构建日志以填充临时文本文件。
问题发生在与G'相近/相关的地方。命令。它不是将保持模式行附加到字符串的末尾,而是将其附加到字符串的开头。

原始日志样本/所需输出:

=== Metech ITAMS Log ===

Metech Recycling
ITAMS Hardware Report
Date: Thu Mar  2 08:01:38 PST 2017
Tech: SP

=== Manufacturer Information ===

# dmidecode 2.12
...

不幸的是,我得到的输出看起来像这样:

=== Manufacturer Information ===

=== Metech ITAMS Log ===

Metech Recycling
ITAMS Hardware Report
Date: Fri Mar  3 09:39:02 PST 2017
Tech: SS

# dmidecode 2.12
...

有人能够帮助我理解我做错了什么,或者提出修复建议吗?这是我的第一个问题,如果有必要提供更多信息,我很乐意提供。提前致谢。

编辑#1:根据请求调用该函数的代码片段:

        2)
            printf "\n"
            text_prompt "Please enter Tech initials: "
            set_tech_id
            text_prompt "Please enter Traveler ID: "
            set_travel_id
            mv "$FILE" "$TRAVEL_ID $TECH_INITIALS"
            FILE="$TRAVEL_ID $TECH_INITIALS"
            log_edit "=== Metech ITAMS Log ===" \
"=== Manufacturer Information ===" "print_header" 
            unset TECH_INITIALS
            unset TRAVEL_ID
        ;;

这是菜单功能的一部分,包含整个内容会有点过分,只要知道会有几个带有不同开始/停止字符串的log_edit调用(尽管都遵循=== ===模式),但通常调用不同的函数来填充temp.txt。

编辑2:为了更加清晰,我想我应该添加用$ 3调用的函数:

print_header(){ #Prints log header.
print_div "Metech ITAMS Log"
printf "Metech Recycling\nITAMS Hardware Report\nDate: $(date)\nTech: %s\n" \
"$TECH_INITIALS"
}

和print_header调用print_div:

print_div(){ #Prints a divider.  Required parameter: $1=Text for divider.
printf "\n=== %s ===\n\n" "$1"
}

编辑3:为了清楚问题,我的问题是$ 2字符串是在temp.txt的内容之前写入日志,而不是之后。

最终编辑:找到了解决方案。我想我会发布下面的工作代码,以防它对别人有帮助。我的问题很大一部分是对sed如何使用' r'命令。此解决方案的另一部分来自我仍然不理解的已接受的答案,这是添加反斜杠的替代命令,这是使其工作的关键。我不知道它为什么会起作用,但确实如此。

log_edit() { #Works!!
"$3" > temp.txt
sed -i -n '/^'"$1"'$/ {
:loop
n
/^'"$2"'$/!b loop
i\
'"$(sed 's/\\/\\&/g;s/$/\\/' -- "temp.txt")"'

#Blank line terminates i command.
}
p' "$FILE"
rm temp.txt
}

3 个答案:

答案 0 :(得分:2)

r命令在下一次读取之前复制文件,而不是在评估它时,并且不修改模式空间。但是,该文件可以作为i命令的一部分插入到脚本中:

log_edit() {
    sed -n '/^'"$1"'$/ {
        p
        :loop
        n
        /^'"$2"'$/!bloop
        i\
'"$("$3" | sed 's/^[[:space:]]/\\&/;s/\\/\\&/g;s/$/\\/')"'

        # The blank line above is part of the `i' command,
        # and appends a newline to the inserted text.
    }
    p' "$FILE" > "$FILE.mod" && mv -f -- "$FILE.mod" "$FILE"
}

命令替换"$("$3" | sed '...')"过滤输出 $3的{​​{1}}与sed i命令一起使用。打印i命令 除了最后一个以\结尾的一系列行。

$ echo three | sed 'i\
> one\
> two
> '
one
two
three

答案 1 :(得分:1)

在那里看起来只是一些乱序。试试这个:

log_edit(){
"$3" > text.tmp
sed -i -n "/$1/{
r text.tmp
:loop
N
/$2/!b loop
s/.*\n/\n\n/g
}
p
" "$FILE"
rm text.tmp
}

print_header(){ #Prints log header.
    print_div "Metech ITAMS Log"
    printf "Metech Recycling\nITAMS Hardware Report\nDate: $(date)\nTech:%s" "$TECH_INITIALS"
}

print_div(){ #Prints a divider.  Required parameter: $1=Text for divider.
    printf "\n=== %s ===\n\n" "$1"
}

log_edit "=== Metech ITAMS Log ===" "=== Manufacturer Information ===" "print_header"

答案 2 :(得分:0)

尝试csplit程序,该程序可以根据模式将文件分成多个部分:

csplit $3 "/\($1\|$2\)/" "{*}"

这意味着接收文件$3,并将其分成文件xxNN(其中NN00开始,然后上升)根据无限数字划分的部分( {*}个模式$1$2(两个备用模式,由\|分隔,并按转义括号分组)。分界线将保留在输出中。然后,您可以编写辅助代码来删除不需要的文件。您还可以更改输出文件名和模式的名称。

# cat foo
a
b
@
c
d
%
e
f
@
g
h
%
i
j
# csplit foo '/\(@\|%\)/' '{*}'
4
6
6
6
6
# more xx0*
::::::::::::::
xx00
::::::::::::::
a
b
::::::::::::::
xx01
::::::::::::::
@
c
d
::::::::::::::
xx02
::::::::::::::
%
e
f
::::::::::::::
xx03
::::::::::::::
@
g
h
::::::::::::::
xx04
::::::::::::::
%
i
j

注意:如果您的分界线可能重复/无序发生,您需要调整它。这很简单;无论顺序如何,中断都发生在一个模式或另一个模式的任何一点上。