Beefy Boxes and Bandwidth Generously Provided by pair Networks
XP is just a number
 
PerlMonks  

Perl Path Editor for Unix

by 5p1d3r (Hermit)
on Sep 13, 2005 at 20:33 UTC ( [id://491687]=CUFP: print w/replies, xml ) Need Help??

After doing this:
> export PATH=/my/new/applications/path
instead of this:
> export PATH=/my/new/applications/path:$PATH
one too many times I decided to do something about it.

Enter Perl and a small about of shell scripting and I have an interactive way of toggling directories in my path (or any "path like" variable) on or off and adding directories to the beginning and end of my path.

The first part of this little utility is the setpath.pl script:

#!/usr/bin/perl use strict; my $variableName = shift; if ( !$variableName ) { $variableName = "PATH"; } my $home = $ENV{HOME} || $ENV{LOGDIR} || (getpwuid($<))[7]; my $fName = "$home/.setpath.out"; if ( -e $fName ) { unlink ( $fName ) or die( "Unable to unlink previous $fName file ($!)!\n" ); } my @directories; if ( !exists $ENV{$variableName} ) { print( "No variable, $variableName, in the environment. Continue [Y/ +n]? " ); my $result; do { $result = <STDIN>; chomp( $result ); if ( !defined($result) || lc($result) eq "n" ) { exit( 1 ); } } while ( lc($result) ne "y" && $result ne "" ); } else { @directories = split( /:/, $ENV{$variableName} ); } my $dirty = 0; while( 1 ) { print( "Your current $variableName contains:\n" ); for ( my $i = 0; $i < scalar(@directories); ++$i ) { my $directory = $directories[ $i ]; my $state = "on"; if ( $directory =~ /\.off$/ ) { $state = "off"; $directory =~ s/\.off$//; } printf( " %2d) [%3s] %s\n", $i, $state, $directory ); } print( '----- Enter: t<n> to toggle a directory on/off, [a|A]<directory> to add a new directory (to front|end) q to quit. > ' ); my $commandLine = <STDIN>; if ( !defined($commandLine) ) { exit( 1 ); } chomp( $commandLine ); $commandLine =~ /(.)(.*)/; my ($command, $args) = ($1, $2); if ( $command eq "t" ) { if ( !defined($args) || $args eq "" || $args >= scalar(@directories) || $args < 0 ) { print( "$args is not a valid directory number (0 .. ", scalar(@d +irectories), ")\n\n" ); } else { $dirty = 1; if ( $directories[ $args ] =~ /\.off$/ ) { $directories[ $args ] =~ s/\.off$//; } else { $directories[ $args ] .= ".off"; } print( "\n" ); } } elsif ( $command eq "A" ) { push( @directories, $args ); $dirty = 1; } elsif ( $command eq "a" ) { splice( @directories, 0, 0, $args ); $dirty = 1; } elsif ( $command eq "q" ) { if ( $dirty ) { open( OUT, "> $fName" ) or die( "Unable to open $fName for writing ($!)!\n" ); print OUT ("export $variableName=", join(":", @directories), "\n +"); close( OUT ); exit 0; } else { print( "No changes made to $variableName.\n" ); exit( 1 ); } } }
Pretty much useless by itself as it doesn't actually change the path in any way. What it needs is a little bit of shell scripting to tie things together nicely. The following is for bash.
function setpath { /fully/qualified/path/to/setpath.pl "$1"; if [ $? = += 0 ]; then . $HOME/.setpath.out; echo "$1 updated."; fi };
Now I can safely play around with my PATH, LD_LIBRARY_PATH, etc... to my hearts content.

Replies are listed 'Best First'.
Re: Perl Path Editor for Unix
by graff (Chancellor) on Sep 14, 2005 at 04:58 UTC
    I like this idea, but I think that having to face an interactive dialog with a perl script via STDIN every time I use it would give me a headache.

    It wouldn't be that hard to adapt it so that @ARGV has all the information needed for just about every case -- e.g. consider a usage summary like this:

    setpath.pl [ENV_VAR] [:NEWPATH | NEWPATH: | -[-]OLDPATH] Default ENV_VAR is PATH :NEWPATH appends ":NEWPATH" at end of ENV_VAR NEWPATH: prepends "NEWPATH:" at start of ENV_VAR -OLDPATH deletes first instance of "OLDPATH" from ENV_VAR --OLDPATH deletes all instances of "OLDPATH" from ENV_VAR

    Conceivably, you could handle multiple path args to be added or removed from the given variable in a single command line. For that matter, you could even do multiple variables on one command line:

    setpath.pl VAR1 :NEWPATH VAR2 OTHERPATH: VAR3 --BADPATH # # shell function would be: function setpath { /full/path/to/setpath.pl $*; if ... };

    Maybe "chpath" would be a better name for the shell function.

    update: Here is how I would do the @ARGV-based approach -- just a first attempt (I probably need to test more, esp. the regex for matching "valid" path args), but when combined with the appropriate shell function to use it, I think it would be serviceable for most needs. To me, it just seems a lot cleaner without all that prompting and reading user input on STDIN.

      Not quite matching your spec but here you go. I implemented as much as I would use...

      my %path; my ($high, $low) = (0,0); for (split /:/, $ENV{PATH} ){ next if exists $path{$_}; # remove duplicates $path{$_} = $high; ++$high; } for (@ARGV){ /^-(.*)/ and do {delete $path{$1} if exists $path{$1}; next;}; /^\+\+(.*)/ and do {--$low; $path{$1}=$low unless exists $path{$1}; +next;}; /^\+(.*)/ and do {++$high; $path{$1}=$high unless exists $path{$1}; +next;}; } print join ":", sort {$path{$a} <=> $path{$b} } keys %path; print "\n";

      --
      Murray Barton
      Do not seek to follow in the footsteps of the wise. Seek what they sought. -Basho

      That's a nice variation. I think the two could be combined with arguments used by default but falling back to interactive behavior in their absence.

      The regex path matching on a remove or remove all seems a little off:

      > export PATHTEST=/foo:/foobar:/foo/bar
      > setpath.pl --/foo
      > more .setpath.out
      export PATHTEST=:bar:/bar/
      
      >
      
      This works if you append a ':' to the path you're removing (which I guess the script could do for you). It might be nice to add some sort of wild carding to the removals so you could remove every directory rooted at /foo.

      I initially wrote mine to do removes but for my usage I prefer to temporarily toggle directories on/off. The tools developers I work with chose executable names that match those of a number of gnu tools I make use of so on occassion I need to temporaily remove one or other directory from my PATH.

Re: Perl Path Editor for Unix
by bluto (Curate) on Sep 13, 2005 at 21:23 UTC
    FWIW, zsh's vared command ("vared PATH") is really nice for editing arbitrary env variables.
Re: Perl Path Editor for Unix
by QM (Parson) on Sep 13, 2005 at 20:39 UTC
    I had a similar problem on really old *nixes, where I had to suck in setups to run in various production environments. The old *nix couldn't take path variables over some limit, and would just chop it off, leaving only part of the necessary path, and a useless dangling root tip.

    I had to save off the original path, suck in the new stuff, then cut out duplicates and other known useless bits, and set the path with the new shorter string. I think I just used a Perl script as a filter in backquotes in the shell, and all was (mostly) well...until I tried tcsh, but that's another story.

    -QM
    --
    Quantum Mechanics: The dreams stuff is made of

Re: Perl Path Editor for Unix
by duelafn (Parson) on Sep 15, 2005 at 16:00 UTC

    This sort of thing is not too difficult to do in pure shell commands. Here's some code I picked up a while back after a quick google search. (original source)

    Example Usage

    add_path $HOME/bin add_path $HOME/perl PERL5LIB

    The functions

    # @(#)Copyright (c) 1991 Simon J. Gerraty # # This file is provided in the hope that it will # be of use. There is absolutely NO WARRANTY. # Permission to copy, redistribute or otherwise # use this file is hereby granted provided that # the above copyright notice and this notice are # left intact. # # Modified by Dean Serenevy (2005) for more robust quoting # is $1 missing from $2 (or PATH) ? no_path() { eval "case \":\$${2-PATH}:\" in *:\"$1\":*) return 1;; *) +return 0;; esac"; } # if $1 exists and is not in path, append it add_path() { [ -d ${1:-.} ] && no_path $* && eval ${2:-PATH}="\$${2:-P +ATH}:$1"; } # if $1 exists and is not in path, prepend it pre_path() { [ -d ${1:-.} ] && no_path $* && eval ${2:-PATH}="$1:\$${2 +:-PATH}"; } # if $1 is in path, remove it del_path() { no_path $* || eval ${2:-PATH}=`eval echo :'$'${2:-PATH}: +| sed -e "s;:$1:;:;g" -e "s;^:;;" -e "s;:\$;;"`; }

    Good Day,
        Dean

Re: Perl Path Editor for Unix
by scollyer (Sexton) on Sep 28, 2005 at 14:26 UTC
    For those interested in a non-Perl-based approach to this problem, I have a set of bash/ksh functions

    addpath
    delpath
    uniqpath
    edpath

    that allow you to manipulate path variables in various ways. (which are idempotent addition of paths, deletion of paths based on regex match, path uniquification, and path editing via your favourite editor)

    They can be downloaded from http://www.netspinner.co.uk/Downloads/pathfunc.tgz

    They're in the public domain so you can do what you like with them.

    Steve Collyer

      http://www.netspinner.co.uk/Downloads/pathfunc.tgz appears to be dead. Any idea where this code lives today?

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: CUFP [id://491687]
Approved by socketdave
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others wandering the Monastery: (2)
As of 2024-04-20 03:18 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found