Category: | Utility Scripts |
Author/Contact Info | |
Description: | This script emulates the shell functions pushd, popd, and dirs. It stores the directory stacks in a data file ~/.ppushd.dat. It needs some help from the shell because otherwise it won't be able to change the directory. Save the script with the name ppushd-bin somewhere in your path, make it executable, and add the following commands to your bashrc to be able to use it.
This script is dedicated to demerphq. Update: it might be better to implement this fully as a shell script without perl. Update 2016-01-06: See also "I'm trying to write a script that will change directory (or set a variable), but after the script finishes, I'm back where I started (or my variable isn't set)!" in Greg's bash FAQ |
#!/usr/bin/env perl # pushdir implementation in perl # usage: # cd "`ppushd-bin command args`" # where command can be one of: init, dirs, pushd, popd. init clears t +he dirstack. # the dirstacks are associated to the pid of the shell, so you should +call # ppushd-bin init >/dev/null # from every shell before you use the program. # I recommend adding the following snippets to your bashrc to use this +: # pdirs() { cd "`ppushd-bin dirs "$@"`"; } # pinit() { cd "`ppushd-bin init "$@"`"; } # ppushd() { cd "`ppushd-bin pushd "$@"`"; } # ppopd() { cd "`ppushd-bin popd "$@"`"; } # pinit # you may have to adapt these to work in other shells, # eg. converting the functions to tcsh aliases # bugs: # might not work for dirs with newlines in them. # the -nclpv switches or numeric arguments aren't implemented. # might work differently for symlinks than the shell does. # not well tested. use warnings; use strict; use Fcntl "LOCK_EX"; use Cwd; my $ppid = getppid(); 1 <= @ARGV or die q{usage: cd "`ppushd-bin command args`"\nwhere command is one +of: init, dirs, pushd, popd\n}; my($cmd, @arg) = @ARGV; my $wd = getcwd(); defined($ENV{"HOME"}) or die "HOME"; my $datname = $ENV{"HOME"} . "/.ppushd.dat"; my $D; open $D, "+<", $datname or $!{"ENOENT"} and open $D, "+>", $datname or die "error: cannot open data file: $!"; flock $D, LOCK_EX() or die "error: cannot lock data file exclusiviely: $!"; my @dat = <$D>; my $dat = ""; for my $n (0 .. @dat - 1) { $dat[$n] =~ /^(-?\d+)\s(.+)/ or next; $ppid == $1 and do { $dat = $2; splice @dat, $n, 1; last; }; } my @stk = map { pack "H*", $_ } $dat =~ /(\S+)/g; if ($cmd eq "init") { @arg and warn "warning: no arguments expected to init"; @stk = (); } elsif ($cmd eq "dirs") { print STDERR join(" ", @stk, $wd), "\n"; } elsif ($cmd eq "pushd") { if (0 == @arg) { 1 <= @stk or die "error: directory stack empty when trying to swap top +elements"; ($stk[-1], $wd) = ($wd, $stk[-1]); } elsif (1 == @arg) { push @stk, $wd; chdir $arg[0] or die "cannot chdir: $!"; $wd = getcwd(); } else { die "error: at most one argument expected after pushd"; } } elsif ($cmd eq "popd") { 1 <= @stk or die "error: directory stack empty when trying to popd"; @arg and die "error: no arguments expected to popd"; $wd = pop @stk; } else { die "error: invalid command"; } print $wd or die "error: cannot print wd"; 0 != @stk and push @dat, $ppid . " " . join(" ", map { unpack "H*", $_ } @stk) . + "\n"; seek $D, 0, 0 or die "error: cannot seek data file\n"; sub quickdump { die "as you might have just lost the dirstacks of all shells, here +'s a quick dump:\n@dat"; } print $D @dat or do { warn "error: cannot write to data file: $!"; die quickdump(); }; 0 <= (my $o = tell $D) or do { warn "error: cannot tell data file: $!"; die quickdump(); }; truncate $D, $o or do { warn "error: cannot tell data file: $!"; die quickdump(); }; close $D or do { warn "error: cannot tell data file: $!"; die quickdump(); }; __END__ |
|
---|