diff --git a/doc/src/sgml/ref/psql-ref.sgml b/doc/src/sgml/ref/psql-ref.sgml
index 969548f..6719135 100644
--- a/doc/src/sgml/ref/psql-ref.sgml
+++ b/doc/src/sgml/ref/psql-ref.sgml
@@ -1971,7 +1971,8 @@ lo_import 152801
           Sets the output format to one of <literal>unaligned</literal>,
           <literal>aligned</literal>, <literal>wrapped</literal>,
           <literal>html</literal>,
-          <literal>latex</literal>, or <literal>troff-ms</literal>.
+          <literal>latex</literal>,  <literal>troff-ms</literal> or
+          <literal>shell</literal>.
           Unique abbreviations are allowed.  (That would mean one letter
           is enough.)
           </para>
@@ -1996,6 +1997,24 @@ lo_import 152801
           if the total width needed for column headers exceeds the target.
           </para>
 
+          <para><literal>shell</> format is designed for using in shell scripts.
+<programlisting>
+( psql -t -P format=shell postgres &lt;&lt;EOF
+SELECT d.datname as "Name",
+       pg_catalog.pg_get_userbyid(d.datdba) as "Owner",
+       pg_catalog.pg_encoding_to_char(d.encoding) as "Encoding",
+       d.datcollate as "Collate",
+       d.datctype as "Ctype",
+       pg_catalog.array_to_string(d.datacl, E'\n') AS "Access privileges"
+FROM pg_catalog.pg_database d
+ORDER BY 1;
+EOF
+) | while read dbname owner encoding collate ctype priv;
+    do
+      echo "DBNAME=$dbname OWNER=$owner PRIVILEGES=$priv";
+    done;
+</programlisting></para>
+
           <para>
           The <literal>html</>, <literal>latex</>, and <literal>troff-ms</>
           formats put out tables that are intended to
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
index 7f91ff9..c3313ec 100644
--- a/src/bin/psql/command.c
+++ b/src/bin/psql/command.c
@@ -2148,6 +2148,9 @@ _align2string(enum printFormat in)
 		case PRINT_TROFF_MS:
 			return "troff-ms";
 			break;
+		case PRINT_SHELL:
+			return "shell";
+			break;
 	}
 	return "unknown";
 }
@@ -2180,6 +2183,8 @@ do_pset(const char *param, const char *value, printQueryOpt *popt, bool quiet)
 			popt->topt.format = PRINT_LATEX;
 		else if (pg_strncasecmp("troff-ms", value, vallen) == 0)
 			popt->topt.format = PRINT_TROFF_MS;
+		else if (pg_strncasecmp("shell", value, vallen) == 0)
+			popt->topt.format = PRINT_SHELL;
 		else
 		{
 			psql_error("\\pset: allowed formats are unaligned, aligned, wrapped, html, latex, troff-ms\n");
diff --git a/src/bin/psql/print.c b/src/bin/psql/print.c
index c431f6a..f375811 100644
--- a/src/bin/psql/print.c
+++ b/src/bin/psql/print.c
@@ -2037,6 +2037,125 @@ print_troff_ms_vertical(const printTableContent *cont, FILE *fout)
 	}
 }
 
+/********************/
+/* Shell		*/
+/********************/
+
+static void
+shell_escaped_print(const char *in, FILE *fout)
+{
+	const char *p;
+
+	for (p = in; *p; p++)
+		switch (*p)
+		{
+			case '\n':
+				fputs("\\\\n", fout);
+				break;
+			case '\\':
+			case '\'':
+			case '\"':
+			case '[':
+			case ']':
+			case ' ':
+				fputc('\\', fout);
+			default:
+				fputc(*p, fout);
+		}
+}
+
+
+static void
+print_shell_text(const printTableContent *cont, FILE *fout)
+{
+	bool		opt_tuples_only = cont->opt->tuples_only;
+	unsigned int i;
+	const char *const * ptr;
+
+	if (cancel_pressed)
+		return;
+
+	if (cont->opt->start_table)
+	{
+		/* print headers */
+		if (!opt_tuples_only)
+		{
+			for (i = 0, ptr = cont->headers; i < cont->ncolumns; i++, ptr++)
+			{
+				if (i != 0)
+					fputc(' ', fout);
+				shell_escaped_print(*ptr, fout);
+			}
+			fputc('\n', fout);
+		}
+	}
+
+	/* print cells */
+	for (i = 0, ptr = cont->cells; *ptr; i++, ptr++)
+	{
+		shell_escaped_print(*ptr, fout);
+
+		if ((i + 1) % cont->ncolumns == 0)
+		{
+			fputc('\n', fout);
+			if (cancel_pressed)
+				break;
+		}
+		else
+			fputc(' ', fout);
+	}
+}
+
+
+static void
+print_shell_vertical(const printTableContent *cont, FILE *fout)
+{
+	bool		opt_tuples_only = cont->opt->tuples_only;
+	unsigned int i;
+	const char *const * ptr;
+	bool		is_first = true;
+
+	if (cancel_pressed)
+		return;
+
+	if (cont->opt->start_table)
+	{
+		/* print headers */
+		if (!opt_tuples_only)
+		{
+			fputs("( c l )\n", fout);
+		}
+	}
+
+	/* print records */
+	for (i = 0, ptr = cont->cells; *ptr; i++, ptr++)
+	{
+		/* new record */
+		if (i % cont->ncolumns == 0)
+		{
+			if (cancel_pressed)
+				break;
+
+			if (!is_first)
+				fputs(")\n( ", fout);
+			else
+			{
+				fputs("( ", fout);
+				is_first = false;
+			}
+		}
+
+		fputc('[', fout);
+		shell_escaped_print(cont->headers[i % cont->ncolumns], fout);
+		fputs("]=", fout);
+		shell_escaped_print(*ptr, fout);
+		fputc(' ', fout);
+	}
+
+	if (!is_first)
+		fputs(")\n", fout);
+}
+
 
 /********************************/
 /* Public functions		*/
@@ -2431,6 +2550,12 @@ printTable(const printTableContent *cont, FILE *fout, FILE *flog)
 			else
 				print_troff_ms_text(cont, fout);
 			break;
+		case PRINT_SHELL:
+			if (cont->opt->expanded == 1)
+				print_shell_vertical(cont, fout);
+			else
+				print_shell_text(cont, fout);
+			break;
 		default:
 			fprintf(stderr, _("invalid output format (internal error): %d"),
 					cont->opt->format);
diff --git a/src/bin/psql/print.h b/src/bin/psql/print.h
index 25adfc5..0c61fc5 100644
--- a/src/bin/psql/print.h
+++ b/src/bin/psql/print.h
@@ -19,7 +19,8 @@ enum printFormat
 	PRINT_WRAPPED,
 	PRINT_HTML,
 	PRINT_LATEX,
-	PRINT_TROFF_MS
+	PRINT_TROFF_MS,
+	PRINT_SHELL
 	/* add your favourite output format here ... */
 };
 
diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c
index a50e735..5bbe201 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.c
@@ -3007,7 +3007,7 @@ psql_completion(char *text, int start, int end)
 		{
 			static const char *const my_list[] =
 			{"unaligned", "aligned", "wrapped", "html", "latex",
-			"troff-ms", NULL};
+			"troff-ms", "shell", NULL};
 
 			COMPLETE_WITH_LIST_CS(my_list);
 		}