2

I want to apply a unified diff from mypatch.diff to stdin and output the result to stdout.

So far, I have tried:

patch -i mypatch.diff -o - -u originalfile

Which successfully applies mypatch.diff and prints the result to stdout. However, I still have to provide the original file as originalfile, not via stdin.

And if I try something like:

patch -i mypatch.diff -o - -u -

Then the patch gets rejected:

patching file -
Hunk #1 FAILED at 1.
1 out of 1 hunk FAILED -- saving rejects to file -.rej
finefoot
  • 3,060

2 Answers2

2

Here is a work-around solution for POSIX shell:

#!/bin/sh
patchstdin() (
    set -o errexit
    tmpdir="$(mktemp -d)"
    trap 'rm -rf -- "$tmpdir"' EXIT
    trap 'exit 1' HUP INT TERM
    cat >"$tmpdir/original"
    patch "$@" -o "$tmpdir/patched" "$tmpdir/original"
    cat "$tmpdir/patched"
)

Assuming yourcommand produces the output that is supposed to get patched, and anothercommand is expecting to receive the patched output, you would call:

yourcommand | patchstdin -i patch.diff | anothercommand

Note that mktemp is not part of POSIX. See Why is there no mktemp command in POSIX? for more information and How create a temporary file in shell script? for solutions for POSIX shell.

finefoot
  • 3,060
  • Note that -o - is not POSIX either, or at least AFAICT POSIX would require the output to go to a file called - in the current working directory. – Stéphane Chazelas Mar 03 '23 at 18:16
  • Personally, I avoid set -e for anything but the simplest scripts, so typically not when functions of subscripts are used. I tend to prefer the set -o errexit syntax. – Stéphane Chazelas Mar 03 '23 at 19:58
  • you may want to use mktemp -d and use two separate tempfiles for input and output. You'd then delete the whole directory with both files and potential rej/orig files (I find that GNU patch saves rejects to -.rej with -o -) – Stéphane Chazelas Mar 03 '23 at 20:06
  • Thanks. :) I took your advice and changed it to a temp folder with two files. I also took your advice and used the more explicit set -o errexit instead of set -e. Again, thank you very much for your feedback. – finefoot Mar 03 '23 at 23:27
  • Or did you mean to say that you prefer || exit over set -o errexit here? If yes, just a personal preference or is there an argument for this choice? I feel like as I use the shell more and more, my scripts get a little more extensive and I add error handling and it helps to just set -o errexit once at the beginning. – finefoot Mar 03 '23 at 23:32
  • errexit is completely unreliable and unportable and can't replace proper error handling. At best it's only marginally better than not doing any error handling at all. See for instance https://mywiki.wooledge.org/BashFAQ/105. Here that errexit would be cancelled in pathstdin ... && echo Patch applied successfully for instance. My rule is subshells or functions => no errexit – Stéphane Chazelas Mar 04 '23 at 07:35
  • I think your patch command may be back to front? The docs say it should be patch [options] [originalfile [patchfile]] but it looks like you have the patch first. – alicederyn Mar 21 '24 at 09:09
1

It doesn't look like GNU patch has an option for that. - means stdin for -i or stdout for -o (and for -r is interpreted as discarding rejects) but for the file to patch - is interpreted as the file called - in the current directory.

Also, patch will want to create files with .orig or .rej suffix when relevant based on the name of the file to patch.

It looks like patch won't let you patch symlinks, so on Linux, using /dev/stdin or /proc/self/fd/0 won't work.

If using zsh, you can use the =(...) form of process substitution that uses a temporary file:

patch -i mypatch.diff -r - -o - =(cat)

Note that for -o - and -r -, you need GNU patch 2.6 or newer (see commit and commit).

  • @finefoot, not much point concatenating a single file. Use <input patch... instead of cat input | patch. You'd also want to add some error handling, possibly signal handling to handle clean-up (I don't think zsh does signal handling though for the temp file it creates with =(...)). – Stéphane Chazelas Mar 02 '23 at 15:58
  • @finefoot. Also use cat > "$tmpfile" rather than tee "$tmpfile" >/dev/null. No point in duplicating it and discarding one of the copies. In zsh, you could use just >$tmpfile but that would still run cat under the hood, you still something to do the reading and the writing there. – Stéphane Chazelas Mar 02 '23 at 16:51
  • Hahaha, you're too fast for me. :) I wanted to post everything first and then reply in the comments. Re: "not much point concatenating a single file" Yes, that was only an example to show the pipe. Re: "use cat (...) rather than tee" Very good point. I don't know what I was thinking there. – finefoot Mar 03 '23 at 18:21
  • I also realized that mktemp isn't POSIX and my Debian doesn't have m4 installed by default, so I wanted to have a more general POSIX solution. https://unix.stackexchange.com/a/738570 I posted it here if you're in the mood for more proofreading. :) Let me check out your edit on my post now. Thank you so much for your participation on this site. – finefoot Mar 03 '23 at 18:21