Is there a way to make sed ask me for confirmation before each replace? Something similar to 'c' when using replace inside vim.
Does sed do this at all?
Is there a way to make sed ask me for confirmation before each replace? Something similar to 'c' when using replace inside vim.
Does sed do this at all?
Doing it with sed
would probably not be possible as it's a non-interactive stream editor. Wrapping sed
in a script would require far too much thinking. It is easier to just do it with vim
:
vim -c '%s/PATTERN/REPLACEMENT/gc' -c 'wq' file.in
Since it was mentioned in comments below, here's how this would be used on multiple files matching a particular filename globbing pattern in the current directory:
for fname in file*.txt; do
vim -c '%s/PATTERN/REPLACEMENT/gc' -c 'wq' "$fname"
done
Or, if you first want to make sure that the file really contains a line that matches the given pattern first, before performing the substitution,
for fname in file*.txt; do
grep -q 'PATTERN' "$fname" &&
vim -c '%s/PATTERN/REPLACEMENT/gc' -c 'wq' "$fname"
done
The above two shell loops modified into find
commands that do the same things but for all files with a particular name somewhere in or under some top-dir
directory,
find top-dir -type f -name 'file*.txt' \
-exec vim -c '%s/PATTERN/REPLACEMENT/gc' -c 'wq' {} \;
find top-dir -type f -name 'file*.txt' \
-exec grep -q 'PATTERN' {} \; \
-exec vim -c '%s/PATTERN/REPLACEMENT/gc' -c 'wq' {} \;
Or, using the original shell loops and having find
feed pathnames into them:
find top-dir -type f -name 'file*.txt' -exec sh -c '
for pathname do
vim -c "%s/PATTERN/REPLACEMENT/gc" -c "wq" "$pathname"
done' sh {} +
find top-dir -type f -name 'file*.txt' -exec sh -c '
for pathname do
grep -q "PATTERN" "$pathname" &&
vim -c "%s/PATTERN/REPLACEMENT/gc" -c "wq" "$pathname"
done' sh {} +
You do, in any case, not want to do something like for filename in $( grep -rl ... )
since
grep
finishes running before even starting the first iteration of loop, which is inelegant, andgrep
would be split into words on whitespaces, and these words would undergo filename globbing (this disqualifies pathnames that contains spaces and special characters).Related:
sed
is it can operate on multiple files, e.g., sed -i 's/old/new/g' /path/to/*.txt
or something similar.
– user1717828
May 10 '16 at 12:44
for i in $(grep -rl "old"); do vim -c "%s/old/new/gc" -c "wq" "$i"; done
– tcpaiva
Aug 14 '16 at 02:19
vim $(grep -RIlZ 'pattern')
then in vim use :bufdo %s/pattern/replace/gce | update
– JKirchartz
Apr 21 '20 at 15:16
You can get this by doing such:
:%s/OLD_TEXT/NEW_TEXT/gc
Specifically, adding the c
after the third delimiter.
Note that the 'c' option only works in Vim; you won't be able to use it with sed
at the command line.
vim
, so this answer is applicable; though, clearly vim and sed have different abilities
– ILMostro_7
Mar 13 '16 at 00:35
You could let sed
do its thing on the file and then save the result to a temporary file which you can then interactively patch into the original file using sdiff
(see http://www.gnu.org/software/diffutils/manual/diffutils.html#Invoking-sdiff):
sed -r 's/something/something_else/g' my_file > tmp_file
sdiff -o my_file -s -d my_file tmp_file
If you don't want to open and close vim a whole bunch as other answers suggest, you can do the entire operation in vim...
1: open all files containing your pattern:
vim $(grep -RIlZ 'pattern')
2) in vim replace your pattern in all open buffers (with confirmation):
:bufdo %s/pattern/replace/gce | update
These can even be combined like so
vim $(grep -RIlZ 'pattern') -c ':bufdo %s/pattern/replace/gce | update'
searchandreplace () {
if [ -z $2 ] ; then
realpath * | xargs grep -ins --directories=skip --color=always "$1" | nl -s "."
else
realpath * | xargs grep -ins --directories=skip --color=always "$1" | nl -s "."
exp=$(realpath * | xargs grep -ins --directories=skip --color=never "$1" | cut -d':' -f1,2 | awk -F':' '{print $2 $1}' | sed -E "s|^[0-9]+|sed -i \'&s/|; s|//|/"$1"/"$2"/g\' /|")
IFS=$'\n'
for d in $exp
do
read -p "Exec $(echo -e -n "\033[1;31m$d\033[0m")? " -e REP
if [ "$REP" = y ]; then
echo `bash -c $d`;
fi
done
fi
}
If you feed this with single argument –searchandreplace "AAA"
– it only searches for that string in the current directory, but if you feed it with double arguments –searchandreplace AAA BBB
– then in addition to the above it also asks you to replace the first with the second "line by line" for every line.