mirror of
https://github.com/libguestfs/libguestfs.git
synced 2026-03-22 07:03:38 +00:00
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.
This commit is contained in:
57
fish/fish.c
57
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)
|
||||
{
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user