Search code examples
awksedyamlindentation

How to replace a text in yml file with sed making sure that the indentation is preserved


I have a YAML file with the following content:

apiVersion: v1
kind: ConfigMap
metadata:
  name: castlereport-cfg
  namespace: qa
data:
  application.properties: |
    BODY

I need to replace the BODY with the application.properties file content. I've used sed to do that like this:

sed -i -e '/BODY/r target/classes/application-dev.properties' -e 's///' -e '/^ *\$/d'  target/classes/$kubeConfigFile

It does what I need but loses the indentation and I what I get is this:

apiVersion: v1
kind: ConfigMap
metadata:
  name: castlereport-cfg
  namespace: qa
data:
  application.properties: |

server.port=8080
server.context-path=/

spring.main.banner-mode=off
logbook.format.style=http

What should I do to keep the indentation?

And one more thing that -e '/^ *\$/d' part in the sed command is there to remove blank lines, but it doesn't seem to work either.

The Expected output is the following:

apiVersion: v1
kind: ConfigMap
metadata:
  name: castlereport-cfg
  namespace: qa
data:
  application.properties: |
    server.port=8080
    server.context-path=/
    spring.main.banner-mode=off
    logbook.format.style=http   

UPDATE: I have a Jenkins pipeline script where I am able to achieve what I want with the following three lines:

   sh ( script : "sed -i -e 's/^[ \t\$]*/   /' -e \"/^ *\$/d\"  ./target/classes/"+configFile, returnStdout: true).trim() //remove trailing spaces and blank lines
   sh ( script : "sed -i -e \"/BODY/r ./target/classes/"+configFile+"\" -e \"s///\" ./target/classes/" + configMapKubernetes, returnStdout: true).trim() // insert content of application-dev.properties to YAML file using BODY
   sh ( script : "sed -i -e \"/^ *\$/d\"  ./target/classes/" + configMapKubernetes, returnStdout: true).trim() //remove  blank lines

The pipeline script is in groovy so the whole command is inside double-quotes.


Solution

  • sed is for doing simple s/old/new on individual strings that is all. This awk script is probably what you want:

    $cat tst.awk
    NR==FNR {
        rec[++numLines] = $0
        next
    }
    s = index($0,"BODY") {
        indent = sprintf("%*s",s-1,"")
        for (lineNr=1; lineNr<=numLines; lineNr++) {
            print indent rec[lineNr]
        }
        next
    }
    { print }
    

    For example, given these input files:

    $ cat foo.yaml
    apiVersion: v1
    kind: ConfigMap
    metadata:
      name: castlereport-cfg
      namespace: qa
    data:
      application.properties: |
        BODY
    
    $ cat props
    here is some text
        split across
      a few lines
    

    we can do this which uses the BODY indentation from the yaml file but also retains any additional indentation from the props file:

    $ awk -f tst.awk props foo.yaml
    apiVersion: v1
    kind: ConfigMap
    metadata:
      name: castlereport-cfg
      namespace: qa
    data:
      application.properties: |
        here is some text
            split across
          a few lines
    

    If you wanted instead to ignore the indentation from the props file and line all the props text up where BODY began that's a simple tweak:

    $ cat tst.awk
    NR==FNR {
        sub(/^[[:space:]]+/,"")
        rec[++numLines] = $0
        next
    }
    s = index($0,"BODY") {
        indent = sprintf("%*s",s-1,"")
        for (lineNr=1; lineNr<=numLines; lineNr++) {
            print indent rec[lineNr]
        }
        next
    }
    { print }
    
    $ awk -f tst.awk props foo.yaml
    apiVersion: v1
    kind: ConfigMap
    metadata:
      name: castlereport-cfg
      namespace: qa
    data:
      application.properties: |
        here is some text
        split across
        a few lines