3

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
skrowten_hermit
  • 751
  • 4
  • 13
  • 34

5 Answers5

3

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 
Jeff Schaller
  • 67,283
  • 35
  • 116
  • 255
  • 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? – skrowten_hermit Apr 12 '17 at 13:17
  • 1
    The sed solution is a beautiful hack. Nothing I would do in real life, but surely a brilliant puzzle! +1 – Philippos Apr 12 '17 at 13:26
  • 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 Apr 14 '17 at 05:36
1

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)

Jeff Schaller
  • 67,283
  • 35
  • 116
  • 255
1

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.

Philippos
  • 13,453
  • 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? – skrowten_hermit Apr 12 '17 at 13:26
  • @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. – Philippos Apr 12 '17 at 13:30
  • @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. –  Apr 12 '17 at 13:35
  • @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 Apr 12 '17 at 13:53
  • @RakeshSharma, I honestly am lost with your solution. It'll be great if you could explain it in parts (probably by adding comments). – skrowten_hermit Apr 12 '17 at 13:55
1

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
L.Ray
  • 469
  • 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. – skrowten_hermit Apr 14 '17 at 07:31
  • 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. – L.Ray Apr 14 '17 at 15:59
  • Though the number of loop iterations is limited, repeatedly grepping the same file in a loop is a code smell I'd like to avoid. – tripleee Apr 16 '17 at 06:41
0

sed processes the file line by line and it's very hard to make it "remember" any information across lines.

You can use grep to find out whether a file contains a given pattern; with -f, you can supply multiple patterns at the same time. The following generates the full list pts/0 .. pts/9, then removes the patterns already present in the given file, and adds the remaining ones into the file:

#!/bin/bash
printf 'pts/%d\n' {0..9} \
| grep -vFf "$1"  - >> "$1".new
mv "$1".new "$1"
choroba
  • 47,233
  • 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! – choroba Apr 12 '17 at 12:19
  • 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 Apr 12 '17 at 12:20