用sed删除除括号之外的所有内容,然后缩进

时间:2012-09-25 15:19:36

标签: bash sed awk tr

我有一个巨大的文件,一个非常庞大的文件(大约600 + MB的文本)。实际上它们是jsons。每个json都在一个新行上,只有几种风格。

他们看起来像:

{"text":{"some nested words":"Some more","something else":"Yeah more stuff","some list":["itemA","ItemB","itemEtc"]},"One last object":{"a thing":"and it's value"}}

而我想要的是它通过sed,删除文本,并为每个nexted对放入一些缩进,所以我们得到:

{
 -{
--[]
 -}
--{}
 -}
}

(我不是100%肯定我在输出上得到了嵌套,我认为这是正确的)

这可能吗?我看到this,这是我能想象到的最接近的,但是它摆脱了括号2。

我注意到那里使用braching的答案,所以我认为我需要它,我需要做某种s/pattern/newline+tab/space/g类型的命令,但我无法弄清楚如何或如何制作...

有人可以帮忙吗?它不一定是纯粹的sed,但这是首选。

4 个答案:

答案 0 :(得分:5)

这不会很漂亮...... =)这是我作为sed脚本的解决方案。请注意,它要求第一行通知shell如何调用sed来执行我们的脚本。如你所见," -n"使用了标志,所以我们强制sed只打印我们明确命令通过" p"或" P"命令。 " -f"选项告诉sed从文件中读取命令,名称跟在选项后面。由于脚本的文件名由shell连接到最终命令,它将正确地从脚本中读取命令(即,如果你运行" ./ myscript.sed" shell将执行" ; / bin / sed -nf myscript.sed")。

#!/bin/sed -nf

s/[^][{}]//g

t loop
: loop

t dummy
: dummy

s/^\s*[[{]/&/
t open

s/^\s*[]}]/&\
/
t close
d

: open
s/^\(\s*\)[[]\s*[]]/\1[]\
/
s/^\(\s*\)[{]\s*[}]/\1{}\
/

t will_loop
b only_open

: will_loop
P
s/.*\n//
b loop

: only_open

