diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
index 8eca4cf23bb643b9c5ec0c974a69b5f64c6a5997..6e8c62395c0735f434d961dfa02b9b673c38be8b 100644
--- a/src/bin/psql/command.c
+++ b/src/bin/psql/command.c
@@ -1531,6 +1531,7 @@ exec_command(const char *cmd,
 				if (fname[0] == '|')
 				{
 					is_pipe = true;
+					disable_sigpipe_trap();
 					fd = popen(&fname[1], "w");
 				}
 				else
@@ -1565,6 +1566,9 @@ exec_command(const char *cmd,
 			}
 		}
 
+		if (is_pipe)
+			restore_sigpipe_trap();
+
 		free(fname);
 	}
 
diff --git a/src/bin/psql/common.c b/src/bin/psql/common.c
index 3254a140b3a665c0e10b009373c83143b387cc3f..a287eeee1951f7f1e9de885de8b7b6cd3c9904d8 100644
--- a/src/bin/psql/common.c
+++ b/src/bin/psql/common.c
@@ -26,72 +26,90 @@
 #include "mbprint.h"
 
 
-
 static bool ExecQueryUsingCursor(const char *query, double *elapsed_msec);
 static bool command_no_begin(const char *query);
 static bool is_select_command(const char *query);
 
+
 /*
- * setQFout
- * -- handler for -o command line option and \o command
+ * openQueryOutputFile --- attempt to open a query output file
  *
- * Tries to open file fname (or pipe if fname starts with '|')
- * and stores the file handle in pset)
- * Upon failure, sets stdout and returns false.
+ * fname == NULL selects stdout, else an initial '|' selects a pipe,
+ * else plain file.
+ *
+ * Returns output file pointer into *fout, and is-a-pipe flag into *is_pipe.
+ * Caller is responsible for adjusting SIGPIPE state if it's a pipe.
+ *
+ * On error, reports suitable error message and returns FALSE.
  */
 bool
-setQFout(const char *fname)
+openQueryOutputFile(const char *fname, FILE **fout, bool *is_pipe)
 {
-	bool		status = true;
-
-	/* Close old file/pipe */
-	if (pset.queryFout && pset.queryFout != stdout && pset.queryFout != stderr)
-	{
-		if (pset.queryFoutPipe)
-			pclose(pset.queryFout);
-		else
-			fclose(pset.queryFout);
-	}
-
-	/* If no filename, set stdout */
 	if (!fname || fname[0] == '\0')
 	{
-		pset.queryFout = stdout;
-		pset.queryFoutPipe = false;
+		*fout = stdout;
+		*is_pipe = false;
 	}
 	else if (*fname == '|')
 	{
-		pset.queryFout = popen(fname + 1, "w");
-		pset.queryFoutPipe = true;
+		*fout = popen(fname + 1, "w");
+		*is_pipe = true;
 	}
 	else
 	{
-		pset.queryFout = fopen(fname, "w");
-		pset.queryFoutPipe = false;
+		*fout = fopen(fname, "w");
+		*is_pipe = false;
 	}
 
-	if (!(pset.queryFout))
+	if (*fout == NULL)
 	{
 		psql_error("%s: %s\n", fname, strerror(errno));
-		pset.queryFout = stdout;
-		pset.queryFoutPipe = false;
-		status = false;
+		return false;
 	}
 
-	/* Direct signals */
-#ifndef WIN32
-	pqsignal(SIGPIPE, pset.queryFoutPipe ? SIG_IGN : SIG_DFL);
-#endif
-
-	return status;
+	return true;
 }
 
+/*
+ * setQFout
+ * -- handler for -o command line option and \o command
+ *
+ * On success, updates pset with the new output file and returns true.
+ * On failure, returns false without changing pset state.
+ */
+bool
+setQFout(const char *fname)
+{
+	FILE	   *fout;
+	bool		is_pipe;
+
+	/* First make sure we can open the new output file/pipe */
+	if (!openQueryOutputFile(fname, &fout, &is_pipe))
+		return false;
+
+	/* Close old file/pipe */
+	if (pset.queryFout && pset.queryFout != stdout && pset.queryFout != stderr)
+	{
+		if (pset.queryFoutPipe)
+			pclose(pset.queryFout);
+		else
+			fclose(pset.queryFout);
+	}
+
+	pset.queryFout = fout;
+	pset.queryFoutPipe = is_pipe;
+
+	/* Adjust SIGPIPE handling appropriately: ignore signal if is_pipe */
+	set_sigpipe_trap_state(is_pipe);
+	restore_sigpipe_trap();
+
+	return true;
+}
 
 
 /*
  * Error reporting for scripts. Errors should look like
  *	 psql:filename:lineno: message
- *
  */
 void
 psql_error(const char *fmt,...)
