114

If I do:

$ ls -R
.:
4Shared/  Cloud/

./4Shared:
UFAIZLV2R7.part3.rar

./Cloud:
UFAIZLV2R7.part2.rar.part
UFAIZLV2R7.part1.rar.part
UFAIZLV2R7.part4.rar.part

If I want to list the .rar files only, and I use grep, it will show me too the .rar.part files, what is not my wish.
I am solving this using find or ls **/*.rar as told in this thread and they work fine, but I would like to learn if it is possible to do it via grep.

I have tried (thinking about EOL):

ls -R | grep ".rar\n"

with no results.
I think that the problem lies in discover if the greping is found at the end of the line, but I am not sure.

Any help out here, please?

  • Why would you want to use grep in this case? Why not find? – devnull Apr 13 '14 at 09:31
  • 2
    @devnull, knowing how to detect patterns that are only at the end of a line could be useful in many cases. For example: portability, usage in routers with embedded too-simplistic Linux, usage with UnxUtils for Windows (its find command conflicts with the Windows one) and... learning ;-) . The question is not about "Listing Files in a dir" (that is just a custom example) but rather "Understanding the grep command usage" – Sopalajo de Arrierez Apr 13 '14 at 12:09
  • 2
    honestly, if you're counting on the end of a line to be your separator, then you should be using ls -1R. – mikeserv Apr 13 '14 at 14:48
  • 2
    And you don't even need grep. See my answer. – mikeserv Apr 13 '14 at 15:02
  • @mikeserv, what could happen without the -1R switch. Apparently the results are the same. – Sopalajo de Arrierez Apr 14 '14 at 01:01
  • @SopalajodeArrierez Yeah - needs -q not -1. – mikeserv Apr 14 '14 at 01:02

8 Answers8

147

The $ anchor matches the end of a line.

ls -R | grep '\.rar$'

You can also use find for this:

find . -name '*.rar'
jordanm
  • 42,678
  • 2
    I didn't realize the "." needs to be escaped in grep. Is it treated as a wild card? – thebunnyrules Mar 13 '18 at 15:22
  • 3
    @thebunnyrules "." in regex means one of any character. – jordanm Mar 13 '18 at 16:11
  • 2
    This does not work for me on macOS with zsh. Soon as I add the $ I get no matches, when I can see without the $ that I should get a match. ^ works for beginning of the line, but $ doesn't work for EOL – swpalmer Jan 23 '22 at 01:44
  • @swpalmer Only thing I can think is you might need ls -lR on mac, assuming -R doesn't imply -l like it does in linux. The find version is POSIX and should work everywhere. – jordanm Jan 23 '22 at 02:25
  • 2
    @jordanm Sorry, the issue is with grep not ls. I'm doing "cat list_of_words.txt | grep '^lunar$'" Without the $ I get a list of 5 words: lunar, lunaria, lunarian, lunarians, lunars .. if I add the $ then I expected to get just "lunar" but I get nothing. Using \b instead of $ works. – swpalmer Jan 23 '22 at 03:18
  • @swpalmer that means your line doesn't end in a "r", it ends in a blank space after the "r" – jordanm Jan 23 '22 at 18:05
  • 1
    @jordanm Sort of, I confirmed the problem by dumping the hex bytes with 'od -tx1a'. The line separators in that file are /r/n not /n – swpalmer Jan 24 '22 at 00:32
14

In addition to your question please note that .rar does not only match ".rar" but matches every single character (including .) before the rar. In this case probably not a problem but . must be escaped in regexes.

ls -R | grep "\.rar$"
Hauke Laging
  • 90,279
7

You can also instruct grep to look for your string starting at a word boundary. There is such a boundary between . (a non-word character) and r (a word character). Depending on your grep implementation, the word boundary operator can be \b or possibly \< or [[:<:]] (boundary left of a word only), \> or [[:>:]] (right).

$ ls -R | grep '\brar$'

Example

Say I have this sample data.

$ ls -1
afile.rar
xrar
UFAIZLV2R7.part1.rar.part
UFAIZLV2R7.part2.rar.part

This command would find only the file with the .rar extension.

$ ls -R | grep '\brar$'
afile.rar

How this works?

The metacharacter \b is an anchor like the caret and the dollar sign. It matches at a position that is called a "word boundary". This match is zero-length.

Situations where this won't work

If you have files that are named blah-rar these will get detected as well.

$ ls -R | grep '\brar$'
afile-rar
afile.rar

That's because characters other than a alphanumerics are typically considered boundary characters, and so would slip past this approach.

slm
  • 369,824
  • Seems to be the same at first sight, but it is slightly different, indeed. Thanks, @slm. Does it bother if I use double quotes " instead of simple quotes? – Sopalajo de Arrierez Apr 13 '14 at 01:24
  • 2
    @SopalajodeArrierez - nope works either way. This will find any files that may be named starting w/ .rar. But these won't be an issue with the use of ls -R. Only if you happened to use ls -Ra. – slm Apr 13 '14 at 01:26
  • Would one of you be interested in explaining this slight difference to the public? – Hauke Laging Apr 13 '14 at 14:02
  • @HaukeLaging The -P switch to grep in my example. That triggers PCRE interpretation of the argument. – slm Apr 13 '14 at 14:09
  • @slm That's obvious but why is that useful? echo xrar | grep -P '.rar$' – Hauke Laging Apr 13 '14 at 14:15
  • @HaukeLaging - ah crap. I though I added an example in my test cases that covered that scenario but now I see I've missed that. Thanks! I'll delete, this will do nothing additional to the regular grep. I thought it was doing literal dots by default. – slm Apr 13 '14 at 14:35
  • @HaukeLaging - here's I've recast this A using \b instead of PCRE. Thanks for the heads up. – slm Apr 13 '14 at 14:39
  • slm, if you're using grep you need to keep using -1R to handle file/dir names containing \n - but if you look at mine... – mikeserv Apr 13 '14 at 15:17
  • You're point about ls -1R is valid. The regex you used in your example didn't quite work though. Might be able to refactor it? – slm Apr 13 '14 at 15:30
  • @slm, there is no -P switch right now on your text. Maybe you have accidentally deleted it on last edit? – Sopalajo de Arrierez Apr 14 '14 at 01:08
  • @SopalajodeArrierez - further testing showed that wouldn't have worked so I've recast this answer in a different direction now. – slm Apr 14 '14 at 01:24
  • Yeah, a bit messy. But the \b info is very useful, anyway. Thanks, @slm. The '-1' thing is what I still haven't understood. Why should it be needed? My tests show the same result without it. – Sopalajo de Arrierez Apr 14 '14 at 01:34
  • 2
    @SopalajodeArrierez - if a file contains a newline character (\n) which is a legal character. The ls -1R will force the files to be displayed in a single column regardless. – slm Apr 14 '14 at 01:43
4

Use single quotes to make the $ work as end-of-line. If you want to grep with some variable also, use combination of double and single quotes as below:

grep "$var"'$'

My previous post was deleted saying it is duplicate. Let me explain how this is different.

The other posts mention either full use of double quotes "", or full use of single quotes ''. They both have their own limitations. Following explains it.

The problem with all double quotes is following: grep "pattern$" gives following error: Illegal variable name.

And using all single quotes works, but if you want variable substitution, all single quotes will not work. For example:

If I have string A_BOOK, including other strings in a file FILE.

$ cat FILE
A_BOOK
B_BOOK_NOT_LAST
C_BOOK

If I set the BOOK to a variable BK

set BK = BOOK

If I grep with all double quotes, I get the following error: grep "${BK}$" FILE*: 1st $ for variable substitution, 2nd for end of pattern (Illegal variable name).

If I grep with all single quotes, the variable substitution does not happen. grep '${BK}$' FILE returns nothing

If I use a combination of double and single quotes, I get what I expect. Double quotes for variable substitution, and single quotes for end of pattern.

$ grep "${BK}"'$'  # << gives expected output
A_BOOK
C_BOOK
0

Just do :

ls -1R -I"?" -I"??" -I"???" -I"*[!.][!r][!a][!r]"

You don't need grep at all.

NOTE: The above works... except it still gets at least afile-rar and I don't understand why. I'll leave it here, but I'm not proud of it. In any case, as others have said:

find . '*.rar'
slm
  • 369,824
mikeserv
  • 58,310
  • This didn't block files named xrar or afile-rar. – slm Apr 13 '14 at 15:27
  • I'm still getting the other files in the output. – slm Apr 13 '14 at 15:36
  • @slm What other files? I think it might be cause they're too short. I just noticed that myself. I fixed that too. – mikeserv Apr 13 '14 at 15:45
  • The files afile-rar and xrar are still being included in the output. No change with your latest mods either. Pesky problem no? It's fun trying to solve it without the regular methods 8-) – slm Apr 13 '14 at 16:25
  • @slm Yeah, it's why I come here. I don't understand why the -dash gets through. The xrar thing I could handle, but not the -dash. I don't understand the -dash. – mikeserv Apr 13 '14 at 16:32
0

If after following the above and nothing works, it could be due to line-endings. To fix, do: dos2unix pr0n.txt and do your grep again.

daparic
  • 286
0

I had this problem, and something came to mind suddenly. Terminal color codes... At some stage I actually aliased ls to ls --color=always and that meant that line start and line end may be terminal color escape sequences instead of the content that's visible on the screen.

I have since changed my alias back to ls --color=auto, so ls | grep "^ABC" now works as expected.

Albert
  • 141
-1

Use pattern followed by $ in double quotes.

Example: I want to find the line that ends with semicolon in a file hey.txt

grep ";$" hey.txt

Output : prints the lines that end with semicolon.

AdminBee
  • 22,803