8

I'm trying to get the output from ls /dev to match 'tty' that ends with numbers between 1-4.

So from:

tty5
tty4
tty2
tty6
tty1

Should match:

tty4
tty2
tty1

The regexp

"\s([tty]+[0-4])\s"

works in RegExr.

I've tried using this with grep:

ls /dev | grep -E \s([tty]+[0-4])\s

ls /dev | grep -E \s([tty]\+\[0-4])\s

ls /dev | grep -Ex \s([tty]+[0-4])\s

ls /dev | grep -P \s([tty]+[0-4])\s

as I've read in other posts, still I can't make it work.

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

3 Answers3

31

The reason it isn't matching is because you are looking for whitespace (\s) before the string tty and at the end of your match. That never happens here since ls will print one entry per line. Note that ls is not the same as ls | command. When the output of ls is piped, that activates the -1 option causing ls to only print one entry per line. It will work as expected if you just remove those \s:

ls /dev | grep -E '([tty]+[0-4])'

However, that will also match all sorts of things you don't want. That regex isn't what you need at all. The [ ] make a character class. The expression [tty]+ is equivalent to [ty]+ and will match one or more t or y. This means it will match t,or tttttttttttttttt, or tytytytytytytytytyt or any other combination of one or both of those letters. Also, the parentheses are pointless here, they make a capture group but you're not using it. What you want is this:

$ ls /dev | grep '^tty[0-4]$'
tty0
tty1
tty2
tty3
tty4

Note how I added the $ there. That's so the expression only matches tty and then one number, one of 1, 2, 3 or 4 until the end of the line ($).

Of course, the safe way of doing this that avoids all of the dangers of parsing ls is to use globs instead:

$ ls /dev/tty[0-4]
/dev/tty0  /dev/tty1  /dev/tty2  /dev/tty3  /dev/tty4

or just

$ echo /dev/tty[0-4]
/dev/tty0 /dev/tty1 /dev/tty2 /dev/tty3 /dev/tty4
terdon
  • 242,166
  • 1
    Great answer. Out of curiosity, in your regex solution (before you get into why you should use globbing instead -- which you totally should!), why not start the regex with ^? – JakeRobb Jan 20 '20 at 18:09
  • 1
    @jakerobb um, because I didn't think to do it! I should have though, you're quite right. Thanks :) – terdon Jan 20 '20 at 19:58
  • 2
    also /dev/tty{1..4} :) – Eevee Jan 20 '20 at 20:34
  • 1
    @Eevee that depends on the shell you're using. – reinierpost Jan 21 '20 at 10:15
  • 1
    @Eevee yes, not only is that not portable, it also prints error messages if any of those don't exist (with ls) and will print everything regardless of whether it exists with echo. – terdon Jan 21 '20 at 10:17
  • @terdon which may or may not be what you want! but imo it's more appropriate for counting — wildcards get a little messier if you want, say, ttys 0 through 13 – Eevee Jan 21 '20 at 19:49
  • If the OP wants to have each file printed on its own line, (s)he could use printf %s\\n /dev/tty[1-4]. It's just like the echo command from your answer, but with newlines instead of spaces. – TSJNachos117 Jan 21 '20 at 22:39
  • Also, do you really need the "-E" in ls /dev/* | grep -E '^tty[0-4]'? It seems to me that said switch doesn't do anything that the OP needs. – TSJNachos117 Jan 21 '20 at 22:44
  • @TSJNachos117 what makes you think the OP would want that? But yes, indeed, that is one way of doing it. As for the -E, you're quite right, I don't need it. I just copied it from the OP, thanks for pointing it out. – terdon Jan 21 '20 at 22:46
  • Honestly, I have no idea what the OP wants. I just though I might point out that option, since it would give the same output as most of the other options in your answer. – TSJNachos117 Jan 21 '20 at 22:49
7

That regex seems a little overcomplicated. Also, you should be using quoting!

$ ls /dev | grep -E "tty[1-4]$"
tty1
tty2
tty3
tty4

However, you should definitly read this: Why *not* parse `ls` (and what to do instead)?

Panki
  • 6,664
  • That's great thanks, I'm still curious why it works in Regexr and not in grep -P, yeah forgot to write quotations in the post, I use them. Thanks I will have a look at that link. – Surister Jan 20 '20 at 09:55
  • 2
    Most regex engines are a little bit different from each other. Even regexr has support for "Javascript" and "PCRE" variants in the top right menu. Also grep supports two different variants through grep and grep -E (which is same as egrep). All of these four are different to some extent. grep -P is the same as PCRE though. – Jonas Berlin Jan 20 '20 at 10:38
  • 1
    I'd advise single quotes instead of double quotes in this answer. – reinierpost Jan 21 '20 at 10:15
-1
bash-4.2$ ls -l /dev | grep -o "tty[1-4]$"
tty1
tty2
tty3
tty4


bash-4.2$ ls -l /dev | awk '/tty[1-4]$/{print $NF}'
tty1
tty2
tty3
tty4

bash-4.2$ output=$(ls /dev); echo "${output}" | grep "tty[1-4]$"
tty1
tty2
tty3
tty4
Kamaraj
  • 4,365
  • 9
    Why use -l? That just makes it more complicated (forcing you to use the non-standard -o and print $NF instead of print) for no benefit. And why would you store it in a variable to then print the same variable again? – terdon Jan 20 '20 at 10:09