Bash 脚本:更新属性文件

Bash scripting: update a properties file

propertyOne=1
propertyTwo=a/b
propertyThree=three

如何将 属性 文件的内容更改为以下模式?

我尝试使用 sed -i -e,但只有对每一行的更改进行硬编码才能成功;有什么改进代码的建议吗?

sed -i -e '/propertyTwo=/ s=.*/=one/2/two' path/to/file

awk 救援!

$ awk -F'[=/]' '/propertyOne/{="apple/"} 
                /propertyTwo/{="/and/"} 
              /propertyThree/{="/end"} 
                             {print  "=" }' file

propertyOne=apple/1
propertyTwo=a/and/b
propertyThree=three/end

定义两个字段分隔符以便能够引用组件。根据您的规则设置新的第二个字段并打印。

注意 显然,如果您的内容中包含 /=,则此方法将不起作用。您可以概括第一个和最后一个规则以添加或附加到等式的值部分,但没有办法绕过第二种情况。

此外,为了更好地控制规则,您可能希望针对每种情况将 /../ 更改为 =="..."

这是另一种选择,

$ awk -F= -v OFS='=' '=="propertyOne"{="apple/"} 
                      =="propertyTwo"{sub(/\//,"/and/")}   
                    =="propertyThree"{$NF=$NF"/end"}1' file

使用边缘案例进行测试

$ awk -F= ... << EOF
> propertyOne=a==b
> propertyTwo=a==b/c==d
> propertyThree=x/y==z
> Not_propertyOne=no change
> EOF

产量

propertyOne=apple/a==b
propertyTwo=a==b/and/c==d
propertyThree=x/y==z/end
Not_propertyOne=no change

在这种情况下,纯 Bash 解决方案 提供了灵活性和稳健性(但请参阅下文以了解更快的 awk 解决方案)。

虽然 Bash 逐行读取文件的解决方案通常很慢,但这可能不会成为属性文件的问题,因为属性文件往往很小。

#!/usr/bin/env bash

while IFS='=' read -r prop val; do
  case $prop in
    propertyOne)
      val="apple/$val"
      ;;
    propertyTwo)
      val="${val/\///and/}"
      ;;
    propertyThree)
      val="$val/end"
      ;;
  esac
  printf '%s\n' "$prop=$val"
done < file > file.tmp && mv file.tmp file

Bash 内置 read 方便地提供了其余的行逻辑: 通过仅在 IFS='-' read -r prop value 中指定 2 个变量,第二个变量 value 接收 第一个 = 之后的所有内容,无论它是什么,即使它包含额外的 = 个实例。

< file > file.tmp && mv file.tmp file 是(松散地)就地更新文件的常用习惯用法。从技术上讲,修改后的内容被写入临时文件。文件和那个温度。然后文件替换原来的文件。

注:
* 需要这种间接更新方式,因为 shell 不支持在同一命令中读取和输出到同一文件。
* 这种简单的方法可能会有问题,因为如果输入文件是一个符号链接,它被替换为一个普通文件,新文件的权限可能不同,...


awk,如 所示,当然是更快的选择,但它有一个警告 - 这可能是也可能不是你的问题:

从概念上讲,属性文件并非严格基于字段,因为属性值可能包含内部值 = 个实例。

如果您按 = 将输入拆分为字段,那么通用值处理可能会出现问题,因为您不会有一个 单个 变量引用该值整体

一个简单的例子:假设您有一个输入行 foo=bar=baz,并且您想要将字符串 @ 附加到现有值 bar=baz,而无需提前知道现有值是否恰好嵌入了 = 个字符。
如果您盲目地使用 = "@" 进行附加,结果值将只是 bar@ - 换句话说:您 丢失了数据 .

解决这个问题需要更多的工作;这是一个改编自 karakfa 的 awk 解决方案,它在单个变量 val:

中提供 whole
awk -F= '
  # Capture the entire value (everything to the right of "=") in variable "val".
  { val= [=11=]; sub("^[^=]+=", "", val) }
   == "propertyOne"   { val = "apple/" val } 
   == "propertyTwo"   { sub(/\//, "/and/", val) }   
   == "propertyThree" { val = val "/end" }
  { print  "=" val }  
' file > file.tmp && mv file.tmp file

注意:如果你使用GNUawk并且版本号>=4.1,你可以使用-i inplace代替> file.tmp && mv file.tmp file就地更新输入文件(松散地说)。 -i inplace除了比后一种方式更方便之外,还保留了原文件的权限,但基本的做法是一样的:文件被替换,承担替换的风险带有常规文件的符号链接。


sed 不是一个好的选择,因为很难以通用方式将替换限制在一行的一部分。