multiple file text replace with sed
Solution 1
You can loop through the file using while ... do
loop:
$ while read i; do printf "Current line: %s\n" "$i"; done < target_files_list.txt
In your case you should replace printf ...
with sed
command you want.
$ while read i; do sed -i -- 's/SOME_TEXT/SOME_TEXT_TO_REPLACE/g' "$i"; done < target_files_list.txt
However, notice that you can achieve what you want using only find
:
$ find /path/to/files/ -name "target_text_file" -exec sed -i -- 's/SOME_TEXT/SOME_TEXT_TO_REPLACE/g' {} \;
You can read more about -exec
option by running man find | less '+/-exec '
:
-exec command ; Execute command; true if 0 status is returned. All following arguments to find are taken to be arguments to the command until an argument consisting of `;' is encountered. The string `{}' is replaced by the current file name being processed everywhere it occurs in the arguments to the command, not just in arguments where it is alone, as in some versions of find. Both of these constructions might need to be escaped (with a `\') or quoted to protect them from expansion by the shell. See the EXAMPLES section for examples of the use of the -exec option. The specified command is run once for each matched file. The command is executed in the starting directory. There are unavoidable security problems surrounding use of the -exec action; you should use the -execdir option instead.
EDIT:
As correctly noted by users
terdon and dessert in the comments
it's necessary to use -r
with read
because it will correctly
handle backslashes. It's also reported by shellcheck
:
$ cat << EOF >> do.sh
#!/usr/bin/env sh
while read i; do printf "$i\n"; done < target_files_list.txt
EOF
$ ~/.cabal/bin/shellcheck do.sh
In do.sh line 2:
while read i; do printf "\n"; done < target_files_list.txt
^-- SC2162: read without -r will mangle backslashes.
So it should be:
$ while read -r i; do sed -i -- 's/SOME_TEXT/SOME_TEXT_TO_REPLACE/g' "$i"; done < target_files_list.txt
Solution 2
One way would be to use xargs
:
xargs -a target_files_list.txt -d '\n' sed -i -- 's/SOME_TEXT/TEXT_TO_REPLACE/g'
From man xargs
:
-a file, --arg-file=file
Read items from file instead of standard input.
--delimiter=delim, -d delim
Input items are terminated by the specified character. The
specified delimiter may be a single character, a C-style charac‐
ter escape such as \n, or an octal or hexadecimal escape code.
Solution 3
Just use a for
loop.
IFS=$'\n' # Very important! Splits files on newline instead of space.
for file in $(cat files.txt); do
sed ...
done
Note that you will run into problems if you encounter any files with newlines (!) in their names. (:
elanozturk
Prospective Dreamer/Cloud Appreciator/Aviator/Cyclist/Tourist Guide/DreamSynth/C64&Sid
Updated on September 18, 2022Comments
-
elanozturk over 1 year
I try to replace some texts in text files with
sed
but don't know how to do it with multiple files.
I use:sed -i -- 's/SOME_TEXT/SOME_TEXT_TO_REPLACE/g /path/to/file/target_text_file
Before i go with the multiple files i printed the paths of targeted text files in a text file with this command:
find /path/to/files/ -name "target_text_file" > /home/user/Desktop/target_files_list.txt
Now i want to run
sed
according totarget_files_list.txt
. -
dessert over 6 years
read
will not see the last line of the input file if it doesn't end with a newline character, I recommendwhile IFS='' read -r i || [[ -n "$i" ]]; do …
instead. -
Arkadiusz Drabczyk over 6 yearsI appreciate your remark. However, note that user uses
>
that will handle new lines automatically and that it's common inunix
world to end files with newilne: stackoverflow.com/questions/729692/…. -
dessert over 6 yearsYou're totally right and that's the way everyone should do that, but I prefer adapting the code to the user, not the other way around. ;)
-
terdon over 6 years@ArkadiuszDrabczyk the problem isn't so much the rare occurrence of a file with no terminal newline, but the much more common issue of file names with spaces. Using
while IFS=
makes this work on file names with spaces (or tabs, newlines etc) and using-r
makes it work for file names with backslashes. Yourfind
solution can deal with arbitrary file names, but thewhile
approach needs the changes dessert suggested to make it robust. -
Arkadiusz Drabczyk over 6 years@terdon: @dessert: I added
-r
, thanks for your help. But I'm still not sure why should I addIFS=''
explicitly. The current solution with the default value ofIFS
makesprintf
correctly print filenames with spaces and tabs. Is this just to make sure thatIFS
is set to the correct value before starting the loop? -
steeldriver over 6 years@ArkadiuszDrabczyk the only case I'm aware of where it makes a difference (at least in current versions of bash) is if the filename starts with whitespace