=pod =head1 USING PERL TO ADMINISTER LINUX SORCERER I am a confirmed Sorcerer Linux SA ( see L ). For several years now I have been steadily building up my base of Sorcerer installations ( eat your heart out Billy G. ), and have found it to be fast, lean, reliable, and down right fun to maintain. My users are happy, so I am happy. I have been seriously using Linux for about four years now, having come from the Microsoft world. Before I started using and administrating Linux I was one of Microsoft's minions and thought if the software didn't cost a lot of money and have the funny 'window' picture on the box, it was really just for amateurs and geeks. Not so, as I and many of you have discovered. Perl, for me, is a different story. I have been a 'serious' perl monger for almost ten years, reaching back to my Windows days. I am still more productive in perl than bash. When I started writing perl scripts to help me administer Sorcerer, I ran into the problem that the environment variables on which Sorcerer so heavily depends, are 'sourced' into the bash environment from B. It is very hard to access them from perl. And variables that are bash arrays, good luck! Try this: # . /etc/sorcery/config && \ perl -e 'print "$_ => $ENV{$_}\n" for sort keys %ENV' Look at the result. Yep, nary a Sorcerer variable appears. Now try: # . /etc/sorcery/config # export GRIMOIRE # perl -e 'print "$_ => $ENV{$_}\n" for sort keys %ENV' Yep, GRIMORE appears in the output. So I had to write bash scripts to source /etc/sorcery/config, export the variables I wanted to use, and run my perl script. That got old quickly. And I still didn't have a good way to get to bash arrays. I have developed a solution, based on the CPAN module B. Please take a moment to glance at the documentation before continuing. =head1 A SIMPLE EXAMPLE I have written a small, more or less useless, perl script using Env::Bash with Sorcerer. The source is B in the module's distribution. The script can list the spells in the grimoire and/or display spell details: perl scripts/show-spell [-d] [-l] [ ...] where: -d shows internal debugging information -l lists spells in the grimoire display details of one or more spells. If the script is started without any arguments, spell linux is displayed. Here is a blow-by-blow description of show-spell.pl =head2 Starting #!/usr/bin/perl use warnings; use strict; use Env::Bash; use Data::Dumper; use Getopt::Std; my %opt; unless (getopts('dl', \%opt)) { usage(); } Pretty standard; options are handled too. =head2 tie a HASH to the Environment # tie a hash to /etc/sorcery/config, no ForceArray my %env = (); tie %env, "Env::Bash", Source => "/etc/sorcery/config", Debug => $opt{d}; This is the easiest way to interface to Env::Bash ( there is a simple, standard interface and an oo interface which are fully discussed in the module's documentation ). The tie statement says to interface to the environment through hash %env, with the option Source ( the script or list of scripts to source ), and conditionally set internal debugging. More on ForceArray below. =head2 Find the GRIMOIRE directory # find the GRIMOIRE directory my $grimoire = $env{GRIMOIRE} || die "cannot find GRIMOIRE\n"; This is the first real use of the module; the grimoire directory ( defined in /etc/sorcery/config ), is returned. =head2 Perl code to list the GRIMOIRE and display spells # display spells in the grimoire if option -l if( $opt{l} ) { print "---spells in grimoire-------------------------\n"; my @spells = (); for my $spell( <$grimoire/*> ) { $spell =~ s,.*/,,; push @spells, $spell; } print "$_\n" for sort @spells; } # show spells on command line, or linux if none given show_spell( $_ ) for @ARGV; show_spell( 'linux' ) unless @ARGV || $opt{l}; Just perl code. =head2 show-spell subroutine sub show_spell { my $spell = shift; # find the spell and DETAILS unless( -e "$grimoire/$spell" ) { warn "Spell '$spell' not found.\n"; return; } my $details = -d _ ? "$grimoire/$spell/DETAILS" : "$grimoire/$spell"; unless( -e "$details" ) { warn "Spell '$spell' DETAILS not found.\n"; return; } # tie a hash to /etc/sorcery/config and DETAILS w/ForceArray my %env = (); tie %env, "Env::Bash", [], Source => [ "/etc/sorcery/config", $details ], Debug => $opt{d}; print "---$spell-------------------------------------\n"; show_detail( VERSION => \%env ); show_detail( CATEGORY => \%env ); show_detail( ATTRIBUTE => \%env ); show_detail( SOURCE => \%env ); show_detail( URL => \%env ); show_detail( HOMEPAGE => \%env ); show_detail( REQ => \%env ); show_detail( PROTECT => \%env ); show_detail( ESTIMATE => \%env ); show_detail( DESC => \%env ); } Here we do another tie for two reasons: 1) we want to source the spell's DETAILS script as well as /etc/sorcery/config, and 2) we want to be able to access any variables that are bash arrays - like VERSION. The Source option to the tie is a list reference - that's how Env::Bash knows how to use more than one source script. To access the environment, the module constructs a mini bash script, which in this case would be something like: #!/bin/sh . /etc/sorcery/config;. /var/state/sorcery/grimoire//DETAILS;set The script is run by forking via backtics and the set output is parsed to get a list of environment names. Turn on debugging ( -d ) if you really want to see the the script. To access bash arrays, the ForceArray option ( shortcut: [] ) is specified. The module again creates and runs a bash script that returns all elements of any bash arrays; the results are stored in the tied hash as an array reference, B. Again, for the more curious, look at what's happening by turning on debugging. =head2 Show spell details sub show_detail { my( $name, $env ) = @_; # get the requested detail ( return is an array because the # the $env hash was tied with ForceArray ( [] ). my $values = $env->{$name}; # print each detail my $eq = '='; for my $value( @$values ) { $value = join( "\n".' ' x 14, split /\n/, $value ) if $value =~ /\n/s; printf "%12s%1s\"%s\"\n", $name, $eq, $value; $name = $eq = ''; } } Now we get the variable from the tied hash. As mentioned above, each hash element is an array reference ( because ForceArray was specified ). The resulting values are manipulated and printed. =head2 usage subroutine sub usage { my $progname = $0; $progname =~ s,.*/,,; # only basename left in progname die "Usage: $progname [-d] [-l] [ ...]\n"; } =head2 Summary OK, so you now have a script the demonstrates the use of Env::Bash. I admit it's slower and much harder than a few bash ls and cat commands, but that's not the point. Env::Bash has proved to open up Sorcerer to perl; I have written some perl scripts that are, in fact, useful; I will try to get them posted my my home page ( currently not available as I write this - December 2004 ) and let you know on the Sorcerer list. I hope you can find some use for Env::Bash. =head1 AUTHOR Beau E. Cox, Ebeaucox@hawaii.rr.comE. =head1 COPYRIGHT AND LICENSE Copyright (C) 2004 by Beau E. Cox. This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself, either Perl version 5.8.6 or, at your option, any later version of Perl 5 you may have available. =cut