I have these lines of code in my /etc/bash.bashrc file in Ubuntu 15. How can I uncomment only those lines from the command line, without without messing up any other comments in the file?

# enable bash completion in interactive shells
#if ! shopt -oq posix; then
#  if [ -f /usr/share/bash-completion/bash_completion ]; then
#    . /usr/share/bash-completion/bash_completion
#  elif [ -f /etc/bash_completion ]; then
#    . /etc/bash_completion
#  fi

The desired output should look like this:

# enable bash completion in interactive shells
if ! shopt -oq posix; then
  if [ -f /usr/share/bash-completion/bash_completion ]; then
    . /usr/share/bash-completion/bash_completion
  elif [ -f /etc/bash_completion ]; then
    . /etc/bash_completion

Remember that there are other lines of code in this file that I don't want to mess up and that this should be done automatically by a bash script.

  • 1
    Why automatic? Use vi :-) – Ned64 Sep 9 '15 at 13:30
  • 3
    How can the script know which comments are real comments and which are commented code? – chaos Sep 9 '15 at 13:33
  • What chaos has mentioned severely limits the automation simplicity. You'll have to write lexical parser for bash that makes sense of semantics in your file (excluding the #) and leave the # untouched on lines where it does NOT make sense to the parser. A simpler solution is what Ned64 suggested. Use vi -> 1) Find the lines to be uncommented 2) Esc followed by Ctrl + V' to enter visual block mode 3) Select the '#'s by pressing h` or k 4) 'd' to delete followed by Esc and Voila! Vim experts could probably do this in 1 key ;-) – toddlermenot Sep 9 '15 at 13:48

In fact what is asked it a very wrong way to do multiprofiles bashrc. You should just have several different .bashrc files like .bashrc_1, .bashrc_rc2, where you define your different need from the basic .bashrc file and source them when needed.

That's it.


I can't give you a definitive answer without seeing the entire file. If there is only one instance of if ! shopt -oq posix; and ` you can use that to delimit the relevant section:

perl -i -pe '$i++ if /^#if ! shopt -oq posix;/; s/^#// if $i==1; $i=0 if /^fi/' /etc/bash.bashrc 

This script sets $i to 1 the first time it encounters a line starting with #if ! shopt -oq posix;. It sets it back to 0 if it encounters a line starting with fi (not #fi, because the substitution operator will have uncommented it at this point). Then, if $i is 1, it removes comments from the start of the line.


  • This will fail if you have more than one lines starting with #if ! shopt -oq posix;
  • It will fail if you have a space after the #.

A much better approach would be to edit your file so that the relevant section is clearly delimited using something else. For example, XXXstart and XXXend:

Then, you can make a more general approach:

# enable bash completion in interactive shells
# XXXstart
#if ! shopt -oq posix; then
#  if [ -f /usr/share/bash-completion/bash_completion ]; then
#    . /usr/share/bash-completion/bash_completion
#  elif [ -f /etc/bash_completion ]; then
#    . /etc/bash_completion
#  fi

Now, use this script on the file:

 perl -i -pe '$i=0 if /XXXend/; s/^\s*#*// if $i==1; $i=1 if /XXXstart/; ' /etc/bash.bashrc

Finally, don't do this. Don't mess with /etc/bash.bashrc. Just add these lines to your ~/.bashrc file and avoid changing /etc/bash.bashrc. First because it is cleaner and safer and second because any changes you make to the file in /etc will be overwritten by the next update.

  • i like the idea of search for the first #if and starting to uncomment until the first #fi. i also understand the issues that can come from editing this file if an update will come in a later point. the reason behind my question is that i have to customize a clean cd installation with bsh script and this should be done also during deployment customization. – Asaf Magen Sep 9 '15 at 15:28
  • @AsafMagen a much cleaner way would be to save all this in a second file and source that from .bashrc. Up to you though. – terdon Sep 9 '15 at 15:29
  1. why not have a temporary file that you just concat to /etc/bash.bashrc ?

    cat /tmp/posix-opt.txt >> /etc/bash.bashrc

(this will add /tmp/posix-opt.txt (which contains uncommented line) to /etc/bash.bashrc )

  1. If not possible

    n=$(grep -n 'shopt -oq posix' /etc/bash.bashrc)
    sed -i -e "$n,$(($n + 7))s/^#//" /etc/bash.bashrc

this is the same as in vi, you just need the grep to get line number.

notice however, that should there be 2 lines containing "shopt -oq posix" the script will break.

  • you mean like a patch file to append to the original file? – Asaf Magen Sep 9 '15 at 13:22
  • yes just prepare the file once, then run cat /tmp/posix-opt.txt >> /etc/bash.bashrc – Archemar Sep 9 '15 at 13:23
  • there will be duplicate line of code this way. – Asaf Magen Sep 9 '15 at 13:24
  • ?? in /tmp/posix-opt.txt you have uncommented line, while in /etc/bash.bashrc you have commented one. – Archemar Sep 9 '15 at 13:26

If you know what line in the file your block starts at (e.g, line 30), you can do it with

sed '31,+6s/^.//' </etc/bash.bashrc >/etc/bash.bashrc-uncommented

Then check your new file bash.bashrc-uncommented to make sure it's right before copying

mv /etc/bash.bashrc-uncommented /etc/bash.bashrc

What the sed statement is doing is looking for line 31 (the line after your block starts) and applying a regular expression to those 6 lines you asked for.

For each line, the regular expression looks for the beginning of the line ^ and the following character ., and replaces it with nothing (i.e., deletes it).

The </etc/... and >/etc/... are telling sed where to find your file and where to send the result of the command.


This proposed solution i came up that need some preparation but it's fine with me.

First i copied the source file to a temp folder

cp /etc/bash.bashrc /var/ldt/bash.bashrc_modifed

Then i edit only what i needed in /var/ldt/bash.bashrc_modifed.

Created the patch by running:

diff -u /etc/bash.bashrc /var/ldt/bash.bashrc_modifed > /var/ldt/bash.bashrc_patch

Now i can apply the change only to what i exactly want.

patch /etc/bash.bashrc < /var/ldt/bash.bashrc_patch

A solution with awk:

awk '/shopt -oq posix/ { sub("#","",$0); print; for(n=0; n<=6; n++) { getline ; sub("#","",$0); print} }1' </etc/bash.bashrc >NEWFILE

Then rename NEWFILE to /etc/bash.bashrc or whatever.

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.