diff options
author | Richard W.M. Jones <rjones@redhat.com> | 2011-01-18 11:46:03 +0000 |
---|---|---|
committer | Richard W.M. Jones <rjones@redhat.com> | 2011-01-18 13:16:28 +0000 |
commit | c3887285abbb3606822626ba396a51cb5df80d43 (patch) | |
tree | 6710d2b44d0cd9268ad78fc7718b7c4483a63575 /fish | |
parent | 61a4db138e4f85033c655bf6b24df0949683c24c (diff) | |
download | libguestfs-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.c | 57 | ||||
-rw-r--r-- | fish/guestfish.pod | 26 |
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 |