@@ -611,27 +629,23 @@ PrintQueryTuples(const PGresult *results)
 	/* write output to \g argument, if any */
 	if (pset.gfname)
 	{
-		/* keep this code in sync with ExecQueryUsingCursor */
-		FILE	   *queryFout_copy = pset.queryFout;
-		bool		queryFoutPipe_copy = pset.queryFoutPipe;
-
-		pset.queryFout = stdout;	/* so it doesn't get closed */
+		FILE	   *fout;
+		bool		is_pipe;
 
-		/* open file/pipe */
-		if (!setQFout(pset.gfname))
-		{
-			pset.queryFout = queryFout_copy;
-			pset.queryFoutPipe = queryFoutPipe_copy;
+		if (!openQueryOutputFile(pset.gfname, &fout, &is_pipe))
 			return false;
-		}
-
-		printQuery(results, &my_popt, pset.queryFout, false, pset.logfile);
+		if (is_pipe)
+			disable_sigpipe_trap();
 
-		/* close file/pipe, restore old setting */
-		setQFout(NULL);
+		printQuery(results, &my_popt, fout, false, pset.logfile);
 
-		pset.queryFout = queryFout_copy;
-		pset.queryFoutPipe = queryFoutPipe_copy;
+		if (is_pipe)
+		{
+			pclose(fout);
+			restore_sigpipe_trap();
+		}
+		else
+			fclose(fout);
 	}
 	else
 		printQuery(results, &my_popt, pset.queryFout, false, pset.logfile);
@@ -1199,10 +1213,10 @@ ExecQueryUsingCursor(const char *query, double *elapsed_msec)
 	PGresult   *results;
 	PQExpBufferData buf;
 	printQueryOpt my_popt = pset.popt;
-	FILE	   *queryFout_copy = pset.queryFout;
-	bool		queryFoutPipe_copy = pset.queryFoutPipe;
+	FILE	   *fout;
+	bool		is_pipe;
+	bool		is_pager = false;
 	bool		started_txn = false;
-	bool		did_pager = false;
 	int			ntuples;
 	int			fetch_count;
 	char		fetch_cmd[64];
@@ -1268,21 +1282,22 @@ ExecQueryUsingCursor(const char *query, double *elapsed_msec)
 	/* prepare to write output to \g argument, if any */
 	if (pset.gfname)
 	{
-		/* keep this code in sync with PrintQueryTuples */
-		pset.queryFout = stdout;	/* so it doesn't get closed */
-
-		/* open file/pipe */
-		if (!setQFout(pset.gfname))
+		if (!openQueryOutputFile(pset.gfname, &fout, &is_pipe))
 		{
-			pset.queryFout = queryFout_copy;
-			pset.queryFoutPipe = queryFoutPipe_copy;
 			OK = false;
 			goto cleanup;
 		}
+		if (is_pipe)
+			disable_sigpipe_trap();
+	}
+	else
+	{
+		fout = pset.queryFout;
+		is_pipe = false;		/* doesn't matter */
 	}
 
 	/* clear any pre-existing error indication on the output stream */
-	clearerr(pset.queryFout);
+	clearerr(fout);
 
 	for (;;)
 	{
@@ -1302,12 +1317,10 @@ ExecQueryUsingCursor(const char *query, double *elapsed_msec)
 		if (PQresultStatus(results) != PGRES_TUPLES_OK)
 		{
 			/* shut down pager before printing error message */
-			if (did_pager)
+			if (is_pager)
 			{
-				ClosePager(pset.queryFout);
-				pset.queryFout = queryFout_copy;
-				pset.queryFoutPipe = queryFoutPipe_copy;
-				did_pager = false;
+				ClosePager(fout);
+				is_pager = false;
 			}
 
 			OK = AcceptResult(results);
@@ -1331,17 +1344,17 @@ ExecQueryUsingCursor(const char *query, double *elapsed_msec)
 			/* this is the last result set, so allow footer decoration */
 			my_popt.topt.stop_table = true;
 		}
-		else if (pset.queryFout == stdout && !did_pager)
+		else if (fout == stdout && !is_pager)
 		{
 			/*
 			 * If query requires multiple result sets, hack to ensure that
 			 * only one pager instance is used for the whole mess
 			 */
-			pset.queryFout = PageOutput(INT_MAX, &(my_popt.topt));
-			did_pager = true;
+			fout = PageOutput(INT_MAX, &(my_popt.topt));
+			is_pager = true;
 		}
 
-		printQuery(results, &my_popt, pset.queryFout, did_pager, pset.logfile);
+		printQuery(results, &my_popt, fout, is_pager, pset.logfile);
 
 		PQclear(results);
 
@@ -1355,7 +1368,7 @@ ExecQueryUsingCursor(const char *query, double *elapsed_msec)
 		 * the pager dies/exits/etc, there's no sense throwing more data at
 		 * it.
 		 */
-		flush_error = fflush(pset.queryFout);
+		flush_error = fflush(fout);
 
 		/*
 		 * Check if we are at the end, if a cancel was pressed, or if there
@@ -1365,24 +1378,25 @@ ExecQueryUsingCursor(const char *query, double *elapsed_msec)
 		 * stop bothering to pull down more data.
 		 */
 		if (ntuples < fetch_count || cancel_pressed || flush_error ||
-			ferror(pset.queryFout))
+			ferror(fout))
 			break;
 	}
 
