diff --git a/gdb/NEWS b/gdb/NEWS
index ba5ae520112..bde8116cf4a 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -63,6 +63,15 @@
     break foo thread 1 task 1
     watch var thread 2 task 3
 
+* The printf command now accepts a '%V' output format which will
+  format an expression just as the 'print' command would.  Print
+  options can be placed withing '[...]' after the '%V' to modify how
+  the value is printed.  E.g:
+    printf "%V", some_array
+    printf "%V[-array-indexes on]", some_array
+  Will print the array without, or with array indexes included, just
+  as the array would be printed by the 'print' command.
+
 * New commands
 
 maintenance print record-instruction [ N ]
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 92d99349dbf..54412177290 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -13131,6 +13131,10 @@
 
 @findex $_as_string@r{, convenience function}
 @item $_as_string(@var{value})
+This convenience function is considered deprecated, and could be
+removed from future versions of @value{GDBN}.  Use the @kbd{%V} format
+specifier instead (@pxref{%V Format Specifier}).
+
 Return the string representation of @var{value}.
 
 This function is useful to obtain the textual label (enumerator) of an
@@ -29025,6 +29029,36 @@
 printf "D32: %Hf - D64: %Df - D128: %DDf\n",1.2345df,1.2E10dd,1.2E1dl
 @end smallexample
 
