Use sed command to check if a particular line exists, if not, add it

6,228

Solution 1

We note down the pts/ number when we meet the corresponding line. The -p option will autoprint lines. When we reach the eof we pull out the hash %h to and pass it thru the grep filter to determine which terminals didn't print and we use map to prepare the format for that to happen.

perl -lpe 'm|^pts/([0-9])$| and $h{$1}++;
   END{ print for map { "pts/$_" } grep { !$h{$_} } 0 .. 9; }
' /etc/securetty

We initialize the hold space with numbers 0 1 2 ... 9. Whenever we meet the pts/[0-9] line, we clip this from the hold space. At eof, we get hold of hold space and if any numbers are found shall the massaged to the proper format and printed out.

sed -e '
   # initialize the hold space with 0 1 ... 9
   1{x;s|.*|'"$(echo {0..9})"'|;x}

   # whatever be the line, it needs to be printed
   p

   # we meet a valid pts/ line
   \|^pts/[0-9]$|{
      # the hold space gets appended to the pattern space
      G
      # grab what is the pts number and search for it in the hold and
      # delete itand store back the changes into hold space.
      s|^pts/\([0-9]\)\n\(.*\)\1 |\2|;h
   }

   # weve not arrived at the eof and weve processed the input so go no further
   $!d

   # we are at the eof, so we bring back the hold space. just in case all
   # numbers were dealt with up, we simply bail out. Else, prepend the str 
   # pts/ to the numbers present and simply were home
   g;/[0-9]/!d;s/ //g
   s|[0-9]|pts/&\n|g;s/.$//

   # *TIP*: Sprinkle the l, list pattern space at various places to see 
   # whats going on.

' /etc/securetty 

Solution 2

Remove any/all pts/N lines, then add them all back in:

{ grep -xv '^pts/[0-9]$' /etc/securetty; printf 'pts/%d\n' {0..9}; } > /etc/securetty.new
cat /etc/securetty.new
mv /etc/securetty.new /etc/securetty

You could also do this in one go with your favorite text processing tool e.g. ed

ed -s /etc/securetty <<IN
g/^pts\/[0-9]$/d
.r ! printf pts/\%d\\\n {0..9}
,p
q
IN

(replace ,p with w to edit in-place) or sed

{ printf '%s\\\n' '$a' pts/{0..8}
printf '%s\n' 'pts/9' '/^pts\/[0-9]$/d'
} | sed -f- /etc/securetty

which is pretty much the same as plain

sed '$a\
pts/0\
pts/1\
pts/2\
pts/3\
pts/4\
pts/5\
pts/6\
pts/7\
pts/8\
pts/9
/^pts\/[0-9]$/d' /etc/securetty

(use sed with -i to edit the file in-place)

Solution 3

To add a single line when it's missing can be done by removing each occurence and appending it at the end:

sed -n '/pattern/!p;$a pattern'

But it's nasty to repeat that for 10 patterns.

sed '/pts\/[0-9]/d;$a pts/0 ...

will fail if the last line is to be removed. So the other way around, assuming the first line is the only one starting with #:

sed '/#/a pts/0\
pts/1\
pts/2\
pts/3\
pts/4\
pts/5\
pts/6\
pts/7\
pts/8\
pts\9
/pts\/[0-9]/d'

Nasty. I suggest to use a different tool in this case.

Solution 4

You could search the securetty file and add missing entries as follows:

for x in 0 1 2 3 4 5 6 7 8 9
do 
   grep "pts/${x}" /etc/securetty || echo "pts/${x}" >> /etc/securetty
done
sort /etc/securetty -o /etc/securetty
Share:
6,228

Related videos on Youtube

skrowten_hermit
Author by

skrowten_hermit

Updated on September 18, 2022

Comments

  • skrowten_hermit
    skrowten_hermit over 1 year

    I want to add more terminals to the file /etc/securetty. More specifically I would like to add pts/n, where n is in the range 0-9, if it does not exist. Is this possible through sed command? The following is how the contents of my /etc/securetty are:

    # Local X displays (allows empty passwords with pam_unix's nullok_secure)
    pts/0
    pts/1
    pts/2
    pts/3
    

    I tried some thing like:

    sudo sed '+pts/3+a pts/4' /etc/securetty
    

    which gives the following error:

    sed: -e expression #1, char 3: extra characters after command
    
    • choroba
      choroba about 7 years
      Do you want to add the first missing pts/n, or all of them?
    • skrowten_hermit
      skrowten_hermit about 7 years
      Add if any are missing and all if none are present.
    • Philippos
      Philippos about 7 years
      Using a different delimiter than / works for s command, but not for addressing, so it would be '/pts\/3/a pts/4'
    • tripleee
      tripleee about 7 years
      You can use a different delimiter when matching, too, but you need to escape it. '\+pts/3+a pts/4'
    • Jeff Schaller
      Jeff Schaller about 7 years
      If any of the existing answers solves your problem, please consider accepting it via the checkmark. Thank you!
  • choroba
    choroba about 7 years
    The script only adds missing lines, it doesn't remove anything. The argument is the name of the file (/etc/securetty). Don't run the script if you don't understand how it works!
  • skrowten_hermit
    skrowten_hermit about 7 years
    But my file contains other terminal names as well. Say, ttyUSB0 etc. They would still remain right? I was assuming sed should have some way to get around this. Saw examples using sed where you could change lines.
  • skrowten_hermit
    skrowten_hermit about 7 years
    I'm looking for something like this, but would like to understand what this does. Could you add comments/explanation line by line what's going on? And the perl command is for?
  • Philippos
    Philippos about 7 years
    The sed solution is a beautiful hack. Nothing I would do in real life, but surely a brilliant puzzle! +1
  • skrowten_hermit
    skrowten_hermit about 7 years
    I like this idea, though it's a bit nasty (more sed commands can be piped right?). But sed '/pts\/[0-9]/d;$a pts/0' /etc/securetty appended pts/0 to the end of the file. I would like to insert in place. Any way to do this by modifying/tweaking a bit?
  • Philippos
    Philippos about 7 years
    @skrowten_hermit: Sharma's sed solution does leave the lines where they are and only append the missing ones at the end. You could even use the -i option to edit in place, if your sed version supports it.
  • Admin
    Admin about 7 years
    @Philippos I would like to give this solution very inventive. But somehow I am not able to give any marks to anything on this site. I would advise @ skrowten to go with this as it is short/swift/less noise.
  • skrowten_hermit
    skrowten_hermit about 7 years
    @Philippos, Nope. Tried -i option. It still appends to the bottom of the file. I wanted to insert below the line # Local X displays (allows empty passwords with pam_unix's nullok_secure) after the lines are removed.
  • skrowten_hermit
    skrowten_hermit about 7 years
    I entered the following sed -e '1{x;s|.*|'"$(echo {0..9})"'|;x}p\|^pts/[0-9]$|{s|^pts/\([0-9]\)\n\(.*\)\1 |\2|;h}$!dg;/[0-9]/!d;s///gs|[0-9]|pts/&\n|g;s/.$//' /etc/securetty and the following error is thrown: sed: bad option in substitution expression
  • skrowten_hermit
    skrowten_hermit about 7 years
    Though this does add the lines, and is an alternative to sed, it doesn't really add the missing entries. Moreover, it doesn't add in-place but append to the end of the file.
  • Jeff Schaller
    Jeff Schaller about 7 years
    Is it possible you missed the trailing ' for the sed statement?
  • L.Ray
    L.Ray about 7 years
    It certainly does add the missing lines to the /etc/securetty file (don't be fooled by the output to your screen). I added a line to address your sort concerns.