-	/* close \g argument file/pipe, restore old setting */
 	if (pset.gfname)
 	{
-		/* keep this code in sync with PrintQueryTuples */
-		setQFout(NULL);
-
-		pset.queryFout = queryFout_copy;
-		pset.queryFoutPipe = queryFoutPipe_copy;
+		/* close \g argument file/pipe */
+		if (is_pipe)
+		{
+			pclose(fout);
+			restore_sigpipe_trap();
+		}
+		else
+			fclose(fout);
 	}
-	else if (did_pager)
+	else if (is_pager)
 	{
-		ClosePager(pset.queryFout);
-		pset.queryFout = queryFout_copy;
-		pset.queryFoutPipe = queryFoutPipe_copy;
+		/* close transient pager */
+		ClosePager(fout);
 	}
 
 cleanup:
diff --git a/src/bin/psql/common.h b/src/bin/psql/common.h
index caf31d19b89d3ba03c1034c213ca82b7e2e29891..62a602632a128105c2b24c6cea369abce5513a81 100644
--- a/src/bin/psql/common.h
+++ b/src/bin/psql/common.h
@@ -16,6 +16,7 @@
 
 #define atooid(x)  ((Oid) strtoul((x), NULL, 10))
 
+extern bool openQueryOutputFile(const char *fname, FILE **fout, bool *is_pipe);
 extern bool setQFout(const char *fname);
 
 extern void psql_error(const char *fmt,...) pg_attribute_printf(1, 2);
diff --git a/src/bin/psql/copy.c b/src/bin/psql/copy.c
index c0fc28373a93a7e7b9f13268eea67a4df18d05af..a09408bf5504e7ab09f15d9627447bc952a6f012 100644
--- a/src/bin/psql/copy.c
+++ b/src/bin/psql/copy.c
@@ -37,7 +37,7 @@
  * where 'filename' can be one of the following:
  *	'<file path>' | PROGRAM '<command>' | stdin | stdout | pstdout | pstdout
  * and 'query' can be one of the following:
- *  SELECT | UPDATE | INSERT | DELETE
+ *	SELECT | UPDATE | INSERT | DELETE
  *
  * An undocumented fact is that you can still write BINARY before the
  * tablename; this is a hangover from the pre-7.3 syntax.  The options
@@ -312,9 +312,7 @@ do_copy(const char *args)
 				fflush(stdout);
 				fflush(stderr);
 				errno = 0;
-#ifndef WIN32
-				pqsignal(SIGPIPE, SIG_IGN);
-#endif
+				disable_sigpipe_trap();
 				copystream = popen(options->file, PG_BINARY_W);
 			}
 			else
@@ -399,9 +397,7 @@ do_copy(const char *args)
 				}
 				success = false;
 			}
