summaryrefslogtreecommitdiffstats
path: root/fish
diff options
context:
space:
mode:
authorRichard W.M. Jones <rjones@redhat.com>2011-01-18 11:46:03 +0000
committerRichard W.M. Jones <rjones@redhat.com>2011-01-18 13:16:28 +0000
commitc3887285abbb3606822626ba396a51cb5df80d43 (patch)
tree6710d2b44d0cd9268ad78fc7718b7c4483a63575 /fish
parent61a4db138e4f85033c655bf6b24df0949683c24c (diff)
downloadlibguestfs-c3887285abbb3606822626ba396a51cb5df80d43.tar.gz
libguestfs-c3887285abbb3606822626ba396a51cb5df80d43.tar.xz
libguestfs-c3887285abbb3606822626ba396a51cb5df80d43.zip
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.
Diffstat (limited to 'fish')
-rw-r--r--fish/fish.c57
-rw-r--r--fish/guestfish.pod26
2 files changed, 83 insertions, 0 deletions
diff --git a/fish/fish.c b/fish/fish.c
index 566d769f..4a960dc0 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 21f25bd8..5ec6689e 100644
--- a/fish/guestfish.pod
+++ b/fish/guestfish.pod
@@ -676,6 +676,32 @@ 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 shell command is executed (as
+for I<!>), but subsequently any output (stdout) of the shell 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
+
+or with names like C</foo.001>:
+
+ <! for n in `seq 1 100`; do printf "write /foo.%03d %d\n" $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