0

I wanted to grep all the .txt files using wildcard character '*'.
I tried this command (as well as without the quotations " ") but failed.

ls | grep "*.txt"

The interesting thing is that if I put another character in the grep command corresponding to a .txt file in the directory, it works

>>ls | grep s*.txt
sample.txt

I know that ls *.txt will work but I was a bit amazed by the nature of the grep command. Could someone help me why is this happening?

Is it because of the fact that grep uses regular expressions, please help.

5
3

In regexes, * means "any number of the previous item", not "any number of any characters", like it does in shell patterns. And . means "any single character". So, to look for "anything, followed by literal .txt", you'd use .*\.txt. Or just \.txt, since usually regex matches search for the match anywhere in the line.

The regex *.txt is either meaningless, an error, or looks for a literal asterisk, depending on the implementation. Best not to use it.

On the other hand, s*.txt would look for "any number of letters s, then any single character, then literal txt". That's a more valid regex, but... still doesn't match sample.txt.

Instead, what happens in your second command, is that because s*.txt is not quoted, the shell expands the s*.txt before grep sees it. If the only matching file is sample.txt, then grep goes looking for that in the output of ls.


But, ls can take a list of files too, so while you could use

ls | grep '\.txt'

to get any .txt file, it'd probably be easier to just use

ls *.txt 

instead.

1

It is in part because grep uses regular expressions (in fact, that's what the re in the name stands for- it's short for global regular expression print).

The * wildcard in regular expressions is different from the * wildcard in shell globbing.

In regular expressions, * means "zero or more of the previous defined object". However, . is also a wildcard, meaning 'one character'.

In shell globs, * means "zero or more characters". . is not a wildcard at all.

When you grep for the pattern "*.txt", you are looking for zero or more of anything, followed by exactly one more character, followed by the literal string txt.

When you grep for the pattern "s*.txt"m you are looking for a literal s, followed by zero or more ss, followed by any character, followed by the literal string txt`.

This is why one common thing you will find in regular expressions is .*, which means "one of any character followed by zero or more of any character". Regexese for "literally any combination of characters other than zero characters".

When you ls *.txt you are telling the shell "Find any filenames that match the glob pattern *.txt, list them here, and provide those as arguments to the ls command.

3
  • 1
    Specifically, the regular expression s*.txt means: zero or more "s" characters, followed by any single character (that's what . means in an RE), followed by "txt". – Gordon Davisson Aug 14 '20 at 21:52
  • Why *.txt means zero or more of anything in regex? What is the previous regex in this case? – LRDPRDX Aug 15 '20 at 0:50
  • @LRDPRDX it doesn't, a repetition character like * coming first in a regexp like *.txt is undefined behavior so YMMV with what any given tool does with it. – Ed Morton Aug 15 '20 at 3:56
0

please note that grep is searching file content while first argument is search PATTERN and other arguments interpreted as FILES to look into

it becomes more clear to you when using grep -H -o flags or put your grep inside a script and run it with bash -x script to see how shell globs expanded before passed as arguments

1
  • refer to askubuntu @GordonDavisson comment which also explain that behavior – alecxs Aug 14 '20 at 23:21

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.