I'm in the process of hardening some of our systems. As part of that hardening process, I need to update a few entries in the /etc/fstab to limit the capabilities of some of the various partitions.

With that said, I would like be able to use a sed in-line replace to update the rows. Below is a snippet from the current /etc/fstab:

# /etc/fstab
# Created by anaconda on Wed Feb 21 09:37:23 2018
/dev/mapper/vg1-lv_root /                       ext4    defaults        1 1
/dev/mapper/vg1-lv_home /home                   ext4    defaults        1 2
tmpfs                   /dev/shm                tmpfs   defaults        0 0

After the sed command is run I would like the file to look like the following:

# /etc/fstab
# Created by anaconda on Wed Feb 21 09:37:23 2018
/dev/mapper/vg1-lv_root /                       ext4    defaults        1 1
/dev/mapper/vg1-lv_home /home                   ext4    defaults,nodev        1 2
tmpfs                   /dev/shm                tmpfs   defaults        0 0

Basically, I need to add "nodev" to all the rows that are ext[2-4], that aren't the root partition.

The sed command that I put together comes close to doing this, but for whatever reason, I can't get the regex to not match the "/" partition, so it always updates that row also.

sed '/^[^#].*ext[2-4]/s/defaults/defaults,nodev/g' /etc/fstab

I would like to key off of the "/" surrounded by spaces, not the vg1-lv_root. The following works, but I don't like the solution because it's clunky:

sed '/^[^#].*ext[2-4]/s/defaults/defaults,nodev/g' /etc/fstab | sed '/^[^#].*root.*ext[2-4]/s/defaults,nodev/defaults/' > /etc/fstab
  • 3
    Is this on a Linux machine? Can we assume GNU tools? – terdon Nov 12 at 13:58

You could use awk to add the logic to add the string and column to reformat the final output file. Assuming you have write permissions to the /etc/ and /tmp/ folders

tempfile=$(mktemp /tmp/tmpfile.XXXXXXXX)

This would create the temporary file in the /tmp/ path in which you can write the awk output to and re-direct that back to the original file

awk '$3 ~ "ext[2-4]"{ $4=$4",nodev" }1 ' /etc/fstab | column -t > "$tempfile" && mv -- "$tempfile" /etc/fstab

The column -t part is just redundant and needed to look the output file more readable, rather to make it disordered and clunky.

  • 1
    Thank you for the response. That's a cool way of solving the problem. – Jason Nov 12 at 13:09
  • 3
    I undeleted this since it is a good and helpful answer. – terdon Nov 12 at 13:46
  • Thanks @terdon, I saw OP's comment of having to use sed and without using temporary file – Inian Nov 12 at 13:48
  • 1
    Well, @Jason (and Inian) if you have access to a newish GNU awk (which you do if you're on Linux), you can do gawk -iinplace '$3 ~ "ext[2-4]"{ $4=$4",nodev" }1 ' OFS="\t" /etc/fstab. Not as pretty as column, but it uses a tab for output which looks cleaner than a space and it will edit in place. – terdon Nov 12 at 13:51
  • You can avoid the tempfile by using sponge, i.e. awk ... | column ... | sponge /etc/fstab. Sponge will wait until it's read the entirety of stdin ("soaking it up") before writing to the file specified. – JoL Nov 12 at 18:54

Here's a simpler sed approach:

$ sed -E 's|(\s/\S+\s+ext[2-4]\s+defaults)|\1,nodev|' fstab 
# /etc/fstab
# Created by anaconda on Wed Feb 21 09:37:23 2018
/dev/mapper/vg1-lv_root /                       ext4    defaults        1 1
/dev/mapper/vg1-lv_home /home                   ext4    defaults,nodev        1 2
tmpfs                   /dev/shm                tmpfs   defaults        0 0

The trick is to look for whitespace followed by a / and one or more non-whitespace characters (\s/\S+), then ext[2-4] but only if preceded by whitespace (\s+ext[2-4]), more whitespace and defaults. That should only match the cases you are interested in. So if it does match, replace the entire match with itself plus nodev: \1,nodev.

I am not sure how portable this is, however. The -E for extended regular expressions is supported by many sed implementations, but it isn't POSIX. For a more portable approach, you can try the same idea in Perl:

$ perl -pe 's|(\s/\S+\s+ext[2-4]\s+defaults)|\1,nodev|' fstab 
# /etc/fstab
# Created by anaconda on Wed Feb 21 09:37:23 2018
/dev/mapper/vg1-lv_root /                       ext4    defaults        1 1
/dev/mapper/vg1-lv_home /home                   ext4    defaults,nodev        1 2
tmpfs                   /dev/shm                tmpfs   defaults        0 0

In both cases, to edit the file in place, use -i:

perl -i -pe 's|(\s/\S+\s+ext[2-4]\s+defaults)|\1,nodev|' fstab 
sed -E 's|(\s/\S+\s+ext[2-4]\s+defaults)|\1,nodev|' fstab 

Or, for BSD or OSX sed:

sed -i '' -E 's|(\s/\S+\s+ext[2-4]\s+defaults)|\1,nodev|' fstab 

Note that the above assume that the defaults option will either be the only one or, at least, the last one. They will fail if you have something like nodev,defaults for example.

Of course, ten minutes after posting the question, I was finally able to get it to do what I want with the following:

sed -r '/^[^#].*[ \t]+\/[^[:space:]].*ext[2-4]/s/defaults/defaults,nodev/g' /etc/fstab

If someone has a cleaner or more foolproof answer, I'd love to hear it. Thanks!

  • Yeah, the main question was, "what am I doing wrong with my regular expression?". Sorry, I probably should have been more clear in my original post. I would prefer to not create temporary files if I can solve the problem with a sed one-liner. – Jason Nov 12 at 13:26
  • 1
    Why are you assuming \t? As far as I know, all fstab needs is whitespace so there's no reason you will always have a tab there. – terdon Nov 12 at 13:55

Excluding matches are difficult to maintain. It's safer to only match the partition you want. And, for only matching one line, the trailing 'g' isn't necessary.

sed -r 's#(/home\s.*defaults)\s#\1,nodev #' /etc/fstab 

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

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