I need regexp which would remove tinrab in all directories
grep -rl "tinrab" /home/miki/go/src/github.com/meower
/home/miki/go/src/github.com/meower/pusher-service/main.go
/home/miki/go/src/github.com/meower/Dockerfile
/home/miki/go/src/github.com/meower/event/nats.go
/home/miki/go/src/github.com/meower/event/event.go
/home/miki/go/src/github.com/meower/meow-service/handlers.go
/home/miki/go/src/github.com/meower/meow-service/main.go
/home/miki/go/src/github.com/meower/query-service/handlers.go
/home/miki/go/src/github.com/meower/query-service/main.go
/home/miki/go/src/github.com/meower/search/elastic.go
/home/miki/go/src/github.com/meower/search/repository.go
/home/miki/go/src/github.com/meower/Gopkg.lock
/home/miki/go/src/github.com/meower/Gopkg.toml
/home/miki/go/src/github.com/meower/db/repository.go
/home/miki/go/src/github.com/meower/db/postgres.go
/home/miki/go/src/github.com/meower/.git/logs/HEAD
/home/miki/go/src/github.com/meower/.git/logs/refs/heads/master
/home/miki/go/src/github.com/meower/.git/logs/refs/remotes/origin/HEAD
/home/miki/go/src/github.com/meower/.git/config
For example in Dockerfile I have this line
WORKDIR /go/src/github.com/tinrab/meower
My goal is to have line without tinrab
WORKDIR /go/src/github.com/meower
This regexp
replaceAll("/tinrab*/ *", " ")
should somehow be implemented.
Any ideas?
If I try
grep -rl "tinrab" /home/miki/go/src/github.com/meower | xargs replaceAll("\/tinrab\/", "")
I got
bash: syntax error near unexpected token `('
You said you wanted a regexp /tinrab*/ *, but based on the examples you gave I guess /tinrab will do what you want..
grep -rl "tinrab" /home/miki/go/src/github.com/meower | \
xargs -r sed -i 's!/tinrab!!g'
The general format of the sed expression I used is: s!<from>!<to>!g which works the same as the replaceAll("<from>", "<to>") pseudocode you considered.
Related
I'm having trouble in getting sed to remove just the specific line I want. Let's say I have a file that looks like this:
testfile
testfile.txt
testfile2
Currently I'm using this to remove the line I want:
sed -i "/$1/d" file
The issue is that with this if I were to give testfile as input it would delete all three lines but I want it to only remove the first line. How do I do this?
With grep
grep -x -F -v -- "$1" file
# or
grep -xFv -- "$1" file
-F is for "fixed strings" -- turns off regex engine.
-x is to match entire line.
-v is for "everything but" the matched line(s).
-- to signal the end of options, in case $1 starts with a hyphen.
To save the file
grep -xFv -- "$1" file | sponge file # `moreutils` package
# or
tmp=$(mktemp)
grep -xFv -- "$1" file > "$tmp" && mv "$tmp" file
So match the whole line.
var=testfile
sed -i '/^'"$var"'$/d' file
# or with " quoting
sed -i "/^$var\$/d" file
You can learn regex with fun online with regex crosswords.
I am trying to use sed to replace the following but not working
replace datetime.now(pytz.utc) with datetime.utcnow() recursively
i have tried the following
grep -rl "datetime.now(pytz.utc)" . | xargs sed -i 's/datetime.now\(pytz.utc\)/datetime.utcnow\(\)/g'
mac command equivalent
LC_ALL=C
grep -e "datetime.now(pytz.utc)" -rl . | xargs sed -i '' 's/datetime.now\(pytz.utc\)/datetime.utcnow\(\)/g'
as you can see i tried to escape all the parentheses but does not work
anyone know how to properly use sed to replace datetime.now(pytz.utc) with datetime.utcnow()?
I tried to explain in the comments, but obviously I wasn't clear. Here are two potential solutions to your problem:
Using your 'grep/xargs' method:
grep -rl "datetime.now(pytz.utc)" . | xargs sed -i 's/datetime.now(pytz.utc)/datetime.utcnow()/g'
Using the 'find/exec' method:
find . -type f -exec sed -i 's/datetime.now(pytz.utc)/datetime.utcnow()/g' {} \;
Both options will replace "datetime.now(pytz.utc)" with "datetime.utcnow()" in the files found. Both answers are platform independent provided you have GNU sed, not BSD sed.
I want to extract /battle/result from following the txt file
$ cat sample
user_id=1234 /battle/start
I run following the sed command
$ cat sample | sed 's|.*\(/.*\)|\1|g'
/start
But, result is deleting /battle, so I can't extract it as I want.
What is wrong with it?
You can remove all characters up to last space:
$ sed 's/.* //' <<< "user_id=1234 /battle/start"
/battle/start
or use cut:
$ cut -d' ' -f2 <<< "user_id=1234 /battle/start"
/battle/start
Sed tries to do a greedy (maximal) match, therefore .* matches your whole line up to but not including the second /.
Try:
< sample sed 's|.* \(/.*\)|\1|g'
or
< sample sed 's|[^/]*\(/.*\)|\1|g'
In your RE the .* is greedy and swallows the /battle part, you could try to invert the logic and delete everything in front of /:
cat sample | sed 's/[^/]*//'
Here [^/]* matches everthing that is not a / and replaces it with nothing.
echo user_id=1234 /battle/start |grep -oP '\s\K.*'
/battle/start
echo user_id=1234 /battle/start |sed -r 's/(^.*\s)(.*)/\2/g'
/battle/start
I am in directory with files consisting of many lines of lines like this:
98.684807 :(float)
52.244898 :(float)
46.439909 :(float)
and then a line that terminates:
[chuck]: cleaning up...
I am trying to eliminate :(float) from every file (but leave the number) and also remove that cleaning up... line.
I can get:
sed -ie 's/ :(float)//g' *
to work, but that creates files that keeps the old files. Removing the -e flag results in an unterminated substitute pattern error.
Same deal with:
sed -ie 's/[chuck]: cleaning up...//g' *
Thoughts?
sed -i '' -e 's/:(float)//' -e '/^.chuck/d' *
This way you are telling sed not to save a copy (null length backup extention to -i) and separately specifying the sed commands.
sed -ie expression [files...]
is equivalent to:
sed -ie -e expression [files...]
and both mean apply expression to files, overwriting the files, but saving the old files with an "e" as the backup suffix.
I think you want:
sed -i -e expression [files...]
Now if you're getting an error from that there must be something wrong with your expression.
your numbers are separated with (float) by the : character. Therefore, you can use awk/cut to get your numbers. Its simpler than a regex
$ head -n -1 file | awk -F":" '{print $1}'
98.684807
52.244898
46.439909
$ head -n -1 file | cut -d":" -f1
98.684807
52.244898
46.439909
Solution :
sed -i '' 's/ :(float)//g' *
sed -i '' 's/[chuck]: cleaning up...//g' *
Explanation :
I can get:
sed -ie 's/ :(float)//g' *
to work, but that creates files that keeps the old files.
That's because sed's i flag is supposed to work that way
-i extension
Edit files in-place, saving backups with the specified extension. If a zero-length extension is given, no backup will be saved.
In this case e is being interpreted as the extension you want to save your backups with. So all your original files will be backed up with an e appended to their names.
In order to provide a zero-length extension, you need to use -i ''.
Note: Unlike -i<your extension>, -i'' won't work. You need to have a space character between -i and '' in order for it to work.
Removing the -e flag results in an unterminated substitute pattern error.
When you remove the e immediately following -i, i.e.
sed -i 's/ :(float)//g' *
s/ :(float)//g will now be interpreted as the extension argument to i flag. And the first file in the list of files produced by shell expansion of * is interpreted as a sed function (most probably s/regular expression/replacement/flags function) You can verify this by checking the output of
sedfn=$(echo * | cut -d' ' -f1); [[ ${sedfn:0:1} == "s" ]]; echo $?
If the output of the above chain of commands is 0, our assumption is validated.
Also in this case, if somehow the first filename qualifies as a valid s/regular expression/replacement/flags sed function, the other filenames will be interpreted as regular files for sed to operate on.
sed -i -e 's/ :(float)//g' *
Check to see if you have any odd filenames in the directory.
Here is one way to duplicate your error:
$ touch -- "-e s:x:"
$ ls
-e s:x:
$ sed -i "s/ :(float)//g' *
sed: -e expression #1, char 5: unterminated `s' command
One way to protect against this is to use a double dash to terminate the options to sed when you use a wild card:
$ sed -i "s/ :(float)//g' -- *
You can do the same thing to remove the file:
$ rm "-e s:x:"
rm: invalid option -- 'e'
$ rm -- "-e s:x:"
This question already has answers here:
How can I do a recursive find/replace of a string with awk or sed?
(37 answers)
Closed 1 year ago.
Using just grep and sed, how do I replace all occurrences of:
a.example.com
with
b.example.com
within a text file under the /home/user/ directory tree recursively finding and replacing all occurrences in all files in sub-directories as well.
Try this:
find /home/user/ -type f | xargs sed -i 's/a\.example\.com/b.example.com/g'
In case you want to ignore dot directories
find . \( ! -regex '.*/\..*' \) -type f | xargs sed -i 's/a\.example\.com/b.example.com/g'
Edit: escaped dots in search expression
Try this:
grep -rl 'SearchString' ./ | xargs sed -i 's/REPLACESTRING/WITHTHIS/g'
grep -rl will recursively search for the SEARCHSTRING in the directories ./ and will replace the strings using sed.
Ex:
Replacing a name TOM with JERRY using search string as SWATKATS in directory CARTOONNETWORK
grep -rl 'SWATKATS' CARTOONNETWORK/ | xargs sed -i 's/TOM/JERRY/g'
This will replace TOM with JERRY in all the files and subdirectories under CARTOONNETWORK wherever it finds the string SWATKATS.
On macOS, none of the answers worked for me. I discovered that was due to differences in how sed works on macOS and other BSD systems compared to GNU.
In particular BSD sed takes the -i option but requires a suffix for the backup (but an empty suffix is permitted)
grep version from this answer.
grep -rl 'foo' ./ | LC_ALL=C xargs sed -i '' 's/foo/bar/g'
find version from this answer.
find . \( ! -regex '.*/\..*' \) -type f | LC_ALL=C xargs sed -i '' 's/foo/bar/g'
Don't omit the Regex to ignore . folders if you're in a Git repo. I realized that the hard way!
That LC_ALL=C option is to avoid getting sed: RE error: illegal byte sequence if sed finds a byte sequence that is not a valid UTF-8 character. That's another difference between BSD and GNU. Depending on the kind of files you are dealing with, you may not need it.
For some reason that is not clear to me, the grep version found more occurrences than the find one, which is why I recommend to use grep.
I know this is a really old question, but...
#vehomzzz's answer uses find and xargs when the questions says explicitly grep and sed only.
#EmployedRussian and #BrooksMoses tried to say it was a dup of awk and sed, but it's not - again, the question explicitly says grep and sed only.
So here is my solution, assuming you are using Bash as your shell:
OLDIFS=$IFS
IFS=$'\n'
for f in `grep -rl a.example.com .` # Use -irl instead of -rl for case insensitive search
do
sed -i 's/a\.example\.com/b.example.com/g' $f # Use /gi instead of /g for case insensitive search
done
IFS=$OLDIFS
If you are using a different shell, such as Unix SHell, let me know and I will try to find a syntax adjustment.
P.S.: Here's a one-liner:
OLDIFS=$IFS;IFS=$'\n';for f in `grep -rl a.example.com .`;do sed -i 's/a\.example\.com/b.example.com/g' $f;done;IFS=$OLDIFS
Sources:
Bash: Iterating over lines in a variable
grep(1) - Linux man page
Official Grep Manual
sed(1) - Linux man page
Official sed Manual
For me works the next command:
find /path/to/dir -name "file.txt" | xargs sed -i 's/string_to_replace/new_string/g'
if string contains slash 'path/to/dir' it can be replace with another character to separate, like '#' instead '/'.
For example: 's#string/to/replace#new/string#g'
We can try using the more powerful ripgrep as
rg "BYE_BYE_TEXT" ./ --files-with-matches | xargs sed -i "s/BYE_BYE_TEXT/WELCOME_TEXT/g"
Because ripgrep is good at finding and sed is great at replacing.
it is much simpler than that.
for i in `find *` ; do sed -i -- 's/search string/target string/g' $i; done
find i => will iterate over all the files in the folder and in subfolders.
sed -i => will replace in the files the relevant string if exists.
Try this command:
/home/user/ directory - find ./ -type f \
-exec sed -i -e 's/a.example.com/b.example.com/g' {} \;
The command below will search all the files recursively whose name matches the search pattern and will replace the string:
find /path/to/searchdir/ -name "serachpatter" -type f | xargs sed -i 's/stringone/StrIngTwo/g'
Also if you want to limit the depth of recursion you can put the limits as well:
find /path/to/searchdir/ -name "serachpatter" -type f -maxdepth 4 -mindepth 2 | xargs sed -i 's/stringone/StrIngTwo/g'