Beefy Boxes and Bandwidth Generously Provided by pair Networks
go ahead... be a heretic
 
PerlMonks  

Ppushd - emulate pushd and popd commands of the shell

by ambrus (Abbot)
on Feb 28, 2006 at 19:32 UTC ( [id://533470]=sourcecode: print w/replies, xml ) Need Help??
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.

pdirs() { cd "`ppushd-bin dirs "$@"`"; } pinit() { cd "`ppushd-bin init "$@"`"; } ppushd() { cd "`ppushd-bin pushd "$@"`"; } ppopd() { cd "`ppushd-bin popd "$@"`"; } pinit

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__

Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others drinking their drinks and smoking their pipes about the Monastery: (7)
As of 2024-04-19 08:28 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found