How to grep query all files for two strings - bash

Here we go:
I need to query php files which both have a TODO statement as well as my name.
Both strings could be anywhere in the document (ie. line) and be positioned anywhere on 0-infinite lines (position 0-n).
How to grep query for my name:
find -name '*.php' -exec grep -in "fincken" {} +
output:
./some/file.php:51: ramon fincken
./somefile.php:2: rfincken
How to grep query for the TODOs
find -name '*.php' -exec grep -n "TODO" {} +
output:
./some/file.php:53: // TODO: foobar!
./some/otherfile.php:53: // TODO: foobar?
I need to combine both grep queries (or their results) so I am expecting this as result:
./some/file.php
I have tried operators in one grep, but they expected both strings on the same line and in a particular order or .. came up with all results (OR .. OR) instead of ( AND )

this line looks ugly, but it should give what you want:
find whatever...|xargs grep -il 'fincken'
|xargs grep -il 'todo'
|xargs grep -in -e'todo' -e'fincken'
The output would look like:
/foo/bar/file : 100:TODO
/foo/bar/file : 101:fincken
only files with both TODO and fincken would be listed.

Ask the first grep to return just the file name and then pipe to another grep:
find -name '*.php' -exec grep -li "fincken" {} + | xargs grep -l "TODO"
From man grep, -l (L) returns file name. This way, the find comman will return a list of files that will be processed one by one through the xargs command.
Your output will be the list of files which contain both "fincken" and "TODO". You can of course pipe more xargs grep -l if you want to add more words to find.
You can also do use of grep alone like this, using -R to do a recursive search:
grep -Rl --include="*php" "TODO" * | xargs grep -il "fincken"
Note I moved the TODO grep to be done in the first place, because you use -i for "fincken" and it is way slowlier. This way, the grep -i will only be run on the already filtered results.

You can pipe the first grep through a second one, get the name of the file and skip repetitions:
find -name '*.php' -exec grep -in "fincken" {} + | grep TODO | cut -d: -f1 | uniq

People are making this more complicated then it needs to be. -exec will take the exit code of the command it runs and use it logically in find. So you can just do
find -name '*.php' -exec grep -iq "fincken" {} \; -exec grep -iq "TODO" {} \; -print
Which will get to the -print only if both -exec blocks return 0.

Related

How do I print all the files older than 10 days containing particular string?

I have tried this but not working.
find . -mtime +10 -print| grep -H -r "test" | cut -d: -f1
You can make use of xargs and process the files found by find, but find alone can make it:
find . -mtime +10 -exec grep -l "test" {} \+
find ... -exec XXX {} \; (or \+, thanks Kevin) performs the XXX command on the files found by find.
grep -l just shows the name of the files, as I think you are trying to get with cut -d: -f1.
You may also need to add -type f to just find files, no directories.
You have to execute using xargs like:
find . -mtime +10 -print0 | xargs -0 grep -H -r "test" | cut -d: -f1
edit
I inserted options so that you won't have problems with spaces in the filenames.

Unix command to search for text string in all files

How do I get unix to search inside ALL files for a the string "hello"?
I've tried the following but it doesn't work:
sudo grep "hello" | find / -name "*" 2>/dev/null
Does anyone have any other ideas?
Thank you.
Maybe this one?
sudo cd / && grep -rn "hello" *
EDIT: the 'n' option of course is not needed - it simply displays the line numbers and I find it nice. The 'r' option tells grep to perform recursive search within directories.
Use
grep -r 'pattern' /
If your grep supports -r, (GNU grep does); if not use
find / -type f -exec grep 'pattern' {} +
If your find supports -exec with +, otherwise use
find / -type f -printf '%p\0' | xargs -0 grep 'pattern'
If your find supports -printf and your xargs supports -0, or
find / -type f -print0 | xargs -0 grep 'pattern'
If your find supports only -print0 and your xargs supports -0. In all other cases fall back on
find / -type f | xargs grep 'pattern'
This is maximally compatible, with the caveat that certain unusual file names will fail to be grepped and crafted file names could pose a security risk.
Note that you will have to be root in order to be sure of searching all files and that the search will be case-sensitive unless you add -i to grep.
This?
find . -exec grep -H "hello" {} \;

