Last active
June 15, 2017 16:38
-
-
Save thealexbaron/45fb567e2c1349983865adbfe6945b57 to your computer and use it in GitHub Desktop.
SSH Config Helper
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 Scalar::Util qw(looks_like_number); | |
use Data::Dumper; | |
# update https://gist.github.com/thealexbaron/45fb567e2c1349983865adbfe6945b57 when we update this file | |
# TODO: List possible tags | |
# TODO: add support for tag tab completion | |
=pod | |
Add tags and notes to your config blocks like this: | |
Host euweb101 | |
HostName 176.34.140.11 | |
IdentityFile ~/keys/ireland.pem | |
User ubuntu | |
#tags rsis-db-schema web ireland | |
#note this is neato note | |
Then you can list and filter with tags like this: | |
ssh-helper.pl web virginia | |
=cut | |
my $config_path = $ENV{'HOME'} . '/.ssh/config'; | |
my $ssh_config = read_config($config_path); | |
render_options($ssh_config, \@ARGV); | |
sub read_config { | |
my ($config_path) = @_; | |
open(my $fh, '<:encoding(UTF-8)', $config_path) or die "Could not open file '$config_path' $!"; | |
my @ssh_config; | |
while (my $row = <$fh>) { | |
chomp $row; | |
my ($key, $value) = split(' ', $row, 2); | |
if (!$key) { next; } | |
if (substr($key, 0) eq '#' && $key ne '#tags' && $key ne '#note') { next; } | |
if ($key eq 'Host') { | |
my %hash = ($key => $value); | |
push(@ssh_config, \%hash); | |
} | |
elsif ($key eq '#tags') { | |
$ssh_config[scalar @ssh_config - 1]{$key} = [ split(' ', $value) ]; | |
} | |
elsif ($key eq '#note') { | |
$ssh_config[scalar @ssh_config - 1]{$key} = $value; | |
} | |
else { | |
$ssh_config[scalar @ssh_config - 1]{$key} = $value; | |
} | |
} | |
return [ map{ $_->{'#tags'} = [] if !defined $_->{'#tags'}; $_; } @ssh_config ]; | |
} | |
sub generate_table_data { | |
my ($ssh_config) = @_; | |
my @table_rows = ([' # ', 'connection aliases', 'tags', 'notes']); | |
my $idx = 0; | |
foreach my $block (@$ssh_config) { | |
$idx += 1; | |
push(@table_rows, ["[$idx]", $block->{'Host'}, join(', ', @{ $block->{'#tags'} }), $block->{'#note'}]); | |
} | |
return \@table_rows; | |
} | |
sub render_table { | |
my ($table_data) = @_; | |
# first determine the widths of the columns | |
my @char_widths = (); | |
foreach my $row (@$table_data) { | |
my $idx = 0; | |
foreach my $field (@$row) { | |
$field = $field || ''; | |
my $field_length = length($field); | |
if (!$char_widths[$idx] || $char_widths[$idx] < $field_length) { | |
$char_widths[$idx] = $field_length; | |
} | |
$idx += 1; | |
} | |
} | |
my $rowIdx = 0; | |
foreach my $row (@$table_data) { | |
my $fieldIdx = 0; | |
foreach my $field (@$row) { | |
$field = $field || ''; | |
my $field_length = length($field); | |
my $add_spaces = $char_widths[$fieldIdx] - $field_length; | |
print $field . (' ' x $add_spaces) . ' '; | |
$fieldIdx += 1; | |
} | |
if ($rowIdx == 0) { | |
print "\n"; | |
foreach my $width (@char_widths) { | |
print '=' x $width . ' '; | |
} | |
} | |
$rowIdx += 1; | |
print "\n"; | |
} | |
} | |
sub render_options { | |
my ($ssh_config, $filters) = @_; | |
my %unique_filtered_ssh_config = (); | |
foreach my $block (@$ssh_config) { | |
my $matches_tags = 1; | |
if (scalar $filters != 0) { | |
foreach my $tag (@$filters) { | |
if ((scalar grep { $_ eq $tag } @{ $block->{'#tags'} }) < 1) { | |
$matches_tags = 0; | |
} | |
} | |
} | |
if ($matches_tags) { | |
$unique_filtered_ssh_config{$block->{'Host'}} = $block; | |
} | |
} | |
foreach my $block (@$ssh_config) { | |
my $note = $block->{'#note'} || $block->{'#notes'}; | |
if ($note) { | |
my $matches_tags = 0; | |
foreach my $word (@$filters) { | |
if ((scalar grep(/\Q$word/, $note)) > 0) { | |
$matches_tags = 1; | |
} | |
} | |
if ($matches_tags) { | |
$unique_filtered_ssh_config{$block->{'Host'}} = $block; | |
} | |
} | |
} | |
my @filtered_ssh_config = (); | |
foreach my $block (values %unique_filtered_ssh_config) { | |
push(@filtered_ssh_config, $block); | |
} | |
if (!scalar @filtered_ssh_config) { | |
print "No matches found with those tags or notes.\n"; | |
exit(); | |
} | |
my @host_sorted_filtered_ssh_config = sort { $a->{'Host'} cmp $b->{'Host'} } @filtered_ssh_config; | |
my $table_data = generate_table_data(\@host_sorted_filtered_ssh_config); | |
render_table($table_data); | |
if (scalar @ARGV == 0) { print "\nFilter results by entering tags as arguments.\n"; } | |
my $selected_number; | |
if (scalar @host_sorted_filtered_ssh_config == 1) { | |
print "\nOnly one option available, automatically using that one.\n"; | |
$selected_number = 1; | |
} | |
else { | |
print "\nHere's a list of possibilities. Which would you like to connect to?\n"; | |
$selected_number = <STDIN>; | |
chomp $selected_number; | |
} | |
if (!looks_like_number($selected_number)) { | |
print qq|\neh... "$selected_number" isn't a number. Maybe you wanna filter by tags?\n\n|; | |
exit(render_options($ssh_config, [$selected_number])); | |
} | |
elsif (!$host_sorted_filtered_ssh_config[$selected_number - 1]) { | |
print qq|eh... "$selected_number" isn't a number in this list.\n|; | |
} | |
else { | |
my $host = $host_sorted_filtered_ssh_config[$selected_number - 1]{'Host'}; | |
print "cool, connecting to $host...\n"; | |
exec("ssh $host") or print STDERR "couldn't exec ssh $host: $!"; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment