Restore multiple backup files created with find followed by sed - sed

I need to edit files with specific pattern in their names. These files are spread across multiple hierarchical directories.
I am using find followed by sed with the help of xargs to achieve the same as follows:
find . -type f -name "file*" | sed -i.bak 's/word1/replace_word1/g'
My problem is that now I want to revert back the changes from backup files (*.bak). Sample directory tree will look like as shown below. I am looking for linux command (may be a chain) to achieve the same.
$ tree
.
|-- pdir1
| |-- dir1
| | |-- dir2
| | | |-- file2
| | | `-- file2.bak
| | |-- file1
| | `-- file1.bak
| `-- dir3
| |-- file3
| `-- file3.bak
`-- pdir2
|-- file1
`-- file1.bak
I can find all backup files using following find command. But can't figure out what to do next. It would be appreciable if your could help me solve this.
find . -type f -name "file*.bak"
Note
I observe this problem as general scenario. I had faced this many times but got away with writing a small wrapper script for it. Here I am hoping for a generic and concise solution.

One option could be use rename with find:
find . -type f -name "*.bak" -exec rename 's/\.bak$//' {} \;
This would not rename foo.bak if foo exists. In order to force overwriting, use the -f option for rename:
find . -type f -name "*.bak" -exec rename -f 's/\.bak$//' {} \;
EDIT: If you can't use rename, try:
find . -name "*.bak" -exec sh -c 'mv -f $0 ${0%.bak}' {} \;
Explanation of the above (last) command: For each result returned by find, a shell command is invoked that takes the result as a parameter (denoted by {}) for mv -f $0 ${0%.bak}. $0 is a positional parameter and ${0%.bak} denotes shell parameter expansion for removing .bak from the end of the parameter. In effect, for a result foo.bak, the shell would issue a command mv -f foo.bak foo.

