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

Pur*_*ret 2 bash awk sed 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"}}
Run Code Online (Sandbox Code Playgroud)

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

{
 -{
--[]
 -}
--{}
 -}
}
Run Code Online (Sandbox Code Playgroud)

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

这可能吗?我看到了这个,这是我能想象的最接近的,但是它摆脱了括号二.

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

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

Jan*_*lho 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
Run Code Online (Sandbox Code Playgroud)

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

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

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
Run Code Online (Sandbox Code Playgroud)

但有一些怪癖.首先,sed不直接支持"while"循环或"if"语句.我们可以得到的最接近的是"b"和"t"命令."b"命令分支(跳转)到预定义标签,类似于C goto语句."t"也分支到预定义的标签,但仅在从当前行或最后一个"t"命令运行的脚本开始以来发生了替换.标签使用":"命令编写.

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

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

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

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

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

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

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

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

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

一些观察:

  1. 这不会检查代码的语法正确性(即.{{[}}将被接受)
  2. 无论类型如何,它都会在遇到括号时添加和删除缩进.这意味着当它添加缩进时,即使遇到的闭括号不是同一类型,它也会删除它.

希望这有帮助,对不起长篇帖子=)