Using grep
Why can't you just use the -r
switch to grep
to recurse the filesystem instead of making use of find
? There are 2 additional switches I'd use too, instead of the -n
switch.
$ grep -rHn PATTERN <DIR> | cut -d":" -f1-2
Example #1
$ grep -rHn PATH ~/.bashrc | cut -d":" -f1-2
/home/saml/.bashrc:25
Details
-r
- recursively search through files + directories
-H
- prints the name of the file if it matches (less restrictive than -l
) i.e. it works with grep
's other switches
-n
- display the line number of the match
Example #2
$ grep -rHn PATH ~/.bash* | cut -d":" -f1-2
/home/saml/.bash_profile:10
/home/saml/.bash_profile:12
/home/saml/.bash_profile_askapache:99
/home/saml/.bash_profile_askapache:101
/home/saml/.bash_profile_askapache:118
/home/saml/.bash_profile_askapache:166
/home/saml/.bash_profile_askapache:218
/home/saml/.bash_profile_askapache:250
/home/saml/.bash_profile_askapache:314
/home/saml/.bash_profile_askapache:2317
/home/saml/.bash_profile_askapache:2323
/home/saml/.bashrc:25
Using find
$ find . -exec sh -c 'grep -Hn PATTERN "$@" | cut -d":" -f1-2' {} +
Example
$ find ~/.bash* -exec sh -c 'grep -Hn PATH "$@" | cut -d":" -f1-2' {} +
/home/saml/.bash_profile:10
/home/saml/.bash_profile:12
/home/saml/.bash_profile_askapache:99
/home/saml/.bash_profile_askapache:101
/home/saml/.bash_profile_askapache:118
/home/saml/.bash_profile_askapache:166
/home/saml/.bash_profile_askapache:218
/home/saml/.bash_profile_askapache:250
/home/saml/.bash_profile_askapache:314
/home/saml/.bash_profile_askapache:2317
/home/saml/.bash_profile_askapache:2323
/home/saml/.bashrc:25
If you truly want to use find
you can do something like this to exec grep
upon finding the files using find
.
As @steeldriver mentionned, the problem is likely to be caused by a different line ending style than what grep
is expecting.
To check the line endings
You can use hexdump
to check exactly how your line endings are formatted. I suggest you use my favorite format :
hexdump -e '"%08_ad (0x%08_ax) "8/1 "%02x "" "8/1 "%02x "' -e '" "8/1 "%_p""|"8/1 "%_p""\n"' masternospaces.txt
With the output, check the line endings : 0a
-> LF
, 0d
-> CR
. A very quick example would give something like this :
$ hexdump -e '"%08_ad (0x%08_ax) "8/1 "%02x "" "8/1 "%02x "' -e '" "8/1 "%_p""|"8/1 "%_p""\n"' masternospaces.txt
00000000 (0x00000000) 4e 6f 20 43 4f 57 20 65 6e 64 69 6e 67 0d 0a 45 No COW e|nding..E
00000016 (0x00000010) 6e 64 69 6e 67 20 69 6e 20 43 4f 57 0d 0a nding in| COW..
Note the line endings in dos format : 0d 0a
.
To change the line endings
You can see here or here for various methods of changing line endings using various tools, but for a one-time thing, you could always use vi/vim :
vim masternospaces.txt
:set fileformat=unix
:wq
To grep without changing anything
If you just want grep
to match no matter the line ending, you could always specify line endings like this :
grep 'COW[[:cntrl:]]*$' masternospaces.txt
If a blank line is shown, you can check that you indeed matched something by using the -v
option of cat
:
grep 'COW[[:cntrl:]]*$' masternospaces.txt | cat -v
My personal favorite
You could also both grep and standardize the output using sed
:
sed -n '/COW^M*$/{;s/^M//g;p;};' masternospaces.txt
where ^M
is obtained by typing Ctrl-V Ctrl-M
on your keyboard.
Hope this helps!
Best Answer
Grep mainly looks for lines that contain a match for the given pattern. Depending on the pattern, it may or may not be possible to determine whether a line matches without looking at the whole line. With
grep Match
, this is possible, but withgrep 'Match$'
, it isn't. Withgrep -o Match
, grep could printMatch
as soon as it sees it, but withgrep -E -o '(Match)+'
, if grep has readMatchMa
, it doesn't know whethertch
will follow or not.Grep does not implement the special cases where it would be possible to write some output before seeing a whole line. (I think it doesn't implement any such special case, but I'm not completely sure: GNU grep has several modes depending on the pattern that behave somewhat differently.) It just reads one whole line before trying to match.
If there's a character that's never appears in matching text and that always (or at least often) appear between matches, transform this character into a newline. (Or into a null byte and use
grep -z
.)Note the use of
stdbuf -o0
to preventtr
from buffering its output. And if you pipe the output of grep, you'll need that for grep as well, or usegrep --line-buffered
. (--line-buffered
buffers the output of grep; it has no impact on how grep reads input, and it's on by default when grep is printing to a terminal.)