Bash 脚本:更新属性文件

Led*_*Led 3 bash properties-file

propertyOne=1
propertyTwo=a/b
propertyThree=three
Run Code Online (Sandbox Code Playgroud)

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

  • propertyOne 将在原始值之前添加一个字符串
  • propertyTwo 将在中间添加一个字符串
  • propertyThree 将在末尾添加一个字符串

    propertyOne=apple/1
    propertyTwo=a/and/b
    propertyThree=three/end
    
    Run Code Online (Sandbox Code Playgroud)

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

sed -i -e '/propertyTwo=/ s=.*/=one/2/two' path/to/file
Run Code Online (Sandbox Code Playgroud)

kar*_*kfa 5

awk来救援!

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

propertyOne=apple/1
propertyTwo=a/and/b
propertyThree=three/end
Run Code Online (Sandbox Code Playgroud)

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

注意显然,如果您有/=作为内容的一部分,您可以概括第一个和最后一个规则以将其添加到方程的值部分,但无法解决第二种情况。

/../另外为了更好地控制您可能想要更改的规则$1=="..."

这是另一种选择,

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

使用边缘情况进行测试

$ awk -F= ... << EOF
> propertyOne=a==b
> propertyTwo=a==b/c==d
> propertyThree=x/y==z
> Not_propertyOne=no change
> EOF
Run Code Online (Sandbox Code Playgroud)

产量

propertyOne=apple/a==b
propertyTwo=a==b/and/c==d
propertyThree=x/y==z/end
Not_propertyOne=no change
Run Code Online (Sandbox Code Playgroud)


mkl*_*nt0 5

在这种情况下,纯 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
Run Code Online (Sandbox Code Playgroud)

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

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

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


awk,如karakfa's answer 所示,当然是更快的选择,但它有一个警告 - 这对您来说可能是也可能不是问题:

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

如果将输入拆分为字段=,则泛型值处理可能会出现问题,因为您将不会有单个变量将值作为一个整体引用。

一个简单的例子:假设您有一个输入 line foo=bar=baz,并且您想将 string 附加@到现有值,bar=baz,而不必提前知道现有值是否恰好具有嵌入的=字符。
如果您盲目地使用$2 = $2 "@"append ,则结果值将只是bar@- 换句话说:您丢失了数据

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

awk -F= '
  # Capture the entire value (everything to the right of "=") in variable "val".
  { val= $0; sub("^[^=]+=", "", val) }
  $1 == "propertyOne"   { val = "apple/" val } 
  $1 == "propertyTwo"   { sub(/\//, "/and/", val) }   
  $1 == "propertyThree" { val = val "/end" }
  { print $1 "=" val }  
' file > file.tmp && mv file.tmp file
Run Code Online (Sandbox Code Playgroud)

注意:如果您使用GNU awk并且版本号 >= 4.1,则可以使用-i inplace代替> file.tmp && mv file.tmp file就地更新输入文件(粗略地说)。除了比后一种方法更方便外,-i inplace还保留了原始文件的权限,但基本方法是相同的:文件被替换,这承担了用常规文件替换符号链接的风险。


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