Using awk to find the offset of the pattern, and dd to truncate the file at that point and append the new trailer:
# usage truncatoo pattern new_end find_args ...
truncatoo(){
pat=$1; shift; tail=$1; shift
LC_CTYPE=C TAIL=$tail find "$@" -exec awk -v q="'" "$pat"'{
gsub(q,q"\\"q q,FILENAME);
system("echo"printf \"$TAIL\" | dd bs="l" seek=1 of="q FILENAME q" 2>/dev/null");
exit
}
{l+=length+1l+=length()+1}
' {} \;
}
truncatoo '/END DATA/' 'NEW END\n' file.txt
truncatoo '/END DATA/' 'NEW END\n' . -type f -name '*.txt'
With an awk implementation which supports nextfile (gawk, bwk, some versions of mawk [1]), that could be done more efficiently by passing batches of files to awk:
# usage truncatoo pattern new_end find_args ...
truncatoo(){
pat=$1; shift; tail=$1; shift
LC_CTYPE=C TAIL=$tail find "$@" -exec awk -v q="'" "$pat"'{
gsub(q,q"\\"q q,FILENAME);
system("echo"printf \"$TAIL\" | dd bs="l" seek=1 of="q FILENAME q" 2>/dev/null");
l=0; nextfile
}
{l+=length+1l+=length()+1}
' {} +
}
$ truncatoo '/END DATA/' 'NEW END' file.txt
$ truncatoo '/END DATA/' 'NEW END' . -type f -name '*.txt'
$ file="a'b\$q * r"; seq 1 100 >"$file"
$ truncatoo /7/ EOT'CUT\n' "$file"; cat "$file"
1
2
3
4
5
6
EOTCUT
Instead of the icky 2>/dev/null status=noxfer could be used with dd implementations which support it.
The quoting kludge and environment variables passing is a mess, it could use some improvement.
[1]: according to the GNU awk manual, it should be supported in mawk too. However, the older version of mawk from Debian 10 does not support it.