0

I have a file ~/.zshrc with the following lines

...
#export PATH="/usr/local/opt/php@7.0/bin:$PATH"
#export PATH="/usr/local/opt/php@7.0/sbin:$PATH"
##export PATH="/usr/local/opt/php@7.1/bin:$PATH"
##export PATH="/usr/local/opt/php@7.1/sbin:$PATH"
#export PATH="/usr/local/opt/php@7.3/bin:$PATH"
#export PATH="/usr/local/opt/php@7.3/sbin:$PATH"
#export PATH="/usr/local/opt/php@7.2/bin:$PATH"
#export PATH="/usr/local/opt/php@7.2/sbin:$PATH"
export PATH="/usr/local/opt/php@7.4/bin:$PATH"
export PATH="/usr/local/opt/php@7.4/sbin:$PATH"
...

I am preparing a small bash script, which accepts the PHP version (first arg) and action (second arg). For example dummy command may look like:

mycommand 7.4 comment

Which will only comment out the following lines form the file as

#export PATH="/usr/local/opt/php@7.4/bin:$PATH"
#export PATH="/usr/local/opt/php@7.4/sbin:$PATH"

And if you run

mycommand 7.1 uncomment

Will update only the following lines as

export PATH="/usr/local/opt/php@7.1/bin:$PATH"
export PATH="/usr/local/opt/php@7.1/sbin:$PATH"

So my question is how to use sed or any other command in .sh script which will parse the file.
My bash script looks like below (not working)

# File mycommand.sh
# ...
if [[ ! -z "$1" && ! -z "$2" && "$2" = "comment" ]]; then
    # remove multi # comments if there's any
    sed -i '' 's/#*export PATH="\/usr\/local\/opt\/php@$1/export PATH="\/usr\/local\/opt\/php@$1/g'   
    # Finally add a single # comment
    sed -i '' 's/*export PATH="\/usr\/local\/opt\/php@$1/#export PATH="\/usr\/local\/opt\/php@$1/g'    
fi
if [[ ! -z "$1" && ! -z "$2" && "$2" = "uncomment" ]]; then
    # remove one or more # comments
    sed -i '' 's/#*export PATH="\/usr\/local\/opt\/php@$1/export PATH="\/usr\/local\/opt\/php@$1/g'   
fi
2

This will work robustly, efficiently, and portably:

#!/usr/bin/env bash

infile=~/.zshrc
tmp=$(mktemp) || exit 1

awk -v ver="$1" -v cmd="$2" '
    index($0,"php@"ver"/") {
        if      ( cmd == "comment" )   { sub(/^#*/,"#") }
        else if ( cmd == "uncomment" ) { sub(/^#+/,"")  }
        else { printf "bad cmd: %s\n", cmd | "cat>&2" }
    }
    { print }
' "$infile" > "$tmp" && mv "$tmp" "$infile"

Change "php@"ver"/" to "export PATH=\"/usr/local/opt/php@"ver"/" if needed to avoid false matches with other lines that exist in your real input but you didn't show in your example.

If you have GNU awk you could use -i inplace instead of manually creating a tmp file.

1

The script that you use should be changed as follows:

if [[ ! -z "$1" && ! -z "$2" && "$2" = "comment" ]]; then
    # remove multi # comments if there's any
    sed -i "s/#*export PATH=\"\/usr\/local\/opt\/php@$1/export PATH=\"\/usr\/local\/opt\/php@$1/g" ~/.zshrc
    # Finally add a single # comment
    sed -i "s/export PATH=\"\/usr\/local\/opt\/php@$1/#export PATH=\"\/usr\/local\/opt\/php@$1/g" ~/.zshrc
fi
if [[ ! -z "$1" && ! -z "$2" && "$2" = "uncomment" ]]; then
    # remove one or more # comments
   sed -i "s/#*export PATH=\"\/usr\/local\/opt\/php@$1/export PATH=\"\/usr\/local\/opt\/php@$1/g" ~/.zshrc
fi

The problems were:

  1. The empty single quotes ('') after sed -i.

  2. You hadn't specified the sed input file (~/.zshrc).

  3. You needed to use double quotes (") instead of single ones (') for the shell to expand the $1 variable first. See this and this relevant questions.

1
  • 2
    1) If you use a delimiter char other than /, e.g. :, then you won't have to escape every / in the regexp or replacement text. 2) There's no need to test for ! -z "$2" when you're explicitly testing for "$2" = "[un]comment". 3) You're using $1 in an unanchored regexp context so 1.2 will match the 142 in ...php@142yeehaw.
    – Ed Morton
    Sep 9 '20 at 13:31

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.