How to find files with specific text inside and specific extension?

4

In UNIX: in folders there are files with .sas (programs) extension and files with .sas7bdat (tables). I need to find programs (and NOT tables) which contain the text "liasse". I tried by launching this command:

grep -rli liasse *.sas ./

In the result I can see a table name. What should be the command to retrieve only programs?

Nasser

Posted 2018-01-11T08:26:57.703

Reputation: 41

Answers

2

This is a POSIX-compliant command that launches grep for many files at once:

find . -type f -name '*.sas' -exec grep -li liasse {} +


Your command grep -rli liasse *.sas ./ doesn't work as you expected. Let's analyze what happens:

  1. *.sas undergoes shell globbing.
    1. If there is at least one object in the current directory that matches *.sas, all of them are placed after liasse.
    2. If there's no such object, grep may get literal *.sas as a path to be inspected (it depends; investigate e.g. shopt -s nullglob, shopt -s failglob in bash) and throw warning.
  2. Either way grep does get ./ and process it recursively in a search for liasse inside files. This processes all files under ./: programs, tables, whatever.

That's why you could get a table name in your result.


My command find . -type f -name '*.sas' -exec grep -li liasse {} + runs grep only for files that match *.sas pattern. Important things:

  • -type f chooses files only. This way I prevent directory names matching *.sas (if any) from being passed to grep (grep without -r should reject them anyway but it would be less elegant).
  • Quoting the pattern prevents shell from globbing; find gets literal *.sas as an argument to the -name operand; it knows how to interpret patterns like this.
  • find … -exec … {} + syntax substitutes multiple objects in place of {}. This way fewer (maybe just one) grep processes are created, compared to find … -exec … {} \;.

Kamil Maciorowski

Posted 2018-01-11T08:26:57.703

Reputation: 38 429

1

You can use find command for recursively finding all files by name, then run grep on each file. The simplest solution is to the -exec option of find:

find . -name '*.sas' -exec grep -li liasse {} \;

Alternatively, you can combine find with xargs:

find . -name '*.sas' -print0 | xargs -0 grep -li liasse

This has slightly better performace, because it executes grep for a large batch of files, not for every single file.

Using the -P option of xargs, you can even run multiple grep invocations in parallel.

vog

Posted 2018-01-11T08:26:57.703

Reputation: 187

I tried find . -name '*.sas' -print0 | xargs -0 grep -li liasse but it returns "The -0 flag is not valid." the full respons is find: 0652-017 -print0 is not a valid option. xargs: The -0 flag is not valid. Usage: xargs [-ptx] [-e[EndOfFileString]] [-E EndOfFileString] [-i[ReplacementString]] [-I ReplacementString | -L Number |-n Number ] [-l[Number]] [-s Size] [Command [Argument ...]] – Nasser – 2018-01-11T08:52:05.533

Nasser: Your system seems to have no support for "xargs -0". Not sure why. Just use the first variant (find -exec) instead. – vog – 2018-01-11T09:19:24.923

-1

This can be achieved with

find . -type f -name "*.sas" -print0 | "xargs" -0 -e grep -liH -e liasse

If you have no xargs implementation with -0 option, which is about communicating a set of paths between find and xargs, you might try

 find . -type f -name "*.sas" -print | "xargs" -e grep -liH -e liasse

mvw

Posted 2018-01-11T08:26:57.703

Reputation: 721

This command does not solve the issue. "grep -rli liasse .sas" take all directories ".sas" and search recusively into them. – vog – 2018-01-11T08:44:41.273

thanks for your help. but I need to search in subfolders as well. and grep -rli liasse *.sas. it searchs only in the curent folder, don't it ? – Nasser – 2018-01-11T08:47:18.710

An answer that starts with a wrong command is not very helpful for future readers. You might want to correct it, rather than appending "updates". Or, you could delete it and create a new answer. – vog – 2018-01-11T09:20:57.210

@vog I usually tend to to keep the history visible, but I have no problem to condense it to the final form. – mvw – 2018-01-11T09:24:11.820

by lauching this command ==> find . -name '*.sas' -exec grep liasse {} ; I succeed to get the content (a part) of files concerned by the text "liasse" but not the file name and its path ! – Nasser – 2018-01-11T09:34:18.350

@Nasser Please give feedback if the the version on the second grey line (the one without -0 option) works for you. – mvw – 2018-01-11T09:35:38.790

@Nasser You have to add your -li options to grep. It means"give file names" and "ignore case". Consult man grep on your system. – mvw – 2018-01-11T09:40:23.987

1many thanks to all for your help. this is the commad that respons perfectly to my meed. find . -name '*.sas' -exec grep -li liasse {} ; – Nasser – 2018-01-11T09:43:37.290