2

所以我有一个文件,其中包含如下所示的块:

menuentry ... {
....
....
}

....

menuentry ... {
....
....
}

我需要查看 bash 脚本中每个菜单项的内容。我对 sed 的经验非常有限,但通过非常详尽的搜索,我能够构建以下内容:

cat $file | sed '/^menuentry.*{/!d;x;s/^/x/;/x\{1\}/!{x;d};x;:a;n;/^}/!ba;q'

我可以用\{1\}我想获得第 n 个块的任何数字替换。这样可以正常工作,但问题是我需要迭代任意次数:

numEntries=$( egrep -c "^menuentry.*{" $file ) 
for i in $( seq 1 $numEntries); do

    i=$( echo $i | tr -d '\n' ) # A google search indicated sed  encounters problems
                                # when a substituted variable has a trailing return char

    # Get the nth entry block of text with the sed statement from before, 
    # but replace with variable $i
    entry=$( cat $file | sed '/^menuentry.*{/!d;x;s/^/x/;/x\{$i\}/!{x;d};x;:a;n;/^}/!ba;q')

    # Do some stuff with $entry #

done

我已经尝试过在变量周围和 sed 语句周围使用引号/双引号和大括号的每一种组合,无论我这样做,我都会遇到某种错误。正如我所说,我对 sed 知之甚少,而科学怪人的声明正是我从各种谷歌搜索中设法混杂在一起的,所以任何帮助或解释都将不胜感激!

TIA

4

3 回答 3

2

sed 用于在单独的行上进行简单的替换,仅此而已,用于操作文本的 shell 循环非常缓慢且难以健壮地编写。发明 sed 和 shell 的人也发明了 awk 来完成这样的任务:

awk -v RS= 'NR==3' file

将打印第三个空白行分隔的文本块,如您的问题所示。这将打印包含字符串“foobar”的每个块:

awk -v RS= '/foobar/' file

您可能想做的任何其他事情同样微不足道。

以上将在任何 UNIX 机器上的任何 shell 中使用任何 awk 高效、健壮和可移植地工作。例如:

$ cat file
menuentry first {
    Now is the Winter
    of our discontent
}

menuentry second {
    Wee sleekit cowrin
    timrous beastie,
    oh whit a panic's in
    thy breastie.
}

menuentry third {
    Twas the best of times
    Twas the worst of times
    Make up your damn mind
}

.

$ awk -v RS= 'NR==3' file
menuentry third {
    Twas the best of times
    Twas the worst of times
    Make up your damn mind
}

$ awk -v RS= 'NR==2' file
menuentry second {
    Wee sleekit cowrin
    timrous beastie,
    oh whit a panic's in
    thy breastie.
}

$ awk -v RS= '/beastie/' file
menuentry second {
    Wee sleekit cowrin
    timrous beastie,
    oh whit a panic's in
    they breastie.
}

如果您发现自己尝试使用 sed 和/或使用 s、g 和 p 以外的 sed 命令(使用 -n)来执行 s/old/new 以外的任何操作,那么您使用的结构在 1970 年代中期 awk 时已过时被发明。

如果上述方法对您不起作用,请编辑您的问题以提供更具代表性的示例输入和预期输出。

于 2018-02-24T19:10:28.013 回答
0

我看到了问题。您正在尝试在单引号内使用 $i 。$i 将保留为 $i 正如我在下面发布的那样,用 $i 周围的单引号对其进行更正,以便 bash 将其视为 3 个字符串

'一个字符串'"$twostrings"'三个字符串'

#!/bin/bash
#filename:grubtest.sh

numEntries=$( egrep -c "^menuentry.*{" "$1" )
i=0 
while [ $i -lt $numEntries ]; do
    i=$(($i+1))
    # Get the nth entry block of text with the sed statement from before, 
    entry=$( cat "$1" | sed '/^menuentry.*{/!d;x;s/^/x/;/x\{'"$i"'\}/!{x;d};x;:a;n;/^}/!ba;q')

    # Do some stuff with $entry #
    echo $entry|cut -d\' -f2

done

当这样运行时,这会返回我的 grub 项目 ./grubtest.sh /etc/grub2.cfg

于 2018-02-23T18:30:15.593 回答
0

把这项工作分开怎么样?

首先搜索行,一个菜单项开始的地方(出于个人原因,我这里用的是grub,不是grub2,根据你的需要调整):

entrystarts=($(sed -n '/^menuentry.*/=' /boot/grub/grub.cfg))

然后,在第二步中,从数组中为第 n 个条目选择一个起始 val${entrystarts[$n]}并从那里继续?Afaik,入口端很容易通过一个右花括号检测到。

for i in ${entrystarts[@]}
do 
    // your code here, proof of concept (note grub/grub2):
    sed -n "$i,/}/p" /boot/grub/grub.cfg
done
于 2018-02-23T21:05:21.807 回答