2

I'm trying to replace the path part of below line from postgresql.conf using shell script:

data_directory = '/var/lib/postgresql/10/main'      # use data in another directory

I first checked if I'm able to locate the line first using below script, but it did not find the line:

#!/bin/bash
while IFS='' read -r line || [[ -n "$line" ]]; do
        if [[ "$line" = "data_directory = '/var/lib/postgresql/10/main'         # use data in another directory" ]]

I know there is better way to replace this line using sed, but I need to know if its doable by reading the file from start to end and then replace the desired part of the line, if found. If not, replacing the entire line with only the path part changed will do it too. Thanks!

4
  • 1
    Yes, it's doable in plain bash (but not recommended). Use parameter expansion, for instance ${var/find/replace}. Commented Dec 17, 2018 at 12:33
  • 1
    It is unclear to me whether you are asking, "How can I avoid sed" or "sed is okay but I do not know how." I have answered the latter. Commented Dec 17, 2018 at 12:57
  • 1
    Sorry for the confusion @tbh, I know how to do it by sed but I want to do it using loop and all because I'm not familiar with it and I know loop will be useful for me in other scenarios as well. Learning! Commented Dec 17, 2018 at 13:10
  • 1
    There are more spaces before # in the string you're comparing with $line than the line you gave as an example. Commented Dec 17, 2018 at 13:25

3 Answers 3

3

Plain bash solution:

path="/newpath"
while IFS= read -r -d $'\n'; do
  if [[ "${REPLY}" == "data_directory = '/var/lib/postgresql/10/main'      # use data in another directory" ]]
  then echo "${REPLY/\'*\'/'${path}'}"
  else echo "${REPLY}"
  fi
done < postgresql.conf > new.conf

mv new.conf postgresql.conf

Test:

$ cat postgresql.conf
# This is a comment
log_connections = yes
log_destination = 'syslog'
search_path = '"$user", public'
shared_buffers = 128MB
data_directory = '/var/lib/postgresql/10/main'      # use data in another directory
# This is a comment

$ path="/newpath"
$ while IFS= read -r -d $'\n'; do
>   if [[ "${REPLY}" == "data_directory = '/var/lib/postgresql/10/main'      # use data in another directory" ]]
>   then echo "${REPLY/\'*\'/'${path}'}"
>   else echo "${REPLY}"
>   fi
> done < postgresql.conf

# This is a comment
log_connections = yes
log_destination = 'syslog'
search_path = '"$user", public'
shared_buffers = 128MB
data_directory = '/newpath'      # use data in another directory
# This is a comment
Sign up to request clarification or add additional context in comments.

1 Comment

This is really insightful. However, the result I get is data_directory = ${path}. Not the path assigned to path variable. I literally copy pasted. Nevertheless, +1 for insightful details.
2

Another approach using case - *'s allow non-exact spacing around the equals and before any comment, but introduce the small possibility of false matches. I think with the rest of the specific info on the line it's small enough that it isn't a problem.

$: cat postgresql.conf
some stuff
data_directory = '/var/lib/postgresql/10/main'         # use data in another directory
some other stuff.

$: path=/new/path/to/
$: while IFS='' read -r line || [[ -n "$line" ]]
>  do case "$line" in
>     data_directory*=*'/var/lib/postgresql/10/main'*) 
>        echo "${line//\/*\//$path}";;
>     *) echo "$line";;
>     esac
>  done < postgresql.conf >/tmp/checkme
some stuff
data_directory = '/new/path/to/main'         # use data in another directory
some other stuff.

If it's good, then

mv /tmp/checkme postgresql.conf

You could test it a few times, and then just make it automatic, but unless it's an ongoing automation that you're building I'd check it personally.

4 Comments

This worked. Thanks! I added > postgresql.conf at the end of the command to have this path change written into the file permanently. But the .conf file simply became empty with all the content gone. Of course it was a duplicate file I was working on,but this was a good lesson. Would you suggest any other way (using same logic as in your answer) to make this change permanent?
That's because the shell opens (and truncates) postgresql.conf for writing before it ever starts to read from the file. You need to write to a temporary file first, then move it into place after you are done.
Updated the answer.
Perfect!! This not only solves the problem, but explains a lot of things to a novice like me.
1
REPLACEMENT_PATH=mypath
sed -i path/postgresql.conf -re "s/^(data_directory[[:space:]]*=[[:space:]]*')[^']*(')/\1${REPLACEMENT_PATH}\2/"

1 Comment

@tbh, thank you for sharing, but I want to know the way to do it by reading the file from start to end using while loop as mentioned in the question. What mistake do you see in my if condition, which is causing it not to find the line?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.