2

I'm trying to customize my pacman.conf file automatically when installing Arch with archiso.

For that, I want to uncomment two lines in the pacman configuration file.

Here is an extract from the file in question:

#[multilib-testing]
#Include = /etc/pacman.d/mirrorlist

#[multilib]
#Include = /etc/pacman.d/mirrorlist

So I want to uncomment the two lines of the multilib block, but not the multilib-testing block!

To uncomment the first line of the block is easy with sed:

sed -i 's/#\[multilib]/\[multilib]/g' /etc/pacman.conf

However, the next line is exactly the same as the one in the previous block (and in many other blocks in practice), so if I do:

sed -i 's/#Include =/Include =/g' pacman.conf

It's going to change all the line matching the pattern.

What I want is to only change the specific line just after [multilib] How can I do that?

Ben
  • 6,321
  • 9
  • 40
  • 76

3 Answers3

4

When you find #[multilib] line, read a New line and append it to pattern space, and remove # coming after a line feed (\n).

sed '/^#\[multilib]/{N;s/\n#/\n/}' file

Given your sample its output:

#[multilib-testing]
#Include = /etc/pacman.d/mirrorlist

#[multilib]
Include = /etc/pacman.d/mirrorlist

If is also an option:

awk 'f{sub(/^#/,"");f=0} $0=="#[multilib]"{f=1} 1' file

This way you don't need to escape regex-active characters.

oguz ismail
  • 1
  • 16
  • 47
  • 69
4

sed is for doing s/old/new on individual strings, that is all. With awk:

$ awk '$0=="#[multilib]"{c=2} c&&c--{sub(/#/,"")} 1' file
#[multilib-testing]
#Include = /etc/pacman.d/mirrorlist

[multilib]
Include = /etc/pacman.d/mirrorlist

No escaping characters required and if you need to remove the # from 50 lines instead of 2 you just change 2 to 50, you don't have to rewrite your script! You can obviously parameterize the beginning string and the number of lines to uncomment if you like:

$ awk -v beg='#[multilib]' -v num=2 '$0==beg{c=num} c&&c--{sub(/#/,"")} 1' file
#[multilib-testing]
#Include = /etc/pacman.d/mirrorlist

[multilib]
Include = /etc/pacman.d/mirrorlist

$ awk -v beg='#[multilib-testing]' -v num=2 '$0==beg{c=num} c&&c--{sub(/#/,"")} 1' file
[multilib-testing]
Include = /etc/pacman.d/mirrorlist

#[multilib]
#Include = /etc/pacman.d/mirrorlist

$ awk -v beg='#[multilib-testing]' -v num=5 '$0==beg{c=num} c&&c--{sub(/#/,"")} 1' file
[multilib-testing]
Include = /etc/pacman.d/mirrorlist

[multilib]
Include = /etc/pacman.d/mirrorlist
Ed Morton
  • 188,023
  • 17
  • 78
  • 185
  • The additional complexity of the AWK solution here is a strong argument for using sed, given the OP's apparent requirement. That said, I do always enjoy reading and pondering your elegant AWK solutions. – Alex Harvey May 03 '19 at 03:05
  • What complexity? It's the idiomatic solution to every single situation where you need to do something for some number of lines starting with some condition and it'll work in any awk in any shell on any UNIX box. It's just idiom "g" at https://stackoverflow.com/a/17914105/1745001 and once you know it you can apply it to any similar situation for any condition(s) affecting number of subsequent lines and any required actions. – Ed Morton May 03 '19 at 04:25
3

There is a simpler sed solution that doesn't require any multiline techniques:

/^#\[multilib]/ {   # Match only the [multilib] line.
  n                 # Then read the next line.
  s/^#//            # ... and delete the comment marker.
}

Testing:

▶ cat > FILE <<EOF
#[multilib-testing]
#Include = /etc/pacman.d/mirrorlist

#[multilib]
#Include = /etc/pacman.d/mirrorlist
EOF
▶ gsed -i '/^#\[multilib]/{n;s/^#//}' FILE

Output:

▶ cat FILE
#[multilib-testing]
#Include = /etc/pacman.d/mirrorlist

#[multilib]
Include = /etc/pacman.d/mirrorlist
Alex Harvey
  • 14,494
  • 5
  • 61
  • 97