There are several ways to do this properly so as to handle arbitrary input.
With GNU sed
and a system that supports /dev/stdin
:
sed -n "${n}{p;q;}" file2 | sed -e "$m{r/dev/stdin" -e 'd;p;}' file1
or, slightly shorter1:
sed $n'!d;q' file2 | sed -e $m'{r /dev/stdin' -e 'd;p;}' file1
With any sed
and a shell that supports process substitution
sed '1h;1d;'$((m+1))'x' <(sed ${n}'!d;q' file2) file1
which could also be written as
sed ${n}'!d;q' file2 | sed '1h;1d;'$((m+1))'x' - file1
Basically, one sed
invocation extracts line n
from file2
which is then read by the other sed
as 1st operand: it saves it into the hold buffer, deletes it and then reads the content of the 2nd operand, namely file1
, exchanging buffers when on line m+1
(of the combined input).
With any sed
that supports reading a script-file via -f
from stdin
one could run:
sed ${n}'!d;i\
'${m}'c\\
s/\\/&&/g
q' file2 | sed -f - file1
here, the 1st sed
turns line n
from file2
into a script-file like
${m}c\
line_n_content_here_with_any_backslash_escaped
which is then used by the 2nd sed
to process file1
(namely replace line m
with the following text...). Any backslashes present in the original text (along with any embedded newlines - but here there's just one line) have to be escaped because when using any of a\
, i\
or c\
to add text
<backslash> characters in text shall be removed, and the following character shall be treated literally.
With any sed
, you can use the always-popular s
ubstitute command making sure that the string interpolated into sed
substitution escapes all reserved characters - in this particular case it's just one line so e.g.
line=$(sed ${m}'!d;s|[\/&]|\\&|g;q' file2)
then substitute:
sed ${m}'s/.*/'"$line"'/' file1
With huge input files you could run:
{ head -n $((m-1)); { head -n $((n-1)) >/dev/null; head -n 1; } <file2; head -n 1 >/dev/null; cat; } <file1
which does something like this:
print (m-1) lines from file1
discard (n-1) lines from file2
print n-th line from file2
discard m-th line from file1
print the remaining lines from file1
though some head
s are dumb and won't comply with the standards so this won't work on all setups... but where it does, it trumps sed
, awk
and the likes in terms of speed.
1: with some shells you might need to disable history expansion for that !
to work...
also, $n
and $m
don't really need quoting here as they're supposed to be positive integers though it doesn't hurt either
As a GNU sed extension, the special value /dev/stdin is supported for the file name, which reads the contents of the standard input
I think GNU sed will handle /dev/stdin irrespective of system details – Sundeep Oct 06 '17 at 09:42sed -n "${n}"'{p;q}'
to exit as soon as line is found – Sundeep Oct 06 '17 at 09:42/dev/stdin
: could be, but my manual doesn't mention that. :) Good point about{p;q;}
, edited. – Satō Katsura Oct 06 '17 at 10:36