Last active
December 29, 2024 03:54
-
-
Save eqhmcow/954c9749a8050e9f43eb9c5c5f2f351e to your computer and use it in GitHub Desktop.
quick and dirty ssh then sudo then run commands in a loop w/ IPC::Run and Parallel::ForkManager
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/perl | |
use strict; | |
use warnings; | |
$|++; | |
use IPC::Run qw/start pump finish timeout/; | |
use Parallel::ForkManager; | |
use Getopt::Long; | |
my (@servers, $servers, $server_file, $command_file, $output_dir, $quiet); | |
$servers = ''; | |
$server_file = ''; | |
$command_file = ''; | |
$output_dir = ''; | |
GetOptions( | |
's|servers=s' => \$servers, | |
'sf|server-file=s' => \$server_file, | |
'cf|command-file=s' => \$command_file, | |
'output-dir=s' => \$output_dir, | |
'quiet' => \$quiet, | |
'help' => sub { usage(); exit 0 }, | |
) or die "Error parsing arguments"; | |
# must specify commands or command file but not both | |
die "Cannot specify both commands and command-file arguments" | |
if (scalar @ARGV and $command_file ne ''); | |
# commands from command line | |
my $cmd = "@ARGV"; | |
# or from file | |
if ($command_file ne '') { | |
@ARGV = $command_file; | |
while (<>) { | |
$cmd .= $_; | |
} | |
} | |
die "must specify a command" | |
if ($cmd =~ m/^\s*$/); | |
# fix up cmd; newlines -> semicolons | |
$cmd =~ s/\s*;\s*\n/ ; /g; | |
$cmd =~ s/\n/ ; /g; | |
$cmd =~ s/^\s*;\s*//; | |
# must specify servers or server-file but not both | |
die "Cannot specify both servers and server-file arguments" | |
if ($servers ne '' and $server_file ne ''); | |
# servers from command line | |
if ($servers ne '') { | |
$servers =~ s/^\s*//; | |
$servers =~ s/\s*$//; | |
$servers =~ s/,+/,/g; | |
@servers = split(m/\s*,\s*|\s+/, $servers); | |
} | |
# or from file | |
if ($server_file ne '') { | |
@ARGV = $server_file; | |
while (<>) { | |
chomp; | |
next if m/^\s*#/; | |
s/^\s*//; | |
s/\s*$//; | |
next if m/^\s*$/; | |
push @servers, $_; | |
} | |
} | |
die "must specify at least one server" | |
unless scalar @servers; | |
warn "running $cmd against @servers servers"; | |
warn "is that ok"; | |
$_ = readline STDIN; | |
die unless m/^y/; | |
my $pm = new Parallel::ForkManager(256); | |
foreach my $server (@servers) { | |
my $pid = $pm->start() and next; | |
my $b = "[$server]"; | |
print "$b ssh to $server\n"; | |
my $out_fh; | |
if ($output_dir ne '') { | |
my $output_file = "$output_dir/$server.out"; | |
open($out_fh, '>', $output_file) or die "Can't open $output_file: $!"; | |
} | |
my $h = start([ 'ssh', '-qt', $server, $cmd ], '<pty<', \my $in, '>pty>', \my $output); | |
while ($h->pump()) { | |
if ($output =~ m/\n/) { | |
$output =~ s/^(.*\n)//; | |
my $d = $1; | |
$d =~ s/\015\012/\012/g; | |
if ($output_dir ne '') { | |
print $out_fh $d; | |
} | |
unless ($quiet) { | |
chomp $d; | |
$d =~ s/^/$b /g; | |
$d =~ s/\n/\n$b /g; | |
print "$d\n"; | |
} | |
} | |
} | |
$h->finish(); | |
$output =~ s/\015\012/\012/g; | |
if ($output_dir ne '') { | |
print $out_fh $output; | |
close $out_fh; | |
} | |
unless ($quiet) { | |
chomp $output; | |
$output =~ s/^/$b /g; | |
$output =~ s/\n/\n$b /g; | |
print "$output\n"; | |
} | |
$pm->finish(); | |
} | |
$pm->wait_all_children(); | |
sub usage { | |
print <<"END_USAGE"; | |
Usage: $0 [options] [command] | |
Options: | |
-s or --servers - List of servers | |
--sf or --server-file - File to read for list of servers | |
--cf or --command-file - File to read for list of commands | |
--output-dir - Directory to write output to | |
--quiet - Don't print output to stdout | |
--help - Show this help message | |
END_USAGE | |
} | |
exit 0; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I know this name.