[Libguestfs] [PATCH 4/4] fish: <! cmd executes a shell command and inlines the resulting commands.

Richard W.M. Jones rjones at redhat.com
Tue Jan 18 12:10:45 UTC 2011


-- 
Richard Jones, Virtualization Group, Red Hat http://people.redhat.com/~rjones
New in Fedora 11: Fedora Windows cross-compiler. Compile Windows
programs, test, and build Windows installers. Over 70 libraries supprt'd
http://fedoraproject.org/wiki/MinGW http://www.annexia.org/fedora_mingw
-------------- next part --------------
>From 487cc792a77308c1544b2ed8b1882e9a2fafeb09 Mon Sep 17 00:00:00 2001
From: Richard W.M. Jones <rjones at redhat.com>
Date: Tue, 18 Jan 2011 11:46:03 +0000
Subject: [PATCH 4/4] fish: <! cmd executes a shell command and inlines the resulting commands.

The new guestfish construct "<! cmd" executes the shell command
"cmd", and then anything printed to stdout by "cmd" is parsed
and executed as a guestfish command.

This allows some very hairy shell scripting with guestfish.
---
 fish/fish.c        |   57 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 fish/guestfish.pod |   22 ++++++++++++++++++++
 2 files changed, 79 insertions(+), 0 deletions(-)

diff --git a/fish/fish.c b/fish/fish.c
index 566d769..4a960dc 100644
--- a/fish/fish.c
+++ b/fish/fish.c
@@ -61,6 +61,7 @@ static void shell_script (void);
 static void script (int prompt);
 static void cmdline (char *argv[], int optind, int argc);
 static struct parsed_command parse_command_line (char *buf, int *exit_on_error_rtn);
+static int execute_and_inline (const char *cmd, int exit_on_error);
 static void initialize_readline (void);
 static void cleanup_readline (void);
 #ifdef HAVE_LIBREADLINE
@@ -708,6 +709,18 @@ parse_command_line (char *buf, int *exit_on_error_rtn)
     return pcmd;
   }
 
+  /* If the next two characters are "<!" then pass the command to
+   * popen(3), read the result and execute it as guestfish commands.
+   */
+  if (buf[0] == '<' && buf[1] == '!') {
+    int r = execute_and_inline (&buf[2], *exit_on_error_rtn);
+    if (r == -1)
+      pcmd.status = -1;
+    else
+      pcmd.status = 0;
+    return pcmd;
+  }
+
   /* If the next character is '-' allow the command to fail without
    * exiting on error (just for this one command though).
    */
@@ -823,6 +836,50 @@ parse_command_line (char *buf, int *exit_on_error_rtn)
   return pcmd;
 }
 
+/* Used to handle "<!" (execute command and inline result). */
+static int
+execute_and_inline (const char *cmd, int global_exit_on_error)
+{
+  FILE *pp;
+  char *line = NULL;
+  size_t len = 0;
+  ssize_t n;
+  int exit_on_error;
+  struct parsed_command pcmd;
+
+  pp = popen (cmd, "r");
+  if (!pp) {
+    perror ("popen");
+    return -1;
+  }
+
+  while ((n = getline (&line, &len, pp)) != -1) {
+    exit_on_error = global_exit_on_error;
+
+    /* Chomp final line ending which parse_command_line would not expect. */
+    if (n > 0 && line[n-1] == '\n')
+      line[n-1] = '\0';
+
+    pcmd = parse_command_line (line, &exit_on_error);
+    if (pcmd.status == -1 && exit_on_error)
+      exit (EXIT_FAILURE);
+    if (pcmd.status == 1) {
+      if (issue_command (pcmd.cmd, pcmd.argv, pcmd.pipe, exit_on_error) == -1) {
+        if (exit_on_error) exit (EXIT_FAILURE);
+      }
+    }
+  }
+
+  free (line);
+
+  if (pclose (pp) == -1) {
+    perror ("pclose");
+    return -1;
+  }
+
+  return 0;
+}
+
 static void
 cmdline (char *argv[], int optind, int argc)
 {
diff --git a/fish/guestfish.pod b/fish/guestfish.pod
index 21f25bd..ece3d29 100644
--- a/fish/guestfish.pod
+++ b/fish/guestfish.pod
@@ -676,6 +676,28 @@ C<local/remote-data.tar.gz>.  (See C<tgz-out>).
 To change the local directory, use the C<lcd> command.  C<!cd> will
 have no effect, due to the way that subprocesses work in Unix.
 
+=head2 LOCAL COMMANDS WITH INLINE EXECUTION
+
+If a line starts with I<E<lt>!> then the command is executed (as for
+I<!>), but subsequently any output (stdout) of the command is parsed
+and executed as guestfish commands.
+
+Thus you can use shell script to construct arbitrary guestfish
+commands which are then parsed by guestfish.
+
+For example it is tedious to create a sequence of files
+(eg. C</foo.1> through C</foo.100>) using guestfish commands
+alone.  However this is simple if we use a shell script to
+create the guestfish commands for us:
+
+ <! for n in `seq 1 100`; do echo write /foo.$n $n; done
+
+When using guestfish interactively it can be helpful to just run the
+shell script first (ie. remove the initial C<E<lt>> character so it is
+just an ordinary I<!> local command), see what guestfish commands it
+would run, and when you are happy with those prepend the C<E<lt>>
+character to run the guestfish commands for real.
+
 =head1 PIPES
 
 Use C<command E<lt>spaceE<gt> | command> to pipe the output of the
-- 
1.7.3.4



More information about the Libguestfs mailing list