删除等号后的多行字符串
Remove mutiline string after equal sign
我正在尝试从 AWS 获取 IAM 策略并将其添加到以下特定格式的文本文件中。我想删除文件中“}”括号结束前的 policy= 之后的所有内容。
这是我的文本文件样本。但是原始文件可以在同一个文件中有多个 example_policy 实例。
"example1_policy" {
name="example"
policy=jsonencode(
{
Statement=[
{
Action=[
s3:*
]
Effect="Allow"
},
]
Version="2012-10-17"
}
)
}
"example2_policy" {
name="example2"
policy=jsonencode(
{
Statement=[
{
Action=[
s3:*
]
Effect="Allow"
},
]
Version="2012-10-17"
}
)
}
"example3_policy" {
name="example3"
policy=jsonencode(
{
Statement=[
{
Action=[
s3:*
]
Effect="Allow"
},
]
Version="2012-10-17"
}
)
}
预期输出:
"example1_policy" {
name="example1"
policy=
}
"example2_policy" {
name="example2"
policy=
}
"example3_policy" {
name="example3"
policy=
}
或
"example1_policy" {
name="example1"
policy=<placeholder>
}
"example2_policy" {
name="example2"
policy=<placeholder>
}
"example3_policy" {
name="example3"
policy=<placeholder>
}
根据@Wiktor 的评论,我尝试了这个命令
sed -i '/policy=/,/^\s*)\s*$/d' test.txt
输出:policy= 应该保持不变。
"example_policy" {
name="example"
}
你可以在 Python 中很容易地做到这一点,因为你有状态:
def clean(s, pattern='policy='):
pre, post = s.split(pattern)
harmony = True
quote = False
braces = 0
for i, char in enumerate(post):
if harmony and char == '\n':
return f'{pre}{pattern}{post[i:]}'
if not quote:
if char == '(':
braces += 1
elif char == ')':
braces -= 1
elif char in ('"', "'"):
quote = char
harmony = braces == 0
elif quote == char:
quote = False
这甚至会忽略包含在字符串(" 和 ' 字符串)中的大括号。
所以,这个版本也适用于更棘手的字符串:
"example_policy" {
name="example"
policy=jsonencode(
{
Statement=[
{
Action=[
s3:*
]
Effect="Al)l'ow"
},
]
Version='"2012-10)-17"'
}
)
}
您可以轻松扩展它以支持其他类型的大括号或引号。唯一的区别是你需要为大括号使用计数器,因为开始和结束字符不同,而对于引号,你只需要在匹配列表中添加额外的字符——因为引号中的其他所有内容都被忽略,你只需要记住报价是用哪个字符打开的。
用正则表达式做这件事会比较麻烦,因为它们只支持有限大括号嵌套。
要删除同一个字符串中的多个策略,我们需要定义一个辅助函数:
def clean(s):
harmony = True
quote = False
braces = 0
for i, char in enumerate(s):
if harmony and char == '\n':
return s[i:]
if not quote:
if char == '(':
braces += 1
elif char == ')':
braces -= 1
elif char in ('"', "'"):
quote = char
harmony = braces == 0
elif quote == char:
quote = False
return s
然后是将“清洁助手”应用于每个单独块的主要功能:
def clean_all(s, pattern='policy='):
head, *tail = s.split(pattern)
return f'{head}{pattern}{pattern.join(clean(part) for part in tail)}'
您可以使用以下 GNU sed 命令:
sed -i '/policy=/,/^\s*)\s*$/{/policy=/!d};s/\(policy=\).*/<placehlder>/' file
参见online demo。 详情:
/policy=/,/^\s*)\s*$/
- 查找带有 policy=
的行和仅包含一个 )
字符并用零个或多个空格 包围的行之间的行块
{/policy=/!d}
- 防止删除找到的块中的第一行并删除其他行
s/\(policy=\).*/<placehlder>/
- 将 policy=
之后的所有内容替换为 <placeholder>
。
如果需要匹配 policy=
,那么任何字符到 (
然后 到下一个平衡的 )
char,你可以使用perl
命令,如
perl -0777 -i -pe 's/^(\h*policy=)[^()]*(\((?:[^()]++|(?2))*\))/<placehlder>/gm' file
详情:
-0777
- 将文件读入单个字符串,以便换行符和行
-i
- 内联文件更改
^
- 行首(由于 m
标志)
(\h*policy=)
- 第 1 组 (</code>):零个或多个水平空格</li>
<li><code>[^()]*
- (
和 )
以外的零个或多个字符
(\((?:[^()]++|(?2))*\))
- 第 2 组(用于递归):\(
- 一个 (
字符,(?:[^()]++|(?2))*
- 一个或多个字符的零个或多个序列 (
和 )
或整个第 2 组模式递归,并且 \)
匹配 )
char
<placehlder>
- 替换为组 1 + <placeholder>
字符串
见online demo and the regex demo:
#!/bin/bash
s='"example1_policy" {
name="example"
policy=jsonencode(
{
Statement=[
{
Action=[
s3:*
]
Effect="Allow"
},
]
Version="2012-10-17"
}
)
}
"example2_policy" {
name="example2"
policy=jsonencode(
{
Statement=[
{
Action=[
s3:*
]
Effect="Allow"
},
]
Version="2012-10-17"
}
)
}
"example3_policy" {
name="example3"
policy=jsonencode(
{
Statement=[
{
Action=[
s3:*
]
Effect="Allow"
},
]
Version="2012-10-17"
}
)
}'
perl -0777 -pe 's/^(\h*policy=)[^()]*(\((?:[^()]++|(?2))*\))/<placehlder>/gm' <<< "$s"
输出:
"example1_policy" {
name="example"
policy=<placehlder>
}
"example2_policy" {
name="example2"
policy=<placehlder>
}
"example3_policy" {
name="example3"
policy=<placehlder>
}
我正在尝试从 AWS 获取 IAM 策略并将其添加到以下特定格式的文本文件中。我想删除文件中“}”括号结束前的 policy= 之后的所有内容。
这是我的文本文件样本。但是原始文件可以在同一个文件中有多个 example_policy 实例。
"example1_policy" {
name="example"
policy=jsonencode(
{
Statement=[
{
Action=[
s3:*
]
Effect="Allow"
},
]
Version="2012-10-17"
}
)
}
"example2_policy" {
name="example2"
policy=jsonencode(
{
Statement=[
{
Action=[
s3:*
]
Effect="Allow"
},
]
Version="2012-10-17"
}
)
}
"example3_policy" {
name="example3"
policy=jsonencode(
{
Statement=[
{
Action=[
s3:*
]
Effect="Allow"
},
]
Version="2012-10-17"
}
)
}
预期输出:
"example1_policy" {
name="example1"
policy=
}
"example2_policy" {
name="example2"
policy=
}
"example3_policy" {
name="example3"
policy=
}
或
"example1_policy" {
name="example1"
policy=<placeholder>
}
"example2_policy" {
name="example2"
policy=<placeholder>
}
"example3_policy" {
name="example3"
policy=<placeholder>
}
根据@Wiktor 的评论,我尝试了这个命令
sed -i '/policy=/,/^\s*)\s*$/d' test.txt
输出:policy= 应该保持不变。
"example_policy" {
name="example"
}
你可以在 Python 中很容易地做到这一点,因为你有状态:
def clean(s, pattern='policy='):
pre, post = s.split(pattern)
harmony = True
quote = False
braces = 0
for i, char in enumerate(post):
if harmony and char == '\n':
return f'{pre}{pattern}{post[i:]}'
if not quote:
if char == '(':
braces += 1
elif char == ')':
braces -= 1
elif char in ('"', "'"):
quote = char
harmony = braces == 0
elif quote == char:
quote = False
这甚至会忽略包含在字符串(" 和 ' 字符串)中的大括号。
所以,这个版本也适用于更棘手的字符串:
"example_policy" {
name="example"
policy=jsonencode(
{
Statement=[
{
Action=[
s3:*
]
Effect="Al)l'ow"
},
]
Version='"2012-10)-17"'
}
)
}
您可以轻松扩展它以支持其他类型的大括号或引号。唯一的区别是你需要为大括号使用计数器,因为开始和结束字符不同,而对于引号,你只需要在匹配列表中添加额外的字符——因为引号中的其他所有内容都被忽略,你只需要记住报价是用哪个字符打开的。
用正则表达式做这件事会比较麻烦,因为它们只支持有限大括号嵌套。
要删除同一个字符串中的多个策略,我们需要定义一个辅助函数:
def clean(s):
harmony = True
quote = False
braces = 0
for i, char in enumerate(s):
if harmony and char == '\n':
return s[i:]
if not quote:
if char == '(':
braces += 1
elif char == ')':
braces -= 1
elif char in ('"', "'"):
quote = char
harmony = braces == 0
elif quote == char:
quote = False
return s
然后是将“清洁助手”应用于每个单独块的主要功能:
def clean_all(s, pattern='policy='):
head, *tail = s.split(pattern)
return f'{head}{pattern}{pattern.join(clean(part) for part in tail)}'
您可以使用以下 GNU sed 命令:
sed -i '/policy=/,/^\s*)\s*$/{/policy=/!d};s/\(policy=\).*/<placehlder>/' file
参见online demo。 详情:
/policy=/,/^\s*)\s*$/
- 查找带有policy=
的行和仅包含一个)
字符并用零个或多个空格 包围的行之间的行块
{/policy=/!d}
- 防止删除找到的块中的第一行并删除其他行s/\(policy=\).*/<placehlder>/
- 将policy=
之后的所有内容替换为<placeholder>
。
如果需要匹配 policy=
,那么任何字符到 (
然后 到下一个平衡的 )
char,你可以使用perl
命令,如
perl -0777 -i -pe 's/^(\h*policy=)[^()]*(\((?:[^()]++|(?2))*\))/<placehlder>/gm' file
详情:
-0777
- 将文件读入单个字符串,以便换行符和行-i
- 内联文件更改^
- 行首(由于m
标志)(\h*policy=)
- 第 1 组 (</code>):零个或多个水平空格</li> <li><code>[^()]*
-(
和)
以外的零个或多个字符
(\((?:[^()]++|(?2))*\))
- 第 2 组(用于递归):\(
- 一个(
字符,(?:[^()]++|(?2))*
- 一个或多个字符的零个或多个序列(
和)
或整个第 2 组模式递归,并且\)
匹配)
char<placehlder>
- 替换为组 1 +<placeholder>
字符串
见online demo and the regex demo:
#!/bin/bash
s='"example1_policy" {
name="example"
policy=jsonencode(
{
Statement=[
{
Action=[
s3:*
]
Effect="Allow"
},
]
Version="2012-10-17"
}
)
}
"example2_policy" {
name="example2"
policy=jsonencode(
{
Statement=[
{
Action=[
s3:*
]
Effect="Allow"
},
]
Version="2012-10-17"
}
)
}
"example3_policy" {
name="example3"
policy=jsonencode(
{
Statement=[
{
Action=[
s3:*
]
Effect="Allow"
},
]
Version="2012-10-17"
}
)
}'
perl -0777 -pe 's/^(\h*policy=)[^()]*(\((?:[^()]++|(?2))*\))/<placehlder>/gm' <<< "$s"
输出:
"example1_policy" {
name="example"
policy=<placehlder>
}
"example2_policy" {
name="example2"
policy=<placehlder>
}
"example3_policy" {
name="example3"
policy=<placehlder>
}