仅对文件的特定部分执行搜索和替换的最佳方法是什么?
What Is the Best Way to Perform a Search and Replace On only Specific Sections of a File?
我有一个 markdown 文件,其中的部分由标题分隔。我只想在特定部分执行搜索和替换;但是,每个部分都有相似的内容,因此全局搜索和替换最终会影响所有部分。因此,我需要以某种方式将搜索和替换限制为文件的某些部分。
例如,假设我想用 # Section 1
、# Section 3
下的 bar
替换 foo
的 所有 个实例,和 # Section 4
保持 # Section 2
和 # Section 5
不变,如下所示
示例输入:
# Section 1
- foo
- foo
- Unimportant Item
- foo
- Unimportant Item
# Section 2
- foo
- Unimportant Item
# Section 3
- foo
- Unimportant Item
# Section 4
- foo
- Unimportant Item
- foo
# Section 5
- foo
- foo
示例输出
# Section 1
- bar
- bar
- Unimportant Item
- bar
- Unimportant Item
# Section 2
- foo
- Unimportant Item
# Section 3
- bar
- Unimportant Item
# Section 4
- bar
- Unimportant Item
- bar
# Section 5
- foo
- foo
如果我不必担心各个部分,使用
进行全局搜索和替换将是微不足道的
sed -i 's/foo/bar/g' <input_file>
但我不确定 sed
是否能够检查上下文以允许我正在寻找的内容。
您可以使用这个 awk
:
awk 'p {sub(/foo$/, "bar")} /^#/ {p = / (Section [134])$/} 1' file
# Section 1
- bar
- bar
- Unimportant Item
- bar
- Unimportant Item
# Section 2
- foo
- Unimportant Item
# Section 3
- bar
- Unimportant Item
# Section 4
- bar
- Unimportant Item
- bar
# Section 5
- foo
- foo
为了使其更具可读性:
awk 'p { # if p==1 and current line # == n
sub(/foo$/, "bar") # replace foo with bar
}
/^#/ { # if line starts with #
p = / (Section [134])$/ # set p = 1/0 if it matches sections
} 1' file
每当您考虑 sed -i
时,我通常的建议是改用它的哥哥 ed
,因为与 sed
不同,它从一开始就打算编辑文件 (它也是 POSIX 标准,与 sed -i
不同,因此更便携。)
类似
ed -s input.md <<EOF
/Section 1/;/Section/s/foo/bar/g
/Section 3/;/Section/sg
w
EOF
已翻译:在从包含 Section 1
的第一行开始到下一个 Section
行结束的块中,将 foo
替换为 bar
。然后在 Section 3
块中执行相同的 s 替换。最后,w将更改写回磁盘。
这是一个 sed 版本:
sed -E '/^#[^#]\s*Section\s+[134]\s*$/, // s/foo/bar/' input.md
您始终可以使用 -e
选项向 sed
提供多个命令,这样即使这些部分是一个接一个的,也可以进行替换:
sed -e '/# Section 1/,/#/ s/foo/bar/' -e '/# Section 2/,/#/ s/foo/bar/' input.md
多个命令也可以放在一个“sed脚本文件”中:
# content of script.sed
/# Section 1/,/#/ s/foo/bar/
/# Section 2/,/#/ s/foo/bar/
你是这样执行的:
sed -f script.sed input.md
为了完成,这个 awk 答案将在整个部分中进行替换,包括 header:
awk '/^#/ { in_section = /Section [1|3|4]/ } in_section { sub(/foo/, "bar") } 1' input.md
如果您想从替换中排除 header:
awk ' /^#/ { in_section = /Section [1|3|4]/; header_line = NR }
in_section && (NR > header_line) { sub(/foo/, "bar") } 1' input.md
详情
awk '/^#/ { # if in section header
in_section = /Section [1|3|4]/; # determine if section of interest (1/0)
header_line = NR; # value of header line to exclude
}
in_section && (NR > header_line) { # if in section of interest and after header line
sub(/foo/, "bar"); # substitute text
} 1' input.md # 1 is to print all lines
sed
的解决方案
密钥在范围内。第一个寻址模式匹配我们希望替换开始的 header(s),第二个寻址模式匹配除第一个寻址模式中的所有 headers。请注意,替换命令包括范围内的第一行和最后一行(即 headers)。
sed -E '/^# Section [134]/, /^# Section [^134]/ s/foo/bar/' input.md
这个从替换中排除了 headers:
sed -E '/^# Section [134]/, /^# Section [^134]/ { /^#/!s/foo/bar/ }' input.md
我有一个 markdown 文件,其中的部分由标题分隔。我只想在特定部分执行搜索和替换;但是,每个部分都有相似的内容,因此全局搜索和替换最终会影响所有部分。因此,我需要以某种方式将搜索和替换限制为文件的某些部分。
例如,假设我想用 # Section 1
、# Section 3
下的 bar
替换 foo
的 所有 个实例,和 # Section 4
保持 # Section 2
和 # Section 5
不变,如下所示
示例输入:
# Section 1
- foo
- foo
- Unimportant Item
- foo
- Unimportant Item
# Section 2
- foo
- Unimportant Item
# Section 3
- foo
- Unimportant Item
# Section 4
- foo
- Unimportant Item
- foo
# Section 5
- foo
- foo
示例输出
# Section 1
- bar
- bar
- Unimportant Item
- bar
- Unimportant Item
# Section 2
- foo
- Unimportant Item
# Section 3
- bar
- Unimportant Item
# Section 4
- bar
- Unimportant Item
- bar
# Section 5
- foo
- foo
如果我不必担心各个部分,使用
进行全局搜索和替换将是微不足道的sed -i 's/foo/bar/g' <input_file>
但我不确定 sed
是否能够检查上下文以允许我正在寻找的内容。
您可以使用这个 awk
:
awk 'p {sub(/foo$/, "bar")} /^#/ {p = / (Section [134])$/} 1' file
# Section 1
- bar
- bar
- Unimportant Item
- bar
- Unimportant Item
# Section 2
- foo
- Unimportant Item
# Section 3
- bar
- Unimportant Item
# Section 4
- bar
- Unimportant Item
- bar
# Section 5
- foo
- foo
为了使其更具可读性:
awk 'p { # if p==1 and current line # == n
sub(/foo$/, "bar") # replace foo with bar
}
/^#/ { # if line starts with #
p = / (Section [134])$/ # set p = 1/0 if it matches sections
} 1' file
每当您考虑 sed -i
时,我通常的建议是改用它的哥哥 ed
,因为与 sed
不同,它从一开始就打算编辑文件 (它也是 POSIX 标准,与 sed -i
不同,因此更便携。)
类似
ed -s input.md <<EOF
/Section 1/;/Section/s/foo/bar/g
/Section 3/;/Section/sg
w
EOF
已翻译:在从包含 Section 1
的第一行开始到下一个 Section
行结束的块中,将 foo
替换为 bar
。然后在 Section 3
块中执行相同的 s 替换。最后,w将更改写回磁盘。
这是一个 sed 版本:
sed -E '/^#[^#]\s*Section\s+[134]\s*$/, // s/foo/bar/' input.md
您始终可以使用 -e
选项向 sed
提供多个命令,这样即使这些部分是一个接一个的,也可以进行替换:
sed -e '/# Section 1/,/#/ s/foo/bar/' -e '/# Section 2/,/#/ s/foo/bar/' input.md
多个命令也可以放在一个“sed脚本文件”中:
# content of script.sed
/# Section 1/,/#/ s/foo/bar/
/# Section 2/,/#/ s/foo/bar/
你是这样执行的:
sed -f script.sed input.md
为了完成,这个 awk 答案将在整个部分中进行替换,包括 header:
awk '/^#/ { in_section = /Section [1|3|4]/ } in_section { sub(/foo/, "bar") } 1' input.md
如果您想从替换中排除 header:
awk ' /^#/ { in_section = /Section [1|3|4]/; header_line = NR }
in_section && (NR > header_line) { sub(/foo/, "bar") } 1' input.md
详情
awk '/^#/ { # if in section header
in_section = /Section [1|3|4]/; # determine if section of interest (1/0)
header_line = NR; # value of header line to exclude
}
in_section && (NR > header_line) { # if in section of interest and after header line
sub(/foo/, "bar"); # substitute text
} 1' input.md # 1 is to print all lines
sed
密钥在范围内。第一个寻址模式匹配我们希望替换开始的 header(s),第二个寻址模式匹配除第一个寻址模式中的所有 headers。请注意,替换命令包括范围内的第一行和最后一行(即 headers)。
sed -E '/^# Section [134]/, /^# Section [^134]/ s/foo/bar/' input.md
这个从替换中排除了 headers:
sed -E '/^# Section [134]/, /^# Section [^134]/ { /^#/!s/foo/bar/ }' input.md