steinybot
steinybot

Reputation: 6163

How to add or replace part of a line in a file which may contain special characters?

I want to write a bash script which can update the /etc/security/limits.conf file.

The file could contain something like:

*               soft    core            0
*               hard    rss             10000
@student        hard    nproc           20
@faculty        soft    nproc           20
@faculty        hard    nproc           50
ftp             hard    nproc           0
@student        -       maxlogins       4

Let's say I want to run the script like so:

Usage:
update-ulimits <domain> <type> <item> <value>

Examples:
./update-ulimits '*' hard rss 20000
./update-ulimits bob hard nofile 1024

Which would change the file to contain:

*               soft    core            0
*               hard    rss             20000
@student        hard    nproc           20
@faculty        soft    nproc           20
@faculty        hard    nproc           50
ftp             hard    nproc           0
@student        -       maxlogins       4
bob hard nofile 1024

So if the file already contains a line which has the domain, type and item it would replace the value, otherwise it will append a new line containing all 4 arguments. The columns do not need to be aligned.

This is a specific example where there are only a couple of meta characters to worry about but I'm curious whether there is a nice solution which could handle all arbitrary meta characters. I'm guessing perl and awk should be able to do it but I am not too familiar with either.

Upvotes: 1

Views: 87

Answers (2)

dan
dan

Reputation: 5251

This is not a good idea. How do you select which line to edit?The only real way to do this is to specify a match expression, and then a substitution line/fields. Even then, you may have multiple matches.

A quicker approach is to just print each lines w/ numbers, and have the user select a line to edit. At that point, you may as well just open it in a text editor.

My advice is to learn sed and awk. Then, whenever you need to do a specific editing job on the file, you can.

EDIT - If you just want to replace a value:

sed -E s'/(^<match-fields>\s+)([0-9]+\s*$)/\1<new-number>/'

Upvotes: 0

markp-fuso
markp-fuso

Reputation: 35246

One awk solution:

$ cat update-ulimits
#!/usr/bin/bash

domain="${1}"                                            # OP can add code to verify inputs as needed
ltype="${2}"
item="${3}"
value="${4}"

awk -v dom="${domain}" -v type="${ltype}" -v item="${item}" -v val="${value}" '
($1 == dom)  &&
($2 == type) &&
($3 == item)     { $4 = val ; foundit=1 }                # overwrite field 4
                 { print }                               # print current line
END { if (! foundit) { print dom, type, item, val } }    # if not found, print all inputs as a new line
' limits.conf

A couple sample runs:

$ update-ulimits '*' hard rss 20000
*               soft    core            0
* hard rss 20000
@student        hard    nproc           20
@faculty        soft    nproc           20
@faculty        hard    nproc           50
ftp             hard    nproc           0
@student        -       maxlogins       4

$ update-ulimits bob hard nofile 1024
*               soft    core            0
*               hard    rss             10000
@student        hard    nproc           20
@faculty        soft    nproc           20
@faculty        hard    nproc           50
ftp             hard    nproc           0
@student        -       maxlogins       4
bob hard nofile 1024

NOTES:

  • OP can decide if awk should automatically overwrite the source file (see the -i flag, eg: Save modifications in place with awk ) or perhaps add additional code to first save the original file before overwriting it.
  • Since column alignment isn't an issue when adding new lines to limits.conf I took the easy way out and didn't bother with maintaining whitespace for modified lines; if maintaining original white space is desired take a look at the answers to How to preserve the original whitespace between fields in awk?.

Upvotes: 2

Related Questions