Quiz: in-file string replacement using bash
Julien Dephix

Julien Dephix @joolsmcfly

About: Been programming for over 20 years and loving it!

Location:
France
Joined:
Jun 24, 2022

Quiz: in-file string replacement using bash

Publish Date: Jun 30 '22
4 4

Hello, coders! 💻

Today's quiz taken from a real world task is:

replace a string in a file using bash.

I challenge you!

Pre-requisites

We have a dynamically generated variable newPath:
newPath=/new/path/$(date +"%Y%m%d%H%M%S")/file.json

We want to change the value of PATH_TO_FILE with the value of newPath in /app/.env.

File before replacement:

# /app/.env
# there could be lines here
PATH_TO_FILE=/path/to/file.json
# there could be lines here too
Enter fullscreen mode Exit fullscreen mode

File after replacement:

# /app/.env
# there could be lines here
PATH_TO_FILE=/new/path/20220630081234/file.json
# there could be lines here too
Enter fullscreen mode Exit fullscreen mode

Starter script

#!/usr/bin/env bash

newPath=/new/path/$(date +"%Y%m%d%H%M%S")/file.json

# your code here
Enter fullscreen mode Exit fullscreen mode

My take on this

Click to see my solution
#!/usr/bin/env bash

newPath=/new/path/$(date +"%Y%m%d%H%M%S")/file.json

sed -iE "s|(PATH_TO_FILE).*|\1=${newPath}|g" /app/.env
Enter fullscreen mode Exit fullscreen mode

-i option means in place. So any change made to /app/.env will be saved.

-E stands for extended regular expressions.

Traditionally we use / when replacing things : s/to_be_replaced/replacement/g for instance.
Since we're dealing with a file path we would need to escape slashes in newPath but there's a better way: use a different separator. I'm using | here but you can use @ if you prefer.

\1 holds what was captured by the parenthesis which is PATH_TO_FILE in our case.

And that's it!

Comments on how to improve this solution are most welcome.

Happy coding! ⌨️

Comments 4 total

  • Ben Sinclair
    Ben SinclairJun 30, 2022

    I'd maybe add a constraint that the variable definition can only be preceded by whitespace, and ends in an equals sign:

    sed -iE "s|^\s*(PATH_TO_FILE)=.*|\1=${newPath}|g" /app/.env
    
    Enter fullscreen mode Exit fullscreen mode

    This would exclude matching variables like ANOTHER_PATH_TO_FILE_FINAL_COPY_2 and would stop matching commented-out lines like # PATH_TO_FILE=/foo.

    Also I think that it depends which sed you're using. GNU sed will work, but BSD sed needs you to specify a backup file when doing in-file replacements.

    • Julien Dephix
      Julien DephixJun 30, 2022

      True that!

      Wouldn't s|^(PATH_TO_FILE)=.* be enough though?

      • Ben Sinclair
        Ben SinclairJun 30, 2022

        I'd want to add the whitespace to cope with indentation. It's not particularly important for this case, but you might have something like this:

        
        if [ "$ENV" = "development" ]; then
            PATH_TO_FILE=/foo/bar.json
        else
            PATH_TO_FILE=/foo/baz.json
        fi
        
        Enter fullscreen mode Exit fullscreen mode
        • Julien Dephix
          Julien DephixJun 30, 2022

          Gotcha, yeah in that case \s* makes sense.

          Thanks for your input.

Add comment