How to grep a word inside xml files in a folder - regex

I know I can use grep to find a word in all the files present in a folder like this
grep -rn core .
But my current directory has many sub-directories and I just want to search in all xml files present in the current directory and its all sub directories. How can I do that ?
I tried this
grep -rn core *.xml // Does not work
But it searches for xml files present in the current directory only. It does not do it recursively.

Try the --include option
grep -R --include="*.xml" "pattern" /path/to/dir
Reference: Grep Include Only *.txt File Pattern When Running Recursive Mode

Use find:
find /path/to/dir -name '*.xml' -exec grep -H 'pattern' {} \;

I use this often:
grep 'pattern' /path/to/dir/**/*.xml
EDIT:
with zsh

Related

How to use grep to find in a directory by a regex?

I tried
grep -R '.*invalidTemplateName.*' -regex './online_admin/.*/UTF-8/.*'
to find all occurences of possible mathces of the '.invalidTemplateName.' regex within a directory regex pattern './online_admin/.*/UTF-8/.*', but it doesn't work. I got the message:
grep: ./online_admin/.*/UTF-8/.*: No such file or directory
If I use
grep -R '.*invalidTemplateName.*' .
it looks up in all subdirectory of the current directory that's overwhelming. How can I specify a directory pattern in grep? Is it possible?
Find might be a better choice here:
find ./online_admin/*/UTF-8/* -type f -exec grep -H "invalidTemplateName" {} \;
Find will locate all files in the locations you want, including subdirs of UTF-8 and then execute grep on each file. the -H argument ensures the filename will be printed along with the match. If you want only the filename, use the -L switch instead.
with find you could do something like that:
find /abs/path/to/directory -maxdepth 1 -name '.*invalidTemplateName.*'
using the name argument you can directly filter by names. you can also use wildcards for the filter-string.
using the maxdepth argument you can specify the level of recursion to look up the files. 1 means to look up in /abs/path/to/directory, 2 means to look up in /abs/path/to/directory and in the first level of directories in /abs/path/to/directory as well.

Replace text across multiple files in a directory with sed

I need to hide the IP addresses in the log files for security reasons. The IP addresses are of version 4 and 6. How do I hide the addresses in a way that, IPv4 example 123.4.32.16 is replaced by x.x.x.x and IPv6 example 232e:23o5:te43:5423:5433:0000:ef09:23ff is replaced by x:x:x:x:x:x:x:x? Is it possible to do this using a single sed command?
You might want to use find and sed for this.
Let's assume your logs have the extension ".log":
find /path/to/logs -type f -name '*.log' -exec \
sed -i -e 's,[0-9]\+\(\.[0-9]\+\)\{3\},x.x.x.x,g' \
-e 's,[0-9a-f]\+\(:[0-9a-f]\+\)\{7\},x:x:x:x:x:x:x:x,gi' {} \;
How does this work?
First, we ask find to recursively locate files with the .log extension starting from /path/to/logs. -type f tells find we wan't to find regular files.
For each file, it will execute sed. The -i argument tells sed you want to edit the file in place. (Check out http://www.grymoire.com/Unix/Sed.html)
One solution using find and perl:
find /the/directory -type f -exec perl -pi -e '
s/\b\d{1,3}(\.\d{1,3}){3}\b/x.x.x.x/g;
s/\b[a-f\d]{1,4}(:[a-f\d]{1,4}){7}\b/x:x:x:x:x:x:x:x/gi' {} \;
(type on one line)
Well, first you should probably just fix whatever is doing the logging to log the way you want to.
Now if you need to go back and modify historical files, you might consider using sed
sed -e 's/\b(\d{1,3}\.){3}\d{1,3}\b/x.x.x.x/' /path/to/file
sed -e 's/\b([:xdigit:]{4}:){7}[:xdigit:]{4}\b/x.x.x.x.x.x.x.x/' /path/to_file
I use this:
find . -name "*.log" -exec grep -izl PATTERN {} \; | xargs perl -i.orig -e -n 's/PATTERN/REPLACEMENT/g'
You'd want to insert your PATTERN(s) and replace *.log with something else depending on the name of your log files.
The -i.orig backs up the files being replaced with an extension of .orig.
I found that this was relatively faster than other things I tried. find/grep combo to indentify candidates, then perl to do the work.

Find a string in multiple files using grep

I have a folder with sub-folders inside, all have many types of files. I want to search for a word inside the .css-files. I am using Windows 7 and I have grep.
How I can use grep to :
Find pattern and print it
Give file name (and path) if pattern found
Actually you don't need find. Just use:
grep -R --include=*.css -H pattern .
this will recurse and look for all *.css in subdirectories, while -H will show the filename.
find folder/ -name "*.css" |xargs grep "your-pattern"
You will need to install cygwin to do this.
if the files in which we have to look for, has pattern then we can use this.
Consider I'm looking for pattern "cardlayout" in files named chap1.lst chap2.lst and so on.
then the command
grep -e 'cardlayout' ` find . -name "chap??.lst"`
hope this would help

Recursive find and replace based on regex

I have changed up my director structure and I want to do the following:
Do a recursive grep to find all instances of a match
Change to the updated location string
One example (out of hundreds) would be:
from common.utils import debug --> from etc.common.utils import debug
To get all the instances of what I'm looking for I'm doing:
$ grep -r 'common.' ./
However, I also need to make sure common is preceded by a space. How would I do this find and replace?
It's hard to tell exactly what you want because your refactoring example changes the import as well as the package, but the following will change common. -> etc.common. for all files in a directory:
sed -i 's/\bcommon\./etc.&/' $(egrep -lr '\bcommon\.' .)
This assumes you have gnu sed available, which most linux systems do. Also, just to let you know, this will fail if there are too many files for sed to handle at one time. In that case, you can do this:
egrep -lr '\bcommon\.' . | xargs sed -i 's/\bcommon\./etc.&/'
Note that it might be a good idea to run the sed command as sed -i'.OLD' 's/\bcommon\./etc.&/' so that you get a backup of the original file.
If your grep implementation supports Perl syntax (-P flag, on e.g. Linux it's usually available), you can benefit from the additional features like word boundaries:
$ grep -Pr '\bcommon\.'
By the way:
grep -r tends to be much slower than a previously piped find command as in Rob's example. Furthermore, when you're sure that the file-names found do not contain any whitespace, using xargs is much faster than -exec:
$ find . -type f -name '*.java' | xargs grep -P '\bcommon\.'
Or, applied to Tim's example:
$ find . -type f -name '*.java' | xargs sed -i.bak 's/\<common\./etc.common./'
Note that, in the latter example, the replacement is done after creating a *.bak backup for each file changed. This way you can review the command's results and then delete the backups:
$ find . -type f -name '*.bak' | xargs rm
If you've made an oopsie, the following command will restore the previous versions:
$ find . -type f -name '*.bak' | while read LINE; do mv -f $LINE `basename $LINE`; done
Of course, if you aren't sure that there's no whitespace in the file names and paths, you should apply the commands via find's -exec parameter.
Cheers!
This is roughly how you would do it using find. This requires testing
find . -name \*.java -exec sed "s/FIND_STR/REPLACE_STR/g" {}
This translates as "Starting from the current directory find all files that end in .java and execute sed on the file (where {} is a place holder for the currently found file) "s/FIND_STR/REPLACE_STR/g" replaces FIND_STR with REPLACE_STR in each line in the current file.

Shell script to recursively browse a directory and replace a string

I need to recursively search directories and replace a string (say http://development:port/URI) with another (say http://production:port/URI) in all the files where ever it's found. Can anyone help?
It would be much better if that script can print out the files that it modified and takes the search/replace patterns as input parameters.
Regards.
find . -type f | xargs sed -i s/pattern/replacement/g
Try this:
find . -type f | xargs grep -l development | xargs perl -i.bak -p -e 's(http://development)(http://production)g'
Another approach with slightly more feedback:
find . -type f | while read file
do
grep development $file && echo "modifying $file" && perl -i.bak -p -e 's(http://development)(http://prodution)g' $file
done
Hope this helps.
It sounds like you would benefit from a layer of indirection. (But then, who wouldn't?)
I'm thinking that you could have the special string in just one location. Either reference the configuration settings at runtime, or generate these files with the correct string at build time.
Don't try the above within a working SVN / CVS directory, since it will also patch the .svn/.cvs, which is definitely not what you want. To avoid .svn modifications, for example, use:
find . -type f | fgrep -v .svn | xargs sed -i 's/pattern/replacement/g'
Use zsh so with advanced globing you can use only one command.
E.g.:
sed -i 's:pattern:target:g' ./**
HTH