Sed命令仅在所需位置更改字符串

Sed command to change a string at only desired place

我想在 bash 脚本中使用 sed 替换文件中的字符串,但该字符串出现在该文件的多个位置。

有没有什么方法可以使用 WHERE 子句替换字符串,这样我就可以只在需要的地方替换字符串?

使用行号不起作用,因为我需要一个比允许的更灵活的脚本。这是我正在尝试做的事情。

我将所需的代码片段存储在一个变量中。我可以在 sed 命令中使用该变量吗?例如,

sed -i "s/condition: succeeded('Fair_PreProd')/condition: succeeded('Fair_UAT')/g" $folder_path/$file_name

这是原始文件:

-stage: Moto_Dev
dependsOn: Build
condition: and(succeeded(), eq(variables.isDevelop, true))

- stage: Unity_Dev
  dependsOn: Build
  condition: and(succeeded(), eq(variables.isUnityDevelop, true))

- stage: QA
  dependsOn: Dev
  condition: succeeded('Dev')

- stage: UAT
  dependsOn: Build
  condition: and(succeeded(), eq(variables.isStaging, true))

dependsOn: Build 出现在 3 个地方。我只想替换 -stage:MotoDev 部分中的那个。我该怎么做?

建议读取 bash 个变量的 awk 解决方案。

filter : awk RegExp 过滤正确的行。

target : 一个 awk RegExp 来标识要替换的字符串

replacement: 一个字符串不是 RegExp 来替换目标 RegExp。

使用提供的示例:

awk '[=10=]~filter && [=10=]~target{gsub(target,replacement)}1' filter="block4" target="a=c" replacement="this = that" input.file
# same command, less readable, but shorter:
awk '[=10=]~f && [=10=]~t{gsub(t,r)}1' f="block4" t="a=c" r="this = that" input.file

这种方法的优点:

  • sed更灵活。
  • 通用:可以添加更多过滤器和过滤逻辑。

这种方法的缺点:

  • 无法对输入文件进行 in-place 替换。喜欢sed -i
  • 因此需要一一明确指定所有文件。

示例输出:

awk '[=11=]~filter && [=11=]~target{gsub(target,replacement)}1' filter="block4" target="a=c" replacement="this = that" input.1.txt 
block1{ a=c } 
block2{ v=c }
block3{ w=c }
block4{ this = that }
block5{ a=c }

Is there any way to replace the string using a WHERE clause so I can replace the string only where I want?

sed 没有 SQL-style WHERE 子句,但是命令可以有“地址”来定义要操作的输入行的子集。这些可以采取多种形式。正则表达式可能是最常见的,但也有行号和一些特殊形式。您还可以包含从简单地址构建的 范围 。地址范围将是解决您提出的问题的一个相当好的方法。

例如,

sed -i '/^\s*-\s*stage:\s*Moto_Dev/,/^\s*-/ s/dependsOn: Build/dependsOn: Test/' input

解释:

  • -icommand-line标志告诉sed工作“in-place”,这实际上意味着它将用一个文件替换原始文件包含 sed 的输出。

  • /^\s*-\s*stage:\s*Moto_Dev/,/^\s*-/ 是一个范围地址,由一个用于范围开始的正则表达式 (/^\s*-\s*stage:\s*MotoDev/) 和一个用于范围结束的正则表达式 (/^\s*-/) 组成。

    • /^\s*-\s*stage:\s*Moto_Dev/ 与您希望进行更改的部分的开头相匹配,在某些位置的白色 space 的确切数量具有一定的灵活性。为了简洁明了,它使用 \s 来表示单个 space 或制表符。那是一个 GNU 扩展,但是如果你不能依赖 GNU sed 那么还有其他方式来表达同样的事情。
    • /^\s*-/ 匹配下一节的开头,因为您已经输入了。如果有必要更具选择性,它可以变得更具体。 该范围包括其端点,但这似乎不是手头任务的问题。
  • 在显示的输入中只有一个这样的范围,并且该范围包含您要修改的行。对范围内的每一行执行指定的替换 s/dependsOn: Build/dependsOn: Test/,但只有包含要替换的匹配项的那一行。该范围内的所有其他人将不受影响。

  • 没有为范围外的行指定任何命令,因此它们也不受影响。


你也问了,

I stored the desired piece of code in a variable. Can I use that variable in a sed command? For example,

sed -i "s/condition: succeeded('Fair_PreProd')/condition: succeeded('Fair_UAT')/g" $folder_path/$file_name

sed 不会扩展 shell-style 参数引用,但您不需要它来做。该命令中的变量引用在执行结果命令之前由 shell 本身扩展,因此

  • 是的,您可以使用它们,并且
  • 这不是使用 shell 变量的问题,特别是 sed