You can restore simply with a for loop that traverse directories. It doesn't check if the file without the bak extension exists.
for f in **/*.bak; do mv -- "$f" "${f%.bak}"; done

Related

Find files and delete characters in the filenames

So I have a directory called testdir and I want to delete the underscores in the filenames from the files in that directory.
I tried to use this command
find testdir -type f -exec ls {} \; | sed 's/_*//'
It will output the filenames without the underscores but it won't delete the underscores permanently. Could anyone help me?
Thanks!
If you are just looping through the files in your dir, use a simple loop:
while IFS= read -r file
do
echo mv "$file" "${file//_/}" #once you are sure it works, remove the echo!
done < <(find -type f -name "*_*")
This will feed the while loop with the output of the find command. Then, uses ${var//_/} to remove all _ in the name.
Why wasn't your approach working?
Because you are saying
find ... -exec ls {} \; | sed '...'
That is, you are finding something and then changing the output with sed. That is, nothing is done to the file itself.
This may help you
find testdir -type f | rename 's/_//'
Regards

Solaris: Find files matching a pattern but only display the directory name

Like it says above. I'm trying to find a simple way to look for a pattern in a file name and display only the directory in which it is found.
For example, given a tree structure that looks like this:
./projecta
./projecta/src/code1.p
./projecta/src/code2.p
./projecta/util.p
./projectb
I would want the command "whatever *.p" to return:
./projecta/src
./projecta
Hope that makes sense. Any further info, please signify in the usual manner.
TIA
N/
To display the directories of .c files:
find . -name \*.c -exec dirname {} \; | uniq
To display the directories of .html files:
find . -name \*.html -exec dirname {} \; | uniq
If you want to use that in a script,
#/bin/bash
ext=$1
find . -name \*.${ext} -exec dirname {} \; | uniq

Linux: Using find and grep to find a keyword in files and count occurrences

I'm using executing this bash commands inside a search script I've built with php:
find myFolder -type f -exec grep -r KEYWORD {} +
find myFolder -type f -exec grep -r KEYWORD {} + | wc -l
find myFolder -type f | wc -l
The first line gives me back the filenames where KEYWORD was found.
The second line gives me the number of occurrences and the third line the total number of files.
Is there a way to do this more elegantly and faster?
You can get more efficiency if you avoid -exec, which makes one fork per file match. xargs is a better choice here. So I would do something like this:
find myFolder -type f -print0 | xargs -0 grep KEYWORD
find myFolder -type f -print0 | xargs -0 grep KEYWORD | wc -l
The last one should be OK, at least with GNU find.
The -print0 and -0 ensure that filenames with spaces in them are handled correctly.
Note that grep -r` implies recursive grepping, but as you're only supplying one filename in each invocation it is redundant.

Using sed to grab filename from full path?

I'm new to sed, and need to grab just the filename from the output of find. I need to have find output the whole path for another part of my script, but I want to just print the filename without the path. I also need to match starting from the beginning of the line, not from the end. In english, I want to match, the first group of characters ending with ".txt" not containing a "/". Here's my attempt that doesn't work:
ryan#fizz:~$ find /home/ryan/Desktop/test/ -type f -name \*.txt
/home/ryan/Desktop/test/two.txt
/home/ryan/Desktop/test/one.txt
ryan#fizz:~$ find /home/ryan/Desktop/test/ -type f -name \*.txt | sed s:^.*/[^*.txt]::g
esktop/test/two.txt
ne.txt
Here's the output I want:
two.txt
one.txt
Ok, so the solutions offered answered my original question, but I guess I asked it wrong. I don't want to kill the rest of the line past the file suffix i'm searching for.
So, to be more clear, if the following:
bash$ new_mp3s=\`find mp3Dir -type f -name \*.mp3\` && cp -rfv $new_mp3s dest
`/mp3Dir/one.mp3' -> `/dest/one.mp3'
`/mp3Dir/two.mp3' -> `/dest/two.mp3'
What I want is:
bash$ new_mp3s=\`find mp3Dir -type f -name \*.mp3\` && cp -rfv $new_mp3s dest | sed ???
`one.mp3' -> `/dest'
`two.mp3' -> `/dest'
Sorry for the confusion. My original question just covered the first part of what I'm trying to do.
2nd edit:
here's what I've come up with:
DEST=/tmp && cp -rfv `find /mp3Dir -type f -name \*.mp3` $DEST | sed -e 's:[^\`].*/::' -e "s:$: -> $DEST:"
This isn't quite what I want though. Instead of setting the destination directory as a shell variable, I would like to change the first sed operation so it only changes the cp output before the "->" on each line, so that I still have the 2nd part of the cp output to operate on with another '-e'.
3rd edit:
I haven't figured this out using only sed regex's yet, but the following does the job using Perl:
cp -rfv `find /mp3Dir -type f -name \*.mp3` /tmp | perl -pe "s:.*/(.*.mp3).*\`(.*/).*.mp3\'$:\$1 -> \$2:"
I'd like to do it in sed though.
Something like this should do the trick:
find yourdir -type f -name \*.txt | sed 's/.*\///'
or, slightly clearer,
find yourdir -type f -name \*.txt | sed 's:.*/::'
Why don't you use basename instead?
find /mydir | xargs -I{} basename {}
No need external tools if using GNU find
find /path -name "*.txt" -printf "%f\n"
I landed on the question based on the title: using sed to grab filename from fullpath.
So, using sed, the following is what worked for me...
FILENAME=$(echo $FULLPATH | sed -n 's/^\(.*\/\)*\(.*\)/\2/p')
The first group captures any directories from the path. This is discarded.
The second group capture is the text following the last slash (/). This is returned.
Examples:
echo "/test/file.txt" | sed -n 's/^\(.*\/\)*\(.*\)/\2/p'
file.txt
echo "/test/asd/asd/entrypoint.sh" | sed -n 's/^\(.*\/\)*\(.*\)/\2/p'
entrypoint.sh
echo "/test/asd/asd/default.json" | sed -n 's/^\(.*\/\)*\(.*\)/\2/p'
default.json
find /mydir | awk -F'/' '{print $NF}'
path="parentdir2/parentdir1/parentdir0/dir/FileName"
name=${path##/*}

using find command to search for all files having some text pattern

I use following find command to find and show all files having the input text pattern.
find . -type f -print|xargs grep -n "pattern"
I have many project folders each of which has its own makefile named as 'Makefile'.(no file extension, just 'Makefile')
How do i use above command to search for a certain pattern only in the files named Makefile which are present in all my project folders?
-AD.
-print is not required (at least by GNU find implementation). -name argument allows to specify filename pattern. Hence the command would be:
find . -name Makefile | xargs grep pattern
If you have spaces or odd characters in your directory paths youll need to use the null-terminated method:
find . -name Makefile -print0 | xargs -0 grep pattern
find . -type f -name 'Makefile' | xargs egrep -n "pattern"
use egrep if you have very long paths
Duplicate of : this
You can avoid the use of xargs by using -exec:
find . -type f -name 'Makefile' -exec egrep -Hn "pattern" {} \;
-H on egrep to output the full path to the matching files.
grep -R "string" /path
Please find this link
http://rulariteducation.blogspot.in/2016/03/how-to-check-particluar-string-in-linux.html
you can use ff command i.e ff -p .format. For eg ff -p *.txt
Find big files occupying large disk space
we need to combine multiple command .
find . -type f | xargs du -sk | sort -n | tail;