-#ifndef WIN32
-			pqsignal(SIGPIPE, SIG_DFL);
-#endif
+			restore_sigpipe_trap();
 		}
 		else
 		{
diff --git a/src/bin/psql/print.c b/src/bin/psql/print.c
index 190f2bc5d854ecedab990204dd2bcafa12a74fbb..05d4b3162c3441530a8d3cb286a23ba65ed5b827 100644
--- a/src/bin/psql/print.c
+++ b/src/bin/psql/print.c
@@ -39,6 +39,13 @@
  */
 volatile bool cancel_pressed = false;
 
+/*
+ * Likewise, the sigpipe_trap and pager open/close functions are here rather
+ * than in common.c so that this file can be used by non-psql programs.
+ */
+static bool always_ignore_sigpipe = false;
+
+
 /* info for locale-aware numeric formatting; set up by setDecimalLocale() */
 static char *decimal_point;
 static int	groupdigits;
@@ -2775,10 +2782,61 @@ print_troff_ms_vertical(const printTableContent *cont, FILE *fout)
 
 
 /********************************/
-/* Public functions		*/
+/* Public functions				*/
 /********************************/
 
 
+/*
+ * disable_sigpipe_trap
+ *
+ * Turn off SIGPIPE interrupt --- call this before writing to a temporary
+ * query output file that is a pipe.
+ *
+ * No-op on Windows, where there's no SIGPIPE interrupts.
+ */
+void
+disable_sigpipe_trap(void)
+{
+#ifndef WIN32
+	pqsignal(SIGPIPE, SIG_IGN);
+#endif
+}
+
+/*
+ * restore_sigpipe_trap
+ *
+ * Restore normal SIGPIPE interrupt --- call this when done writing to a
+ * temporary query output file that was (or might have been) a pipe.
+ *
+ * Note: within psql, we enable SIGPIPE interrupts unless the permanent query
+ * output file is a pipe, in which case they should be kept off.  This
+ * approach works only because psql is not currently complicated enough to
+ * have nested usages of short-lived output files.  Otherwise we'd probably
+ * need a genuine save-and-restore-state approach; but for now, that would be
+ * useless complication.  In non-psql programs, this always enables SIGPIPE.
+ *
+ * No-op on Windows, where there's no SIGPIPE interrupts.
+ */
+void
+restore_sigpipe_trap(void)
+{
+#ifndef WIN32
+	pqsignal(SIGPIPE, always_ignore_sigpipe ? SIG_IGN : SIG_DFL);
+#endif
+}
+
+/*
+ * set_sigpipe_trap_state
+ *
+ * Set the trap state that restore_sigpipe_trap should restore to.
+ */
+void
+set_sigpipe_trap_state(bool ignore)
+{
+	always_ignore_sigpipe = ignore;
+}
+
+
 /*
  * PageOutput
  *
@@ -2792,9 +2850,6 @@ PageOutput(int lines, const printTableOpt *topt)
 	/* check whether we need / can / are supposed to use pager */
 	if (topt && topt->pager && isatty(fileno(stdin)) && isatty(fileno(stdout)))
 	{
-		const char *pagerprog;
-		FILE	   *pagerpipe;
-
 #ifdef TIOCGWINSZ
 		unsigned short int pager = topt->pager;
 		int			min_lines = topt->pager_min_lines;
@@ -2807,20 +2862,19 @@ PageOutput(int lines, const printTableOpt *topt)
 		if (result == -1
 			|| (lines >= screen_size.ws_row && lines >= min_lines)
 			|| pager > 1)
-		{
 #endif
+		{
+			const char *pagerprog;
+			FILE	   *pagerpipe;
+
 			pagerprog = getenv("PAGER");
 			if (!pagerprog)
 				pagerprog = DEFAULT_PAGER;
-#ifndef WIN32
-			pqsignal(SIGPIPE, SIG_IGN);
-#endif
+			disable_sigpipe_trap();
 			pagerpipe = popen(pagerprog, "w");
 			if (pagerpipe)
 				return pagerpipe;
-#ifdef TIOCGWINSZ
 		}
-#endif
 	}
 
 	return stdout;
@@ -2848,9 +2902,7 @@ ClosePager(FILE *pagerpipe)
 			fprintf(pagerpipe, _("Interrupted\n"));
 
 		pclose(pagerpipe);
-#ifndef WIN32
-		pqsignal(SIGPIPE, SIG_DFL);
-#endif
+		restore_sigpipe_trap();
 	}
 }
 
diff --git a/src/bin/psql/print.h b/src/bin/psql/print.h
index df514cffb0c770ae07bc63303ba8057de6c833bd..fd56598426da62390e7428cd6dcba1cbf457bebb 100644
--- a/src/bin/psql/print.h
+++ b/src/bin/psql/print.h
@@ -167,6 +167,10 @@ extern const printTextFormat pg_asciiformat_old;
 extern const printTextFormat pg_utf8format;
 
 
+extern void disable_sigpipe_trap(void);
+extern void restore_sigpipe_trap(void);
+extern void set_sigpipe_trap_state(bool ignore);
+
 extern FILE *PageOutput(int lines, const printTableOpt *topt);
 extern void ClosePager(FILE *pagerpipe);
 
diff --git a/src/bin/psql/startup.c b/src/bin/psql/startup.c
index 7aa997d479c8be02ad8c7b2180b3ff8c3b5c3709..4e5021a43df80fae16ab3b2a4cf34434e52dc6c0 100644
--- a/src/bin/psql/startup.c
+++ b/src/bin/psql/startup.c
@@ -461,7 +461,8 @@ parse_psql_options(int argc, char *argv[], struct adhoc_opts * options)
 				options->no_readline = true;
 				break;
 			case 'o':
-				setQFout(optarg);
+				if (!setQFout(optarg))
+					exit(EXIT_FAILURE);
 				break;
 			case 'p':
 				options->port = pg_strdup(optarg);