1

I'm trying to use ls in zsh (running on macOS) to show all files and directories except directories that begin with a capital letter.

For example, my directory contains

Archive/ data/ README.md test.txt

and I would like to run an ls command that returns only

data/ README.md test.txt

I can use ls -d [A-Z]*/ (note the terminating backslash to indicate directories) to show the directories I want to hide (e.g. only returns Archive/),

and referencing this helpful answer on using the inverted expansion in zsh with the ls *~ syntax,

I tried (what I think is) the negation of the above using ls -d *~[A-Z]*/ but this doesn't work (it hides nothing).

Moreover, using ls -d *~[A-Z]* (without the terminating backslash) returns data/ test.txt but this is not my desired result since I also want to show the file README.md which begins with a capital letter.

Note that I have enabled the extended glob option in zsh, using setopt extendedglob.

Any help on the correct regex/glob syntax for ls in zsh to obtain my desired output would be very much appreciated. Thank you! :)

Edit: There are two very useful answers that work, but any concise answers using ls in zsh (using the extended glob option) would still be awesome!

2 Answers2

0

My version of ls does not directly support regular expressions, I think you are depending upon shell globbing. Perhaps try piping to grep. Something like:

 ls  | grep -v  '^[A-Z].*'

Notice that the regular expression is in quotes. The ^ specifies the beginning of the string. The switch -v is the negation of the match.

Sorry, now I understand your requirements better. You could string the find command to do this. Try the following:

find -E . -regex "\./[^A-Z].*" -type d -exec echo Directory: {} ';' -exec find {} -type f -maxdepth 1 \; 

I tested this on a Mac with the following hierarchy:

find .
.
./Test
./Test/Plain File
./aatest
./aatest/BigTest
./aatest/BigTest/Big File
./aatest/BigTest/small File
./aatest/bb File
./aatest/aaFile

And got the following output:

Directory: ./aatest
./aatest/bb File
./aatest/aaFile
Directory: ./aatest/BigTest
./aatest/BigTest/Big File
./aatest/BigTest/small File

Please note that I added the text "Directory" to the echo command to differentiate the two types of output.

Bill
  • 1,588
  • 12
  • 21
  • Thanks @Bill! This is useful. However, it still has the problem of also hiding capitalized _files_, not only capitalized directories. –  May 05 '21 at 16:29
  • Appreciate your update @Bill. This now works! I can then just pipe to `ls` for desired results. Ideally I was hoping for a syntactically simple function using `ls` like in my attempted code in the question, but this will do the trick for an alias. Many thanks! –  May 06 '21 at 00:01
0

Maybe a bit verbose, but you might use ls -l and prevent the total using grep -v "^total" and then pipe the output to awk.

In awk, print the last field followed by / if the first field starts with d and the last field does not start with an uppercase char A-Z

Or print the last field if the the first field does not start with d

ls -l | grep -v "^total" | awk '{
  if ($1 ~ /^d/ && $NF ~ /^[^A-Z]/){
    print $NF"/"
  } else if ($1 ~ /^[^d]/){
    print $NF
  }
}'

In a single line:

 ls -l | grep -v "^total" | awk '{if($1~/^d/ && $NF~/^[^A-Z]/){print $NF"/"} else if($1~/^[^d]/){print $NF}}'
The fourth bird
  • 154,723
  • 16
  • 55
  • 70