[context] My script needs to replace semvers of multiple .car names with commit sha. In short, I would like that every dev_CA_1.0.0.car became dev_CA_6a8zt5d832.car
ADDING commit sha right before .car was pretty trivial. With this, I end up with dev_CA_1.0.0_6a8zt5d832.car
find . -depth -name "*.car" -exec sh -c 'f="{}"; \
mv -- "$f" $(echo $f | sed -e 's/.car/_${CI_COMMIT_SHORT_SHA}.car/g')' \;
But I find it incredibly difficult to REPLACE. What aspect of sed am I misconceiving trying this:
find . -depth -name "*.car" -exec sh -c 'f="{}"; \
mv -- "$f" $(echo $f | sed -r -E 's/[0-9\.]+.car/${CI_COMMIT_SHORT_SHA}.car/g')
or this
find . -depth -name "*.car" -exec sh -c 'f="{}"; \
mv -- "$f" $(echo $f | sed -r -E 's/^(.*_)[0-9\.]+\.car/\1${CI_COMMIT_SHORT_SHA}\.car/g')' \;
no matches found: f="{}"; mv -- "$f" $(echo $f | sed -r -E ^(.*_)[0-9.]+.car/1684981321531.car/g)
or multiple variants:
\ escaping (e.g. \\.)
( and ) escaping (e.g. \() (I read somewhere that regex grouping with sed requires some care with ())
Is there a more direct way to do it?
Edit
$f getting in sed are path looking like
./somewhere/some_project_CA_1.2.3.car
./somewhere_else/other_project_CE_9.2.3.car
You may use
sed "s/_[0-9.]\{1,\}\.car$/_${CI_COMMIT_SHORT_SHA}.car/g"
See the online demo
Here, sed is used with a POSIX ERE expression, that matches
_ - an underscore
[0-9.]\{1,\} - 1 or more digits or dots
\.car - .car (note that a literal . must be escaped! a . pattern matches any char)
$ - end of string.
Can you try this :
export CI_COMMIT_SHORT_SHA=6a8zt5d832
find . -depth -name "*.car" -exec sh -c \
'f="{}"; echo mv "$f" "${f%_*}_${CI_COMMIT_SHORT_SHA}.car"' \;
Remove echo once you are satisfied of the result.
Related
I am using a find and sed command to replace characters in a file. see the code 1 below
find . -type f -exec sed -i '/Subject/{:a;s/(Subject.*)Subject/\1SecondSubject/;tb;N;ba;:b}' {} +
Given that I have multiple files I need to replace. In a given situation, the Subject I am trying to replace is not available.
Is there a way I can first check if the file contains the attribute 'Subject' if not I need to execute another command. i.e
Check if the file contains character 'Subject'
If true then execute code1 above
If there is no instance of Subject execute code 2 below
find . -name "*.html" -exec rename 's/.html$/.xml/' {} ;
Any Ideas? Thanks in advance
Something like this should work.
find . -type f \( \
-exec grep -q "Subject" {} \; \
-exec sed -i '/Subject/{:a;s/(Subject.*)Subject/\1SecondSubject/;tb;N;ba;:b}' {} \; \
-o \
-exec rename 's/.html$/.xml/' {} \; \)
-exec takes the exit code of the command it executes, so -exec grep -q "Subject" {} \; will only be true if the grep is true. And since the short circuit -o (or) has a lower precedence than the implied -a (and) between the other operators it should conversely only get executed if the grep is false.
You can use find in a process substitution like this:
while IFS= read -d'' -r file; do
echo "processing $file ..."
if grep -q "/Subject/" "$file"; then
sed -i '{:a;s/(Subject.*)Subject/\1SecondSubject/;tb;N;ba;:b}' "$file"
else if [[ $file == *.html ]]; then
rename 's/.html$/.xml/' "$file"
fi
done < <(find . -type f -print0)
I would like to rename all files named *-6.0.dll with *-6.1.dll
I tried:
find . -name '*-6.0.dll*' -exec mv {} $(echo {} | sed -e 's/-6.0.dll/-6.1.dll/g') \;
but this didn't work; the file names didn't change.
Any ideas?
for x in *-6.0.dll; do y=$(echo $x | sed -e 's/-6\.0\.dll$/-6.1.dll/'); echo mv $x $y; done
Remove the echo once you are satisfied the results are correct.
use this:
find . -name '*-6.0.dll*' -exec sh -c 'mv {} $(echo {} | sed -e 's/\-6\.0\.dll/\-6\.1\.dll/g')' \;
an explanation of using the sh -c vs mv can be found here http://linuxplayer.org/2010/05/shell-programming-trap-batch-rename-with-find
I also modified your regex, some of the characters need to be escaped for proper matching.
I'm trying to search and replace the following:
<?php
<!DOCTYPE HTML>
with
<!DOCTYPE HTML>
so far I have tried this:
find . \( -name "*.php" \) -exec grep -Hn "<?php <\!DOCTYPE HTML>" {} \; -exec sed -i 's/<?php <\!DOCTYPE HTML>/<\!DOCTYPE HTML>/g' {} \;
But it's not finding any instances of files with my needle string which exists on my server.
find . -name "*.php" -exec grep -lZz '^<?php[[:space:]]\+<!DOCTYPE HTML>' {} + |
xargs -r0 sed -i '^<?php[[:space:]]*$/,1d'
Edit: The previous version didn't work due to the character \n in the pattern. The updated version avoid this character.
With GNU awk (for RS='\0' to read the whole file as one record) and assuming your file names don't contain newlines all you need is the clear, simple:
find . -name '*.php' -print |
while IFS= read -r file; do
gawk -v RS='\0' '{gsub(/<\?php\n<!DOCTYPE HTML>/,"<!DOCTYPE HTML>"); print}' "$file" > tmp &&
mv tmp "$file"
done
when fixing mass spelling errors in my code base i have used this:
find . -path '*/.svn' -prune -o -name "*min.js" -prune -o -name "*min.css" -prune -o -name "flashLocaleXml.xml" -prune -o -type f -print0 | xargs -0 egrep -n "priority=" -exec sed -i 's/replace/newval/' {} \;
to fix a specific spelling error in all the files in my repo.
however, i am not very good with sed captures, i want to do something like:
X.addEventListener(LevelUpEvent.GENERIC_LEVEL_UP, updateLevels);
becomes:
EventUtil.addEventListener(X, LevelUpEvent.GENERIC_LEVEL_UP, updateLevels);
I have read up extensively but I would appreciate someone explaining how sed captures work with this as a specific example.
I have given it a few shots, but nothing I come up with works: here are my tries
echo "X.addEventListener(LevelUpEvent.GENERIC_LEVEL_UP, updateLevels);" | sed 's/\(.*\)EventUtil\(.*EventUtil\)/\1X\2/'
echo "X.addEventListener(LevelUpEvent.GENERIC_LEVEL_UP, updateLevels);" | sed -r 's/(....),(....),(*\.addEventListener)(LevelUpEvent.*)/\1,\2\n\1,\2,/g'
echo "X.addEventListener(LevelUpEvent.GENERIC_LEVEL_UP, updateLevels);" | sed 's/\([\.$]*\) \([\.$]*\)/\2 \1/'
thank you in advance!
Try with:
sed 's/\([^.]*\)\([^(]*(\)/EventUtil\2\1, /'
Output:
EventUtil.addEventListener(X, LevelUpEvent.GENERIC_LEVEL_UP, updateLevels);
Explanation:
\([^.]*\) # Content until first '.'
\([^(]*(\) # Content until first '('
EventUtil\2\1, # Literal 'EventUtil' plus grouped content in previous expression.
This sed command will do.
sed 's/\(X\).\(addEventListener\)(\(LevelUpEvent.GENERIC_LEVEL_UP, updateLevels\));/EventUtil.\2(\1, \3);/'
Example
$ echo "X.addEventListener(LevelUpEvent.GENERIC_LEVEL_UP, updateLevels);" | sed 's/\(X\).\(addEventListener\)(\(LevelUpEvent.GENERIC_LEVEL_UP, updateLevels\));/EventUtil.\2(\1, \3);/'
EventUtil.addEventListener(X, LevelUpEvent.GENERIC_LEVEL_UP, updateLevels);
Try this
echo "X.addEventListener(LevelUpEvent.GENERIC_LEVEL_UP, updateLevels);" | sed -e "s/\([A-Za-z]\{1,\}\)\.addEventListener(/EventUtil.addEventListener(\1, /"
This regexp will recognize a variable name using
\([A-Za-z]\{1,\}\)
Then .addEventListener(
\.addEventListener(
And replace it with
EventUtil.addEventListener(\1
In which \1 represents the variable name
This is a part of my shell script, which I use to perform a recursive find and replace in the working directory. Backup and other utilities are in other functions, which are irrelevant to my problem.
#!/bin/bash
# backup function goes here
# #param $1 The find pattern.
# #param $2 The replace pattern.
function findAndReplace {
bufferFile=/tmp/tmp.$$
filesToReplace=`find . -type f | grep -vi cvs | grep -v '#'`
sedPattern="s/$1/$2/g"
echo "Using pattern $sedPattern"
for f in $filesToReplace; do
echo "sedding file $f"
sed "$sedPattern" "$f" > "$bufferFile"
exitCode=$?
if [ $exitCode -ne 0 ] ; then
echo "sed $sedPattern exited with $exitCode"
exit 1
fi
chown --reference=$f $bufferFile
mv $bufferFile $f
done
}
backup
findAndReplace "$1" "$2"
Here's a sample usage: recursive-replace.sh "function _report" "function report".
It works, but there is one problem. It uses sed on ALL files in the working directory. I would like to sed only those files, which contain the find pattern.
Then, I modified the line:
filesToReplace=`find . -type f | grep -vi cvs | grep -v '#'`
to:
filesToReplace=`grep -rl "$1" . | grep -vi cvs | grep -v '#'`
And it works too, but not for all find patterns. E.g. for pattern \$this->report\((.*)\) I recieve error: grep: Unmatched ( or \(. This pattern is correct for sed, but not for grep.
Regex syntaxes for grep and sed differ. What can I do?
use grep -E ("extended" regexp option) — it usually solves the problem.
(also sometimes available as egrep)
Also, why not keep using find?
filesToReplace=`find . -name CVS -prune -o -type f -exec grep -l "$1" {} \; | grep -v '#'`
Also note the -i option of sed, which allows in-place changes in files and the removal of the bufferFile/chown/mv logic.
Why not compare source and buffer files before overwriting the source file:
#!/bin/bash
# backup function goes here
# #param $1 The find pattern.
# #param $2 The replace pattern.
function findAndReplace {
bufferFile=/tmp/tmp.$$
filesToReplace=`find . -type f | grep -vi cvs | grep -v '#'`
sedPattern="s/$1/$2/g"
echo "Using pattern $sedPattern"
for f in $filesToReplace; do
echo "sedding file $f"
sed "$sedPattern" "$f" > "$bufferFile"
exitCode=$?
if [ $exitCode -ne 0 ] ; then
echo "sed $sedPattern exited with $exitCode"
exit 1
fi
cmp -s $f $bufferFile
if [ $? -ne 0 ]; then
chown --reference=$f $bufferFile
mv $bufferFile $f
fi
done
}
backup
findAndReplace "$1" "$2"