grep only text files

find . -type f | xargs file | grep text | cut -d':' -f1 | xargs grep -l "TEXTSEARCH" {}
it's a good solution? for find TEXTSEARCH recursively in only textual files
You can use the -r(recursive) and -I(ignore binary) options in grep:
$ grep -rI "TEXTSEARCH" .
-I Process a binary file as if it did not contain matching data; this is equivalent to the --binary-files=without-match option.
-r Read all files under each directory, recursively; this is equivalent to the -d recurse option.
Another, less elegant solution than kevs, is, to chain -exec commands in find together, without xargs and cut:
find . -type f -exec bash -c "file -bi {} | grep -q text" \; -exec grep TEXTSEARCH {} ";"
If you know what the file extension is that you want to search, then a very simple way to search all *.txt files from the current dir, recursively through all subdirs, case insensitive:
grep -ri --include=*.txt "sometext" *

Better way to limit the unix command find by filename

I'm getting results using find with filenames that have '~' and .swp, etc. So I did the following, but is there a better way to do this? The '.*.js' -iname '*.js' part feels "redundant".
$ find ./ '.*.js' -iname '*.js' -print0 | xargs -0 grep -n ".*loginError.*"
find: `.*.js': No such file or directory
./js/signin.js:252: foo.loginError();
./js/signin.js:339:foo.loginError = function() {
./js/signin.js:340: foo.log("ui.loginError");
Try using
find . -name \*.js -print0 | xargs -0 grep -n ".*loginError.*"
That will find only files with 'js' extension and not ending in ~ or .swp
EDIT: Added '0' -print0 (edit requires 6 characters so I'm adding this; ergh!)
To do it all in one command without the xargs you could do it like this
find . -name "*.js" -exec grep -n ".*loginError.*" /dev/null {} \;
the /dev/null piece is to make grep think it's searching multiple files and then it'll output the filename correctly, otherwise it'd just print out the line number without telling you which file it's in

Use find, wc, and sed to count lines

I was trying to use sed to count all the lines based on a particular extension.
find -name '*.m' -exec wc -l {} \; | sed ...
I was trying to do the following, how would I include sed in this particular line to get the totals.
You may also get the nice formatting from wc with :
wc `find -name '*.m'`
Most of the answers here won't work well for a large number of files. Some will break if the list of file names is too long for a single command line call, others are inefficient because -exec starts a new process for every file. I believe a robust and efficient solution would be:
find . -type f -name "*.m" -print0 | xargs -0 cat | wc -l
Using cat in this way is fine, as its output is piped straight into wc so only a small amount of the files' content is kept in memory at once. If there are too many files for a single invocation of cat, cat will be called multiple times, but all the output will still be piped into a single wc process.
You can cat all files through a single wc instance to get the total number of lines:
find . -name '*.m' -exec cat {} \; | wc -l
On modern GNU platforms wc and find take -print0 and -files0-from parameters that can be combined into a command that count lines in files with total at the end. Example:
find . -name '*.c' -type f -print0 | wc -l --files0-from=-
you could use sed also for counting lines in place of wc:
find . -name '*.m' -exec sed -n '$=' {} \;
where '$=' is a "special variable" that keep the count of lines
EDIT
you could also try something like sloccount
Hm, solution with cat may be problematic if you have many files, especially big ones.
Second solution doesn't give total, just lines per file, as I tested.
I'll prefer something like this:
find . -name '*.m' | xargs wc -l | tail -1
This will do the job fast, no matter how many and how big files you have.
sed is not the proper tool for counting. Use awk instead:
find . -name '*.m' -exec awk '{print NR}' {} +
Using + instead of \; forces find to call awk every N files found (like with xargs).
For big directories we should use:
find . -type f -name '*.m' -exec sed -n '$=' '{}' + 2>/dev/null | awk '{ total+=$1 }END{print total}'
# alternative using awk twice
find . -type f -name '*.m' -exec awk 'END {print NR}' '{}' + 2>/dev/null | awk '{ total+=$1 }END{print total}'

Resources