s/^\s*[[{]/&\
/
P
s/.*\n//
s/[][{}]/ &/g
b loop

: close
s/ \([][{}]\)/\1/g
P
s/.*\n//
b loop

在开始之前,我们必须首先将所有内容放入括号和方括号中。这是第一个" s的责任。命令。它告诉sed用任何东西替换不是括号或方括号的每个字符,即。去掉它。请注意,匹配中的方括号表示要匹配的一组字符,但是当它们中的第一个字符是" ^"时,它实际上将匹配除&#34之后指定的字符之外的任何字符。 ; ^&#34 ;.因为我们想匹配结束的方括号,我们需要用方括号来关闭要忽略的字符组,我们告诉一个结束的方括号应该包含在组中,使它成为"之后的第一个字符。 ^&#34 ;.然后我们可以指定其余字符:打开方括号,左括号和右括号(忽略字符组:"] [{}"),然后用结束方括号关闭组。我试着在这里详细说明,因为这可能令人困惑。

现在为实际逻辑。算法非常简单:

while line isn't empty
    if line starts with optional spaces followed by [ or {
        if after the [ or { there are optional spaces followed by a respective ] or }
            print the respective pair, with only the indentation spaces, followed by a newline
        else
            print the opening square or normal bracket, followed by a newline
            remove what was printed from the pattern space (a.k.a. the buffer)
            add a space before every open or close bracket (normal or square)
        end-if
    else
        remove a space before every open or close bracket (normal or square)
        print the closing square or normal bracket, followed by a newline
        remove what was printed from the pattern space
    end-if
end-while

但有一些怪癖。首先,sed并不支持"而#34;循环或"如果"声明直接。我们最接近的是" b"和" t"命令。 " b"命令分支(跳转)到预定义的标签,类似于C goto语句。 " t"也分支到预定义的标签,但只有在自当前行上运行的脚本开始或自上一次" t"命令。标签使用":"命令。

因为第一个命令很可能实际上至少执行了一次替换,所以第一个" t"跟随它的命令将导致分支。因为我们需要测试其他一些替换,我们需要确保下一个" t"由于第一个命令,命令不会自动成功。这就是为什么我们从一个" t"命令到它上面的一条线(即,如果它分支或不分支,它仍然会在同一点继续),所以我们可以"重置" " t"使用的内部标志;命令。

因为"循环"标签将从至少一个" b"命令,当" b"时,可能会设置相同的标志。被执行,因为只有" t"命令可以清除它。因此,我们需要执行相同的解决方法来重置标志,这次使用" dummy"标签

我们现在通过检查是否存在开放式方括号或开放式近括号来启动算法。因为我们只想测试他们的存在,我们必须用自己替换匹配,这就是"&"表示,并且sed将自动设置" t"的内部标志。命令如果匹配成功。如果匹配成功,我们使用" t"命令进入"打开"标签

如果它没有成功,我们需要查看是否匹配方括号或正常括号。该命令几乎相同,但现在我们在结束括号后添加换行符。我们通过在放置匹配后添加转义换行符(即反斜杠后跟实际换行符)来实现此目的(即在#34;&"之后)。与上面类似,我们使用" t"命令分支到"关闭"如果匹配成功则标记。如果它没有成功,我们将该行视为无效,并立即清空模式空间(缓冲区)并在下一行重新启动脚本,所有这些都使用单个" d"命令。

输入"打开"标签,我们将首先处理一对匹配的打开和关闭括号的情况。如果我们匹配它们,我们将使用它们之前的缩进空间打印它们,它们之间没有任何空格,并以换行符结束。每种类型的支架对(正方形或普通)都有一个特定的命令,但它们是类似的。因为我们必须跟踪有多少缩进空间,我们必须将它们存储在一个特殊的变量"中。我们通过使用组捕获来完成此操作,该组捕获将存储在"("并在"之前结束)"之后开始的匹配部分。因此,我们使用它来捕获行开始后和开放括号之前的空格。然后我们继续匹配开放式支架,然后是空格和相应的紧密支架。当我们编写替换时,我们确保使用特殊变量" \ 1"重新插入空格,其中包含匹配中第一组捕获存储的数据。然后我们编写相应的一对开括号和近似括号以及转义的换行符。

如果我们设法进行任何替换,我们必须打印刚才写的内容,将其从模式空间中删除,然后使用该行的其余字符重新启动循环。因此,我们首先使用" t"命令" will_loop"标签。否则,我们分支到" only_open"标签,只处理一个开放式支架的情况,没有连续的相应的紧密支架。

在" will_loop"标签,我们只需打印图案空间中的所有内容,直到第一个换行符(我们手动添加)与" P"命令。然后我们手动删除第一个换行符之前的所有内容,以便继续执行该行的其余部分。这类似于" D"命令没有,但没有重新启动脚本的执行。最后,我们再次分支到循环的开始。

在" only_open"标签,我们以与以前类似的方式匹配一个开括号,但现在我们重写它附加一个换行符。然后我们打印该行并将其从模式空间中删除。现在我们将所有括号(打开或关闭,正方形或正常)替换为其前面有一个空格字符。这样我们就可以增加缩进量。最后,我们再次分支到循环的开头。

最终标签"关闭"将处理一个结束括号。我们首先删除括号前的每个空格,有效地减少缩进。为此,我们需要使用捕获,因为虽然我们想要匹配后面的空格和括号,但我们只想写回括号。最后,在输入"关闭"之前,将所有内容打印到我们手动添加的换行符。标签,删除我们从模式空间打印的内容并重新启动循环。

一些观察结果:

  1. 这不会检查代码的语法正确性(即{{[}}将被接受)
  2. 无论类型如何,它都会在遇到括号时添加和删除缩进。这意味着当它添加缩进时,即使遇到的闭括号不是同一类型,也会将其删除。
  3. 希望这会有所帮助,并为长篇文章感到抱歉=)

答案 1 :(得分:3)

这可能适合你(GNU sed):

sed 's/[^][{}]//g;s/./&\n/g;s/.$//' file |
sed -r '/[[{]/{G;s/(.)\n(.*)/\2\1/;x;s/^/\t/;x;b};x;s/.//;x;G;s/(.)\n(.*)/\2\1/' |
sed -r '$!N;s/((\{).*(\}))|((\[).*(\]))/\2\5\3\6/;P;D'

说明:

  1. 第一个sed命令在其自己的行上生成一个卷曲/方括号的流
  2. 第二个sed命令缩进每个括号
  3. 第三个sed命令将这些成对的括号缩减为一行
  4. 如果您对正确缩进的括号感到满意,则可以省略第三个命令。

答案 2 :(得分:2)

我认为您预期的输出应该如下:

{
-{
--[]
-}
-{
-}
}

这是使用GNU awk的一种方式:

awk -f script.awk file.txt

script.awk的内容:

BEGIN {
    FS=""
    flag = 0
}

{
    for (i=1; i<=NF; i++) {
        if ($i == "{" || $i == "[") {
            flag = flag + 1
            build_tree(flag, $i)
            printf (flag <=2) ? "\n" : ""
        }
        if ($i == "}" || $i == "]") {
            flag = flag - 1
            printf (flag >= 2) ? $i : \
                build_tree(flag + 1, $i); \
                printf "\n"
        }
    }
}

function build_tree (num, brace) {
    for (j=1; j<=num - 1; j++) {
        printf "-"
    }
    printf brace
}

答案 3 :(得分:2)

我知道这是一个古老的线索,无论如何都没有人在看,但现在有一种更简单的方法。

  

cat file.txt | jq'。' | sed's / / - / g'| tr -dc'[[] {}()] \ n-'| sed'/ ^ - * $ / d'

第一个sed中有2个空格。