+@anchor{%V Format Specifier}
+Additionally, @code{printf} supports a special @kbd{%V} output format.
+This format prints the string representation of an expression just as
+@value{GDBN} would produce with the standard @kbd{print} command
+(@pxref{Data, ,Examining Data}):
+
+@smallexample
+(@value{GDBP}) print array
+$1 = @{0, 1, 2, 3, 4, 5@}
+(@value{GDBP}) printf "Array is: %V\n", array
+Array is: @{0, 1, 2, 3, 4, 5@}
+@end smallexample
+
+Additionally, it is possible to include print options with the
+@kbd{%V} format by placing them in @kbd{[...]} immediately after the
+@kbd{%V}, like this:
+
+@smallexample
+(@value{GDBP}) printf "Array is: %V[-array-indexes on]\n", array
+Array is: @{[0] = 0, [1] = 1, [2] = 2, [3] = 3, [4] = 4, [5] = 5@}
+@end smallexample
+
+If you need to print a literal @kbd{[} directly after a @kbd{%V} then
+just include an empty print options list:
+
+@smallexample
+(@value{GDBP}) printf "Array is: %V[][Hello]\n", array
+Array is: @{0, 1, 2, 3, 4, 5@}[Hello]
+@end smallexample
+
 @anchor{eval}
 @kindex eval
 @item eval @var{template}, @var{expressions}@dots{}
diff --git a/gdb/printcmd.c b/gdb/printcmd.c
index dd92e31d31b..7783606fd9a 100644
--- a/gdb/printcmd.c
+++ b/gdb/printcmd.c
@@ -2733,7 +2733,7 @@ ui_printf (const char *arg, struct ui_file *stream)
   if (*s++ != '"')
     error (_("Bad format string, missing '\"'."));
 
-  format_pieces fpieces (&s);
+  format_pieces fpieces (&s, false, true);
 
   if (*s++ != '"')
     error (_("Bad format string, non-terminated '\"'."));
@@ -2875,6 +2875,36 @@ ui_printf (const char *arg, struct ui_file *stream)
 	  case ptr_arg:
 	    printf_pointer (stream, current_substring, val_args[i]);
 	    break;
+	  case value_arg:
+	    {
+	      value_print_options print_opts;
+	      get_user_print_options (&print_opts);
+
+	      if (current_substring[2] == '[')
+		{
+		  std::string args (&current_substring[3],
+				    strlen (&current_substring[3]) - 1);
+
+		  const char *args_ptr = args.c_str ();
+
+		  /* Override global settings with explicit options, if
+		     any.  */
+		  auto group
+		    = make_value_print_options_def_group (&print_opts);
+		  gdb::option::process_options
+		    (&args_ptr, gdb::option::PROCESS_OPTIONS_UNKNOWN_IS_ERROR,
+		     group);
+
+		  if (*args_ptr != '\0')
+		    error (_("unexpected content in print options: %s"),
+			     args_ptr);
+		}
+
+	      string_file buffer;
+	      print_formatted (val_args[i], 0, &print_opts, &buffer);
+	      gdb_puts (buffer.string ().c_str ());
+	    }
+	    break;
 	  case literal_piece:
 	    /* Print a portion of the format string that has no
 	       directives.  Note that this will not include any
diff --git a/gdb/testsuite/gdb.base/printcmds.c b/gdb/testsuite/gdb.base/printcmds.c
index 78291a2803c..fa3a62d6cdd 100644
--- a/gdb/testsuite/gdb.base/printcmds.c
+++ b/gdb/testsuite/gdb.base/printcmds.c
@@ -108,6 +108,7 @@ enum flag_enum
   FE_TWO_LEGACY = 0x02,
 };
 
+enum flag_enum one = FE_ONE;
 enum flag_enum three = (enum flag_enum) (FE_ONE | FE_TWO);
 
 /* Another enum considered as a "flag enum", but with no enumerator with value
@@ -152,6 +153,18 @@ struct some_struct
   }
 };
 
+/* This is used in the printf test.  */
+struct small_struct
+{
+  int a;
+  int b;
+  int c;
+} a_small_struct = {
+  1,
+  2,
+  3
+};
+
 /* The following variables are used for testing byte repeat sequences.
    The variable names are encoded: invalid_XYZ where:
    X = start
diff --git a/gdb/testsuite/gdb.base/printcmds.exp b/gdb/testsuite/gdb.base/printcmds.exp
index 21a2cad458c..10275720e8f 100644
--- a/gdb/testsuite/gdb.base/printcmds.exp
+++ b/gdb/testsuite/gdb.base/printcmds.exp
@@ -957,6 +957,32 @@ proc test_printf_with_dfp {} {
     gdb_test "printf \"%Hf %Hf\\n\",1.2df,1.3df" "1.2 1.3"
 }
 
+# Test the printf '%V' format.
+proc test_printf_V_format {} {
+    # Enums.
+    gdb_test {printf "%V\n", one} "FE_ONE"
+    gdb_test {printf "%V\n", three} "\\(FE_ONE \\| FE_TWO\\)"
+    gdb_test {printf "%V\n", flag_enum_without_zero} "0"
+    gdb_test {printf "%V\n", three_not_flag} "3"
+
+    # Arrays.
+    gdb_test {printf "%V\n", a1} "\\{2, 4, 6, 8, 10, 12, 14, 16, 18, 20\\}"
+    gdb_test {printf "%V[]\n", a1} "\\{2, 4, 6, 8, 10, 12, 14, 16, 18, 20\\}"
+    gdb_test {printf "%V[][]\n", a1} \
+	"\\{2, 4, 6, 8, 10, 12, 14, 16, 18, 20\\}\\\[\\\]"
+    gdb_test {printf "%V[-elements 3]\n", a1} "\\{2, 4, 6\\.\\.\\.\\}"
+    gdb_test {printf "%V[-elements 3][]\n", a1} \
+	"\\{2, 4, 6\\.\\.\\.\\}\\\[\\\]"
+    gdb_test {printf "%V[-elements 3 -array-indexes on]\n", a1} \
+	"\\{\\\[0\\\] = 2, \\\[1\\\] = 4, \\\[2\\\] = 6\\.\\.\\.\\}"
+
+    # Structures.
+    gdb_test {printf "%V\n", a_small_struct} \
+	"\\{a = 1, b = 2, c = 3\\}"
+    gdb_test {printf "%V[-pretty on]\n", a_small_struct} \
+	"\\{\r\n  a = 1,\r\n  b = 2,\r\n  c = 3\r\n\\}"
+}
+
 proc test_print_symbol {} {
     gdb_test_no_output "set print symbol on"
 
@@ -1086,7 +1112,6 @@ proc test_printf_convenience_var {prefix} {
     }
 }
 
-
 clean_restart
 
 gdb_test "print \$pc" "No registers\\."
@@ -1171,6 +1196,7 @@ test_print_array_constants
 test_print_enums
 test_printf
 test_printf_with_dfp
+test_printf_V_format
 test_print_symbol
 test_repeat_bytes
 test_radices
diff --git a/gdbsupport/format.cc b/gdbsupport/format.cc
index 19f37ec8e0c..2edeb5033be 100644
--- a/gdbsupport/format.cc
+++ b/gdbsupport/format.cc
@@ -20,7 +20,8 @@
 #include "common-defs.h"
 #include "format.h"
 
-format_pieces::format_pieces (const char **arg, bool gdb_extensions)
+format_pieces::format_pieces (const char **arg, bool gdb_extensions,
+			      bool value_extension)
 {
   const char *s;
   const char *string;
@@ -44,7 +45,7 @@ format_pieces::format_pieces (const char **arg, bool gdb_extensions)
       char *f = (char *) alloca (strlen (s) + 1);
       string = f;
 
-      while ((gdb_extensions || *s != '"') && *s != '\0')
+      while (*s != '"' && *s != '\0')
 	{
 	  int c = *s++;
 	  switch (c)
@@ -340,6 +341,27 @@ format_pieces::format_pieces (const char **arg, bool gdb_extensions)
 	      bad = 1;
 	    break;
 
+	  case 'V':
+	    if (!value_extension)
+	      error (_("Unrecognized format specifier '%c' in printf"), *f);
+
+	    if (lcount > 1 || seen_h || seen_big_h || seen_big_h
+		|| seen_big_d || seen_double_big_d || seen_size_t
+		|| seen_prec || seen_zero || seen_space || seen_plus)
+	      bad = 1;
+
+	    this_argclass = value_arg;
+
+	    if (f[1] == '[')
+	      {
+		const char *tmp;
+		for (tmp = f; *tmp != ']' && *tmp != '\0'; ++tmp)
+		  ;
+		if (*tmp == ']')
+		  f = tmp;
+	      }
+	    break;
+
 	  case '*':
 	    error (_("`*' not supported for precision or width in printf"));
 
diff --git a/gdbsupport/format.h b/gdbsupport/format.h
index 342b473c3ed..2af34ab9450 100644
--- a/gdbsupport/format.h
+++ b/gdbsupport/format.h
@@ -41,7 +41,8 @@ enum argclass
     int_arg, long_arg, long_long_arg, size_t_arg, ptr_arg,
     string_arg, wide_string_arg, wide_char_arg,
     double_arg, long_double_arg,
-    dec32float_arg, dec64float_arg, dec128float_arg
+    dec32float_arg, dec64float_arg, dec128float_arg,
+    value_arg
   };
 
 /* A format piece is a section of the format string that may include a
@@ -75,7 +76,8 @@ class format_pieces
 {
 public:
 
-  format_pieces (const char **arg, bool gdb_extensions = false);
+  format_pieces (const char **arg, bool gdb_extensions = false,
+		 bool value_extension = false);
   ~format_pieces () = default;
 
   DISABLE_COPY_AND_ASSIGN (format_pieces);
