1

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
  • 2,940
  • 2
  • 21
  • 41

2 Answers2

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).

Stéphane Chazelas
  • 522,931
  • 91
  • 1,010
  • 1,501
  • @finefoot, not much point con`cat`enating a single file. Use ` – 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
1

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
  • 2,940
  • 2
  • 21
  • 41
  • 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