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)

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
  • 1
    Do you want to add the first missing pts/n, or all of them?
    – choroba
    Apr 12 '17 at 11:07
  • Add if any are missing and all if none are present. Apr 12 '17 at 11:09
  • 1
    Using a different delimiter than / works for s command, but not for addressing, so it would be '/pts\/3/a pts/4'
    – Philippos
    Apr 12 '17 at 12:50
  • You can use a different delimiter when matching, too, but you need to escape it. '\+pts/3+a pts/4'
    – tripleee
    Apr 16 '17 at 6:39
  • If any of the existing answers solves your problem, please consider accepting it via the checkmark. Thank you!
    – Jeff Schaller
    Apr 23 '17 at 13:06

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

   # we meet a valid pts/ line
      # the hold space gets appended to the pattern space
      # 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

   # 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

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

' /etc/securetty 
  • 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? 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 Apr 14 '17 at 5:36

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\

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

  • 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? 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.
    – user218374
    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. 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). Apr 12 '17 at 13:55

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
.r ! printf pts/\%d\\\n {0..9}

(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-9]$/d' /etc/securetty

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

  • Tried the above sed command out, it goes to > prompt! Apr 14 '17 at 5:30
  • Is it possible you missed the trailing ' for the sed statement?
    – Jeff Schaller
    Apr 14 '17 at 11:59

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
   grep "pts/${x}" /etc/securetty || echo "pts/${x}" >> /etc/securetty
sort /etc/securetty -o /etc/securetty
  • 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. Apr 14 '17 at 7: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 6:41

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:

printf 'pts/%d\n' {0..9} \
| grep -vFf "$1"  - >> "$1".new
mv "$1".new "$1"
  • 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. Apr 12 '17 at 12:20

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.