2

In one of my lab questions I was asked to list all entries(both files and folders) in the current directory whose names contain only uppercase letters using piping and grep command. Folders must have a forward slash displayed. Initially, my solution looked like this:

ls -p | grep -e "^[[:upper:]]*\/?$"

I thought that I can protect the '/' sign with the backward slash(as in echo \* vs echo *). However, it doesn't work as intended. One of my friends suggested the following solution:

ls -p | grep -e '^[[:upper:]]*/\?$'

I have no idea why the backward slash is used like this. What is even more weird,for me, that it actually produces the right result. I was wondering if someone could explain where the mistake in my solution is and why the second version actually works.

2
  • Why not parse ls? – Cyrus Sep 30 '19 at 0:19
  • It was explicitly said to use grep for that task , so that's what I went for. However, I would be grateful to see another way to solve it – Alex.Kh Oct 1 '19 at 21:43
1

By default, grep uses basic regular expressions (BRE), in which ? matches a literal question mark. Adding the backslash made it have your desired interpretation as a quantifier meaning "zero or one occurrences of the previous expression" (i.e. optionally matching the trailing /).

From man grep:

Basic vs Extended Regular Expressions
           In  basic  regular expressions the meta-characters ?, +, {, |, (, and )
           lose their special meaning; instead use the  backslashed  versions  \?,
           \+, \{, \|, \(, and \).

You could achieve the same by switching to grep into extended regular expression (ERE) mode by adding the -E or --extended-regexp flag:

ls -p | grep -E -e '^[[:upper:]]*/?$'

Note that / is literal in both BRE and ERE - so doesn't need to be escaped in either case.

You could also consider using -x to enforce a whole-line match without the explicit ^ and $ anchors.


Note that (although it's fine as an exercise), piping ls to grep is generally not a recommended way to select files. In bash, you could instead consider using extended globbing:

shopt -s extglob nullglob

ls -pd *([[:upper:]])
1
  • Oh, thanks a lot! I guess I did actually forgot about the Basic and Extended Regular Expressions. I will definitely look into that. Also, even though / is a literal , as you noted, you answer will be very helpful in the case when the character has to be protected. – Alex.Kh Sep 29 '19 at 22:11

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.