在Bash中按字母顺序排列2个模式之间的行

时间:2015-11-26 21:30:42

标签: bash sorting awk sed

我想在Bash shell脚本中按字母顺序排列两种模式。

给出以下输入文件:

aaa
bbb
PATTERN1
foo
bar
baz
qux
PATTERN2
ccc
ddd

我期待作为输出:

aaa
bbb
PATTERN1
bar
baz
foo
qux
PATTERN2
ccc
ddd

首选工具是AWK“单线程”。 Sed和其他解决方案也被接受了。如果包含解释会很好。

8 个答案:

答案 0 :(得分:7)

这是使用asort()对GNU awk中的数组进行排序的完美案例:

gawk '/PATTERN1/ {f=1; delete a}
      /PATTERN2/ {f=0; n=asort(a); for (i=1;i<=n;i++) print a[i]}
      !f
      f{a[$0]=$0}' file

这使用与How to select lines between two marker patterns which may occur multiple times with awk/sed类似的逻辑,并添加:

  • 打印此范围之外的行
  • 存储此范围内的行
  • 当范围结束时,对它们进行排序和打印。

详细说明:

  • /PATTERN1/ {f=1; delete a}找到匹配PATTERN1的行时,设置一个标志,并清除行数组。
  • /PATTERN2/ {f=0; n=asort(a); for (i=1;i<=n;i++) print a[i]}找到匹配PATTERN2的行时,将标志设置为关闭。另外,对包含范围内所有行的数组a[]进行排序并打印它们。
  • !f如果标志关闭(即在范围之外),则评估为True,以便打印该行。
  • f{a[$0]=$0}如果该标志处于启用状态,请将该行存储在数组a[]中,以便稍后可以使用该信息。

测试

▶ gawk '/PATTERN1/ {f=1} /PATTERN2/ {f=0; n=asort(a); for (i=1;i<=n;i++) print a[i]} !f; f{a[$0]=$0}' FILE
aaa
bbb
PATTERN1
bar
baz
foo
qux
PATTERN2
ccc
ddd

答案 1 :(得分:4)

您可以将sedheadtail

一起使用
{
    sed '1,/^PATTERN1$/!d' FILE
    sed '/^PATTERN1$/,/^PATTERN2$/!d' FILE | head -n-1 | tail -n+2 | sort
    sed '/^PATTERN2$/,$!d' FILE
} > output

第一行打印从第1行到PATTERN1的所有内容。

第二行取PATTERN1PATTERN2之间的行,删除最后一行和第一行,并对剩余的行进行排序。

第三行打印从PATTERN2到文件末尾的所有内容。

答案 2 :(得分:3)

更复杂,但可能会减轻存储大量行的内存负担(你的cfg文件必须非常庞大才能解决这个问题,但不过......)。使用GNU awk和排序协处理:

<head>
<link rel="stylesheet" href="{{ url_for('static', filename='fancybox/jquery.fancybox.css') }}" type="text/css" media="screen" />
<link rel="stylesheet" href="{{ url_for('static', filename='fancybox/helpers/jquery.fancybox-buttons.css') }}" type="text/css" media="screen" />
<link rel="stylesheet" href="{{ url_for('static', filename='fancybox/helpers/jquery.fancybox-thumbs.css') }}" type="text/css" media="screen" />

