0

the following sed syntax remove the "#" in the line "Require all granted"

   sed 's/#\([[:space:]]*Require all granted\)/ \1/' graphite-web.conf

   #<IfModule mod_authz_core.c>
   #    # Apache 2.4
   #    Require local
   #    Order Allow,Deny
   #    Allow from All
        Require all granted
   #</IfModule>
   #<IfModule !mod_authz_core.c>
   #    # Apache 2.2
        Require all granted
   #    Order Allow,Deny
   #    Deny from all
   #    Allow from All
   #    Allow from ::1
   #</IfModule>

how to change the sed syntax in order to remove the "#" on the first match ? or for the second match ?

expected output:

   #<IfModule mod_authz_core.c>
   #    # Apache 2.4
   #    Require local
   #    Order Allow,Deny
   #    Allow from All
        Require all granted
   #</IfModule>
   #<IfModule !mod_authz_core.c>
   #    # Apache 2.2
   #    Require all granted
   #    Order Allow,Deny
   #    Deny from all
   #    Allow from All
   #    Allow from ::1
   #</IfModule>

or

   #<IfModule mod_authz_core.c>
   #    # Apache 2.4
   #    Require local
   #    Order Allow,Deny
   #    Allow from All
   #    Require all granted
   #</IfModule>
   #<IfModule !mod_authz_core.c>
   #    # Apache 2.2
        Require all granted
   #    Order Allow,Deny
   #    Deny from all
   #    Allow from All
   #    Allow from ::1
   #</IfModule>
  • Using sed here when other tools are far better suited (e.g. awk) is a bad idea except, perhaps, as an academic exercise (or other form of self-punishment). :) – B Layer Oct 31 '17 at 6:09
  • 1
    A lot of people took the time to try to answer your question. If any of the answers were useful I'm sure they'd appreciate an upvote. And if you're using one of the solutions please "accept" it. If none of the answers are useful please indicate what is missing. Thanks. – B Layer Nov 5 '17 at 4:13
0

Use awk instead

$ cat ip.txt 
   #    Allow from All
   #    Require all granted
   #    # Apache 2.2
   #    Require all granted
   #    Order Allow,Deny
   #    Require all granted
   #    Order Deny

$ awk '/#[[:space:]]*Require all granted/ && ++c==1{sub("#", " ")} 1' ip.txt
   #    Allow from All
        Require all granted
   #    # Apache 2.2
   #    Require all granted
   #    Order Allow,Deny
   #    Require all granted
   #    Order Deny

$ awk '/#[[:space:]]*Require all granted/ && ++c==2{sub("#", " ")} 1' ip.txt
   #    Allow from All
   #    Require all granted
   #    # Apache 2.2
        Require all granted
   #    Order Allow,Deny
   #    Require all granted
   #    Order Deny

$ awk '/#[[:space:]]*Require all granted/ && ++c>1{sub("#", " ")} 1' ip.txt
   #    Allow from All
   #    Require all granted
   #    # Apache 2.2
        Require all granted
   #    Order Allow,Deny
        Require all granted
   #    Order Deny

See also awk save modifications in place


Or, using perl

$ # for inplace editing, use perl -i -pe
$ perl -pe 's/#/ / if /#\s*Require all granted/ && ++$c==1' ip.txt
   #    Allow from All
        Require all granted
   #    # Apache 2.2
   #    Require all granted
   #    Order Allow,Deny
   #    Require all granted
   #    Order Deny
  • one of the negative by using awk is that awk cant update the file and need to redirect the output to other file and then copy it to the original , unless you have better idea as sed use the -i option – yael Oct 31 '17 at 6:27
  • with GNU awk you can use -i inplace and having to use > tmp && mv tmp ip.txt is hardly a bother compared to complicated solution you'd need in sed – Sundeep Oct 31 '17 at 6:44
  • 1
    @yael, the best practice is to backup all configuration files, especially while processing inside scripts. That's why awk approach is the same as sed – Egor Vasilyev Oct 31 '17 at 6:44
0

Or sed approach:

sed ':a;N;$!ba;s/#\([[:space:]]*Require all granted\)/ \1/2' graphite-web.conf

If input file contain:

#<IfModule mod_authz_core.c>
   #    # Apache 2.4
   #    Require local
   #    Order Allow,Deny
   #    Allow from All
   #    Require all granted
   #</IfModule>
   #<IfModule !mod_authz_core.c>
   #    # Apache 2.2
   #    Require all granted
   #    Order Allow,Deny
   #    Deny from all
   #    Allow from All
   #    Allow from ::1
   #</IfModule>

Output will be:

#<IfModule mod_authz_core.c>
   #    # Apache 2.4
   #    Require local
   #    Order Allow,Deny
   #    Allow from All
   #    Require all granted
   #</IfModule>
   #<IfModule !mod_authz_core.c>
   #    # Apache 2.2
        Require all granted
   #    Order Allow,Deny
   #    Deny from all
   #    Allow from All
   #    Allow from ::1
   #</IfModule>

This pattern :a;N;$!ba; reads all lines (like a while loop) before end of file and after eof has reached it execute replacing s///2 of second occurence.

The best explanation of sed labels you may read in What is the meaning of :a;$!N; in a sed command?

And read similar topic How to replace the nth occurrence of a string using sed

0

Sed is meant to work on streams. An easy way to accomplish of what you want is to use ed:

ed -s graphite-web.conf <<<$'/Require all/s/#/ /\nw'

Update: I can go into details about ed but this page explains ed and its extensive usage in much more details.

My explanation for this line is:

-s means quite. Don't show what we changed.

Then we use herestring <<< and apply ANSI C $'' form of quotation in which the backslash character combinations are expanded. \nw means we add a new line and write to the file.

  • 1
    +1 for use of ed. -1 for not providing explanation for those unfamiliar with ed. – B Layer Oct 31 '17 at 13:38
  • @BLayer See the update. Also see the page which explains in great detail how ed works and what you can do with it. – Valentin Bajrami Oct 31 '17 at 15:55
0

Here's an ex version...set shell variable x to a number indicating which occurrence to change (e.g. with x=2 the command will modify the second occurrence of the pattern)...

This one updates the input file in-place:

ex +'/#\(\s*Require all granted\)/|1' +"norm ${x}n" +'s// \1/|wq' file 

Here's a piped variant that doesn't modify the file but prints the modified version to stdout:

cat file | ex +'/#\(\s*Require all granted\)/|1' +"norm ${x}n" +'s// \1/' +'%p|q!' /dev/stdin

Details:

When it's not being run interactively ex is essentially a cmdline-mode-centric, batched version of vim. So a lot of this should be familiar to vim users...

  • /#\(\s*Require all granted\)/|1 - search for the pattern then go to line 1 to "reset"
  • norm ${x}n - go the next occurrence of the pattern x times
  • s// \1/|wq - do the substitution on current line only then write-quit

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.