I have an input:
a b TOCHANGE c d e TOCHANGE
where I need to change the patterns “TOCHANGE” using an external file :
line1 line2 ...
so that I get the following output :
a b line1 c d e line2
I tried the following command :
while read k ; do sed -i "s/TOCHANGE/$k/g" input ; done < externalfile
but I got :
a b line1 c d e line1
Answers:
Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.
Method 1
With perl:
perl -pi -e 's{TOCHANGE}{chomp ($repl = <STDIN>); $repl}ge' input <externalfile
With awk, assuming TOCHANGE doesn’t occur in externalfile (or more generally that replacements don’t generate new occurrences of TOCHANGE which could also happen for instance on an input that contains TOTOCHANGE FROMTOCHANGE and externalfile contains CHANGE and WHATEVER):
POSIXLY_CORRECT=1 PAT=TOCHANGE awk '
{
while ($0 ~ ENVIRON["PAT"]) {
getline repl < "externalfile"
gsub(/[&\]/, "\\&", repl)
sub(ENVIRON["PAT"], repl)
}
print
}' < input > input.new
(POSIXLY_CORRECT=1 is needed for GNU awk where without which it wouldn’t work correctly for replacement strings that contain backslash characters).
Note that $PAT above is taken as an extended regular expression. You may need to escape ERE operators if you want them to be treated literally (like PAT='TO.CHANGE' to replace TO.CHANGE strings).
Method 2
This can be done with GNU sed as given:
sed -e '/TOCHANGE/R file_2' input.txt | sed -e '/TOCHANGE/N;s/TOCHANGE(.*)n(.*)/21/'
In the first pass, sed will place a line of file_2 below the TOCHANGE line for all lines in input.txt
In the next pass, the line comprising TOCHANGE will be joined together with the following line and a s/// substitution will get the intended output.
With Perl it can be accomplished as:
perl -pe 's|TOCHANGE|<STDIN> =~ s/n//r|e' input.txt < file_2
Method 3
With this awk
awk -v old='TOCHANGE' '
NR==FNR{a[NR]=$0;next}
$2==old{$2=a[++i]}
1' changefile infile > outfile
Method 4
Couple tricky solutions with the specific awk features usage.
First variant
If the "TOCHANGE" pattern occurs only at once in each row. The usual awk will be enough.
awk '{
if(NF == 2) {
getline OFS < "file_2"
$1 = $1
}
print
}' FS='TOCHANGE' input.txt
Second variant
If the "TOCHANGE" pattern can occur many times in each row. The gawk needed.
gawk '{
RS="n"
if(RT)
getline ORS < "file_2"
else
ORS=""
print
RS="TOCHANGE"
}' RS='TOCHANGE' input.txt
Testing
input.txt
a b TOCHANGE c d e TOCHANGE f g TOCHANGE
file_2
line1 line2 line3 line4
Output
a b line1 c d e line2 f g line3
All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0