Search and replace entire files - linux

I've seen numerous examples for replacing one string with another among multiple files but what I want to do is a bit different. Probably a lot simpler :)
Find all the files that match a certain string and replace them completely with the contents of a new file.
I have a find command that works
find /home/*/public_html -name "index.php" -exec grep "version:1.23" '{}' \; -print
This finds all the files I need to update.
Now how do I replace their entire content with the CONTENTS of /home/indexnew.txt (I could also name it /home/index.php)
I emphasize content because I don't want to change the name or ownership of the files I'm updating.

find ... | while read filename; do cat static_file > "$filename"; done
efficiency hint: use grep -q -- it will return "true" immediately when the first match is found, not having to read the entire file.

If you have a bunch of files you want to replace, and you can get all of their names using wildcards you can try piping output to the tee command:
cat my_file | tee /home/*/update.txt
This should look through all the directories in /home and write the text in my_file to update.txt in each of those directories.
Let me know if this helps or isn't what you want.

I am not sure if your command without -l and then print it is better than to add -l in grep to list file directly.
find /home/*/public_html -name "index.php" -exec grep -l "version:1.23" '{}' \; |xargs -i cp /home/index.php {}
Here is the option -l detail
-l, --files-with-matches
Suppress normal output; instead print the name of each input
file from which output would normally have been printed. The
scanning will stop on the first match. (-l is specified by
POSIX.)

Related

"find" specific contents [linux]

I would like to go through all the files in the current directory (or sub-directories) and echoes me back the name of files only if they contain certain words.
More detail:
find -type f -name "*hello *" will give me all file names that have "hello" in their names. But instead of that, I want to search through the files and if that file's content contains "hello" then prints out the name of the file.
Is there a way to approach this?
You can use GNU find and GNU grep as
find /path -type f -exec grep -Hi 'hello' {} +
This is efficient in a way that it doesn't invoke as many grep instances to as many files returned from find. This works in an underlying assumption that find returns a set of files for grep to search on. If you are unsure if the files may not be available, as a fool-proof way, you can use xargs with -r flag, in which case the commands following xargs are executed only if the piped commands return any results
find /path -type f | xargs -r0 grep -Hi 'hello'

How to know which file holds grep result?

There is a directory which contains 100 text files. I used grep to search a given text in the directory as follow:
cat *.txt | grep Ya_Mahdi
and grep shows Ya_Mahdi.
I need to know which file holds the text. Is it possible?
Just get rid of cat and provide the list of files to grep:
grep Ya_Mahdi *.txt
While this would generally work, depending on the number of .txt files in that folder, the argument list for grep might get too large.
You can use find for a bullet proof solution:
find --maxdepth 1 -name '*.txt' -exec grep -H Ya_Mahdi {} +

How to perform grep operation on all files in a directory?

Working with xenserver, and I want to perform a command on each file that is in a directory, grepping some stuff out of the output of the command and appending it in a file.
I'm clear on the command I want to use and how to grep out string(s) as needed.
But what I'm not clear on is how do I have it perform this command on each file, going to the next, until no more files are found.
In Linux, I normally use this command to recursively grep for a particular text within a directory:
grep -rni "string" *
where
r = recursive i.e, search subdirectories within the current directory
n = to print the line numbers to stdout
i = case insensitive search
grep $PATTERN * would be sufficient. By default, grep would skip all subdirectories. However, if you want to grep through them, grep -r $PATTERN * is the case.
Use find. Seriously, it is the best way because then you can really see what files it's operating on:
find . -name "*.sql" -exec grep -H "slow" {} \;
Note, the -H is mac-specific, it shows the filename in the results.
To search in all sub-directories, but only in specific file types, use grep with --include.
For example, searching recursively in current directory, for text in *.yml and *.yaml :
grep "text to search" -r . --include=*.{yml,yaml}
If you want to do multiple commands, you could use:
for I in `ls *.sql`
do
grep "foo" $I >> foo.log
grep "bar" $I >> bar.log
done

Shell: find files in a list under a directory

I have a list containing about 1000 file names to search under a directory and its subdirectories. There are hundreds of subdirs with more than 1,000,000 files. The following command will run find for 1000 times:
cat filelist.txt | while read f; do find /dir -name $f; done
Is there a much faster way to do it?
If filelist.txt has a single filename per line:
find /dir | grep -f <(sed 's#^#/#; s/$/$/; s/\([\.[\*]\|\]\)/\\\1/g' filelist.txt)
(The -f option means that grep searches for all the patterns in the given file.)
Explanation of <(sed 's#^#/#; s/$/$/; s/\([\.[\*]\|\]\)/\\\1/g' filelist.txt):
The <( ... ) is called a process subsitution, and is a little similar to $( ... ). The situation is equivalent to (but using the process substitution is neater and possibly a little faster):
sed 's#^#/#; s/$/$/; s/\([\.[\*]\|\]\)/\\\1/g' filelist.txt > processed_filelist.txt
find /dir | grep -f processed_filelist.txt
The call to sed runs the commands s#^#/#, s/$/$/ and s/\([\.[\*]\|\]\)/\\\1/g on each line of filelist.txt and prints them out. These commands convert the filenames into a format that will work better with grep.
s#^#/# means put a / at the before each filename. (The ^ means "start of line" in a regex)
s/$/$/ means put a $ at the end of each filename. (The first $ means "end of line", the second is just a literal $ which is then interpreted by grep to mean "end of line").
The combination of these two rules means that grep will only look for matches like .../<filename>, so that a.txt doesn't match ./a.txt.backup or ./abba.txt.
s/\([\.[\*]\|\]\)/\\\1/g puts a \ before each occurrence of . [ ] or *. Grep uses regexes and those characters are considered special, but we want them to be plain so we need to escape them (if we didn't escape them, then a file name like a.txt would match files like abtxt).
As an example:
$ cat filelist.txt
file1.txt
file2.txt
blah[2012].txt
blah[2011].txt
lastfile
$ sed 's#^#/#; s/$/$/; s/\([\.[\*]\|\]\)/\\\1/g' filelist.txt
/file1\.txt$
/file2\.txt$
/blah\[2012\]\.txt$
/blah\[2011\]\.txt$
/lastfile$
Grep then uses each line of that output as a pattern when it is searching the output of find.
If filelist.txt is a plain list:
$ find /dir | grep -F -f filelist.txt
If filelist.txt is a pattern list:
$ find /dir | grep -f filelist.txt
Use xargs(1) for the while loop can be a bit faster than in bash.
Like this
xargs -a filelist.txt -I filename find /dir -name filename
Be careful if the file names in filelist.txt contains whitespaces, read the second paragraph in the DESCRIPTION section of xargs(1) manpage about this problem.
An improvement based on some assumptions. For example, a.txt is in filelist.txt, and you can make sure there is only one a.txt in /dir. Then you can tell find(1) to exit early when it finds the instance.
xargs -a filelist.txt -I filename find /dir -name filename -print -quit
Another solution. You can pre-process the filelist.txt, make it into a find(1) arguments list like this. This will reduce find(1) invocations:
find /dir -name 'a.txt' -or -name 'b.txt' -or -name 'c.txt'
I'm not entirely sure of the question here, but I came to this page after trying to find a way to discover which 4 of 13000 files had failed to copy.
Neither of the answers did it for me so I did this:
cp file-list file-list2
find dir/ >> file-list2
sort file-list2 | uniq -u
Which resulted with a list of the 4 files I needed.
The idea is to combine the two file lists to determine the unique entries.
sort is used to make duplicate entries adjacent to each other which is the only way uniq will filter them out.

How can I use grep to find a word inside a folder?

In Windows, I would have done a search for finding a word inside a folder. Similarly, I want to know if a specific word occurs inside a directory containing many sub-directories and files. My searches for grep syntax shows I must specify the filename, i.e. grep string filename.
Now, I do not know the filename, so what do I do?
A friend suggested to do grep -nr string, but I don't know what this means and I got no results with it (there is no response until I issue a Ctrl + C).
grep -nr 'yourString*' .
The dot at the end searches the current directory. Meaning for each parameter:
-n Show relative line number in the file
'yourString*' String for search, followed by a wildcard character
-r Recursively search subdirectories listed
. Directory for search (current directory)
grep -nr 'MobileAppSer*' . (Would find MobileAppServlet.java or MobileAppServlet.class or MobileAppServlet.txt; 'MobileAppASer*.*' is another way to do the same thing.)
To check more parameters use man grep command.
grep -nr string my_directory
Additional notes: this satisfies the syntax grep [options] string filename because in Unix-like systems, a directory is a kind of file (there is a term "regular file" to specifically refer to entities that are called just "files" in Windows).
grep -nr string reads the content to search from the standard input, that is why it just waits there for input from you, and stops doing so when you press ^C (it would stop on ^D as well, which is the key combination for end-of-file).
GREP: Global Regular Expression Print/Parser/Processor/Program.
You can use this to search the current directory.
You can specify -R for "recursive", which means the program searches in all subfolders, and their subfolders, and their subfolder's subfolders, etc.
grep -R "your word" .
-n will print the line number, where it matched in the file.
-i will search case-insensitive (capital/non-capital letters).
grep -inR "your regex pattern" .
There's also:
find directory_name -type f -print0 | xargs -0 grep -li word
but that might be a bit much for a beginner.
find is a general purpose directory walker/lister, -type f means "look for plain files rather than directories and named pipes and what have you", -print0 means "print them on the standard output using null characters as delimiters". The output from find is sent to xargs -0 and that grabs its standard input in chunks (to avoid command line length limitations) using null characters as a record separator (rather than the standard newline) and then applies grep -li word to each set of files. On the grep, -l means "list the files that match" and -i means "case insensitive"; you can usually combine single character options so you'll see -li more often than -l -i.
If you don't use -print0 and -0 then you'll run into problems with file names that contain spaces so using them is a good habit.
grep -nr search_string search_dir
will do a RECURSIVE (meaning the directory and all it's sub-directories) search for the search_string. (as correctly answered by usta).
The reason you were not getting any anwers with your friend's suggestion of:
grep -nr string
is because no directory was specified. If you are in the directory that you want to do the search in, you have to do the following:
grep -nr string .
It is important to include the '.' character, as this tells grep to search THIS directory.
Why not do a recursive search to find all instances in sub directories:
grep -r 'text' *
This works like a charm.
Similar to the answer posted by #eLRuLL, an easier way to specify a search that respects word boundaries is to use the -w option:
grep -wnr "yourString" .
Another option that I like to use:
find folder_name -type f -exec grep your_text {} \;
-type f returns you only files and not folders
-exec and {} runs the grep on the files that were found in the search (the exact syntax is "-exec command {}").
grep -r "yourstring" *
Will find "yourstring" in any files and folders
Now if you want to look for two different strings at the same time you can always use option E and add words for the search. example after the break
grep -rE "yourstring|yourotherstring|$" * will search for list locations where yourstring or yourotherstring matches
The answer you selected is fine, and it works, but it isn't the correct way to do it, because:
grep -nr yourString* .
This actually searches the string "yourStrin" and "g" 0 or many times.
So the proper way to do it is:
grep -nr \w*yourString\w* .
This command searches the string with any character before and after on the current folder.
grep -R "string" /directory/
-R follows also symlinks when -r does not.
The following sample looks recursively for your search string in the *.xml and *.js files located somewhere inside the folders path1, path2 and path3.
grep -r --include=*.xml --include=*.js "your search string" path1 path2 path3
So you can search in a subset of the files for many directories, just providing the paths at the end.
Run(terminal) the following command inside the directory. It will recursively check inside subdirectories too.
grep -r 'your string goes here' *
Don't use grep. Download Silver Searcher or ripgrep. They're both outstanding, and way faster than grep or ack with tons of options.

Resources