<!--<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.7/jquery.min.js"></script>-->
<script type="text/javascript" src="http://code.jquery.com/jquery-latest.min.js"></script>
<!--<script type="text/javascript" src="{{ url_for('static', filename='js/lib/jquery-1.11.1.min.js') }}"></script>-->
<script type="text/javascript" src="{{ url_for('static', filename='js/lib/jquery.mousewheel-3.0.6.pack.js') }}"></script>
<script type="text/javascript" src="{{ url_for('static', filename='js/lib/alertify.min.js') }}"></script>
<script src="{{ url_for('static', filename='js/lib/jquery.slides.min.js') }}"></script>
<script type="text/javascript" src="{{ url_for('static', filename='fancybox/jquery.fancybox.js') }}"></script>
<script type="text/javascript" src="{{ url_for('static', filename='fancybox/helpers/jquery.fancybox-buttons.js') }}"></script>
<script type="text/javascript" src="{{ url_for('static', filename='fancybox/helpers/jquery.fancybox-thumbs.js') }}"></script>
<script type="text/javascript" src="{{ url_for('static', filename='fancybox/helpers/jquery.fancybox-media.js') }}"></script>
    <script>
    $(function() {
        $(".signup-form").fancybox({
            autoSize: false,
            fitToView: false,
            width: 813,
            height : 603,
            minWidth: 813,
            minHeight : 603,
            autoCenter: false,
            autoScale: false,
            autoDimensions: false,
            type: "iframe"
//                afterLoad: function () {
//                    this.width = $(this.element).data("width");
//                    this.height = $(this.element).data("height");
//                }
        });
    }
    </script>
</head>

<body>
  ...
<!-- trigger -->
    <div class="sign-up"><a class="signup-form" data-fancybox-type="iframe" href="/signup" id="global-signup-link"><span>Sign up</span></a></div>
  ...
</body>

答案 3 :(得分:2)

有点不合常规,但是使用Vim:

vim -c 'exe "normal /PATTERN1\<cr>jV/PATTERN2\<cr>k: ! sort\<cr>" | wq!' FILE

\<cr>是回车符,先输入 CTRL - v ,然后输入 CTRL - M

进一步的解释:

  • 使用vim正常模式,
  • /PATTERN1\<cr>-搜索第一个模式
  • j-转到下一行
  • V-进入视觉模式
  • /PATTERN2\<cr>-搜索第二个模式
  • k-返回上一行
  • : ! sort\<cr>-对您刚刚选择的可视文本进行排序
  • wq!-保存并退出

答案 4 :(得分:1)

这是一个小巧且易于理解的shell脚本,用于对两种模式之间的行进行排序:

public float nightvalue = 0.4f;

public void Night()
{
    // Change light intensity to nightvalue.
    GL.GetComponent<Light2D>().intensity = nightvalue;
}

用法:<脚本路径> <输入文件> <输出文件>。

模式是硬编码在文件中的,可以根据需要进行更改(或用作参数)。此外,它还会创建一个临时文件来对中间数据进行排序( .temp.for_sort)

算法:

从状态= 0开始并逐行读取文件。

在状态0下,将行写入输出文件,如果遇到START_PATTERN,则状态设置为1。

在状态1中,如果行不是STOP_PATTERN,则将行写入临时文件 在状态1中,如果line为STOP_PATTERN,则对临时文件进行排序,将临时文件的内容附加到输出文件(并删除临时文件),并将STOP_PATTERN写入输出文件。另外,将状态更改为0。

最后,如果临时文件中遗留了某些内容(缺少STOP_PATTERN的情况),请将临时文件的内容写入输出文件

答案 5 :(得分:0)

使用GNU sed(取决于Q命令),由@choroba提出的解决方案:

{
  sed -n '1,/PATTERN1/p' FILE
  sed   '1,/PATTERN1/d; /PATTERN2/Q' FILE | sort
  sed -n '/PATTERN2/,$p' FILE
}

说明:

  • 使用p会在$'1,/PATTERN1/p'中分别打印1到/ PATTERN1 /(含)和(/PATTERN2/,$p是文件结尾)范围内的一行。 / li>
  • 使用-n会禁用打印所有行的默认行为。与p结合使用。
  • 在中间一行,d命令用于删除与/ PATTERN2匹配的第一行的/ PATTERN1 /的行1,以及Q(不打印退出,仅GNU sed) /。这些是要排序的行,因此被送入sort

答案 6 :(得分:0)

显然,这不如GNU AWK解决方案,但都是如此,这是GNU sed解决方案:

sed '
/PATTERN1/,/PATTERN2/ {
  /PATTERN1/b    # branch/break if /PATTERN1/. This line is printed
  /PATTERN2/ {   # if /PATTERN2/,
    x                    # swap hold and pattern spaces
    s/^\n//              # delete the leading newline. The first H puts it there
    s/.*/sort <<< "&"/e  # sort the pattern space by calling Unix sort
    p                    # print the sorted pattern space
    x                    # swap hold and pattern space again to retrieve PATTERN2
    p                    # print it also
  }
  H   # Append the pattern space to the hold space
  d   # delete this line for now - it will be printed in the block above
}
' FILE

请注意,我依赖e命令(GNU扩展)。

测试:

▶ gsed '
/PATTERN1/,/PATTERN2/ {
  /PATTERN1/b
  /PATTERN2/ {
    x
    s/^\n//; s/.*/sort <<< "&"/ep
    x
    p
  }
  H
  d
}
' FILE
aaa
bbb
PATTERN1
bar
baz
foo
qux
PATTERN2
ccc
ddd

答案 7 :(得分:0)

这也可以使用 non-GNU awk 和系统命令 sort 来完成,使其在 macOS 和 Linux 上都能运行。

awk -v SP='PATTERN1' -v EP='PATTERN2' -v cmd=sort '{
if (match($0, SP)>0) {flag=1}
else if (match($0, EP)>0) {
   for (j=0;j<length(a);j++) {print a[j]|cmd}
   close(cmd); delete a; i=0; flag=0}
else if (flag==1) {a[i++]=$0; next}
print $0
}' FILE

输出:

aaa
bbb
PATTERN1
bar
baz
foo
qux
PATTERN2
ccc
ddd