[Dclug] useful bash commands: pmark
Bruce Israel
israel at tux.org
Sun Oct 10 11:20:43 EDT 2010
On Sat, Oct 09, 2010 at 06:46:27PM -0400, Bruce Israel wrote:
>
> > Additionally, I can see some possible generalizations to the
> > marking concept that make the 'variable=/absolute/path' option
> > distinctly inferior. As one example, Bruce already mentioned
> > his persistent marks capability. I would like to see this
> > script, though I wouldn't blame Bruce for deciding against
> > putting it on display to avoid the risk of being told it's a
> > "loser".
>
> I'll send out 'pmark' within the next dayy or two for discussion.
OK, it turns out that I could find, in my archives if not on the
web, a writeup I had done on 'pmark' a while back.
This is an article I wrote on 'pmark' that was published in Unix
Review in June of 2003. Unfortunately they not only got rid of
unixreview.com but they folded it into networkcomputing.com and
removed all the archives. They're not available in the wayback
machine either.
Anyway, here's the article. This should be interesting. The script
was written nine years ago (back in 2001) when I used to write 'csh'
scripts (my scripting is primarily bash and python now), so we don't
need to comment regarding the choice of platform. Jim, otherwise
feel free to critique (there's probably lots to critique
stylistically), though stylistic criticism doesn't much concern me.
My major focus for these things is the functionality, having a neat
idea that solves a problem or makes something easier, and making the
command maximally functional (that focus should have been noticeable
in the discussions on 'mark').
To give a quick intro to 'pmark' in the context of this discussion;
'mark', as we've discussed ad nauseum, marks a file or directory
with a label, implemented as a shell variable, and that persists for
the duration of the shell session.
'pmark', on the other hand, is similar in that it allows marking of
directories for later reference, but it marks only directories, not
files, but it creates persistent marks that work across all shells,
windows and logins, and only applies to 'cd'. With that in context,
here's the article.
Bruce
================================================================
Dropping Breadcrumbs in Directories
In the
<a href="http://www.unixreview.com/documents/s=7932/ur0304d/">April</a>
column, Fergal Goggin demonstrated an approach to
changing directories via 'ncd', an implementation of the Norton
Change Directory program. Here I'll describe an alternative
approach to managing the process of changing directories.
The Issue
When you've used a Unix system for any length of time or when
working a sizable development project, you often end up with a large
directory hierarchy with many deep directories that you move around
among. Typing in long cd commands can be time-consuming and
annoying; e.g.
cd /opt/appls/EverlastingGobstopper2.0/development/AdminClient/com/wonka/loompa/control
Additionally, when working on a development process or other
extended task, one tends to change to the same set of directories
repeatedly over the course of days or weeks. Given that mode of
operation, the question arises as to how to be able to define
shortcuts to frequently used directories so as to simplify switching
to them. These shortcuts need to be persistent, that is, they need
to be available across windows and sessions, and they need to be
simple, preferably working within the context of the existing
commands and structure.
My Approach
My approach was to implement a utility called 'pmark' for creating a
persistent <i>mark</i> for the directory. This is a "breadcrumbs"
script; when one is somewhere to be returned to frequently an
easily, one can drop a "breadcrumb" there for ease of returning.
Examples of Use
Lets assume that I have a large Java project, with one of the source
directories being at the above path. So to drop a persistent mark
in that directory, I first go there and then use 'pmark' to label it
with a label that is simple and easy to remember. e.g.
cd /opt/appls/EverlastingGobstopper2.0/development/AdminClient/com/wonka/loompa/control
pmark adminctl
Later on, when I want to return to this directory (in this session
or another), I can type:
cd adminctl
and I'm back in that directory.
How it Works
The standard Unix shells have a facility called "CDPATH" which often
allows you to shortcircuit these long directories when 'cd'ing. If
you put the parent directory on a shell variable called CDPATH (in
bash; it's 'cdpath' for csh users) you can "cd control" and go
directly there.
This doesn't work if there are multiple directories of the same
name, and it requires editing your .bashrc or other init file
whenever a new addition is needed.
Instead, the way 'pmark' works is to use a single repository for
persistent marks. The 'pmark' script takes advantage of the shell's
CDPATH facility, as well as the fact the shell can 'cd' into a
directory pointed to by a symbolic link. The script creates a
symbolic link in a directory repository, with the name of the
symbolic link being the user defined directory alias and the target
of the link being the actual directory the alias points to. This
directory repository is on the CDPATH, allowing it to be found by
the shell on 'cd' commands.
When a user types 'cd <dir>', the shell first checks to see if the
specified <dir> is a subdirectory of the current directory. If so,
it goes there. If not, it goes through the CDPATH directories one
at a time checking to see if it is a subdirectory of one of those.
If the specified directory is a 'pmark'ed label, then when it gets
to the directory repository on the 'cdpath', it finds the symbolic
link there and traverses it to the destination directory.
One other interesting thing that the script does is if the directory
being marked is under the user's home directory, then the symbolic
link created is a relative link. This allows the user's home
directory to be tarred up or renamed, and the link is still
maintained. If the directory is outside of the user's home
directory, then a symbolic link to the full path is used.
Setting It Up
'pmark' is very easy to set up. It requires one change to your
.bashrc, adding the following line to it:
export CDPATH=$HOME/.dirs
or if you use the C shell, then adding the line:
set cdpath = ( ~/.dirs )
to your .cshrc instead.
Once the script is on your path and the above line is in your
shell's init file (and that file's been run), 'pmark' can be used.
If 'pmark' is used without parameters, then it lists all available
marks, specially indicating marks for directories that no long
exist. e.g.
$ pmark
Usage: pmark <x>
Current pmarks are:
ht -> /home/httpd/htdocs
pics -> ../Personal/pics
(non-existent) p1 -> ../project1/src
src -> /home/src
How The Script Works
In terms of operation, the 'pmark' script is actually very
straightforward (though there's a lot of error checking, so it's
larger). The script does the following:
1) Check the repository and create it if needed
2) if no arguments are given, list the repository contents and exit
3) convert the current directory to an address relative to the repository
4) add the directory to the repository under the specified name.
5) confirm to the user that the action was taken.
Steps 2 & 3 are a little more complicated; 2 because it makes use of
exactly how /bin/ls -l displays the contents of a directory with
symbolic links, and step 3 because it pattern-matches the directory
to see that it's underneath the home directory, and then traverses
that tree to generate the relative link. The advantage of storing
the marks with relative links is that the whole directory tree can
be renamed to tarred up, and all marks within that tree will still
work.
pmark vs. ncd
These two scripts have differing goals. Fergal's 'ncd' is designed
to simplify searching throughout the whole set of directories on
one's machine (an extremely large number of possibilities) which may
be used only infrequently. 'pmark' on the other hand, has the
design goal of simplifying going to a small set of directories that
are frequently used. The advantages of 'ncd' over 'pmark' is that
it does not require any prior setup for a particular directory, and
it takes regular expressions to find directories. On the other
hand, common names do not work very well under 'ncd' (on my machine,
the command "ncd misc" brings up 1047 choices). Also, changes to
the directory structure necessitate either manual or automated
regular updating of the directory database.
In conclusion, 'pmark' can simplify the process of managing a set of
directories that are gone to on a regular basis though as the set of
marks grows large, a simple and consistent naming scheme for the
marks becomes more important.
================================================================
Here's the script.
#!/bin/csh -f
#
# pmark - mark the current directory persistently for 'cd'ing back.
# Written by: Bruce Israel <israel at tux.org>, Sat Oct 27 2001
#
set usage = "pmark <x>"
#
# sets up a persistent mark <x> pointing to the cur dir.
#
# Setting up pmark:
#
# using pmark requires changes at the shell level, which are easy to
# set up. Add the following line to your .bashrc file:
#
# export CDPATH=.:$HOME/.dirs
#
# or if you use the C shell, add this command to your .cshrc:
#
# set cdpath = ( . ~/.dirs )
#
# Once the script exists on your path, and the above line is in the
# shell's init file (and that file's been run), pmark can be
# used. Executing pmark without parameters lists all available
# marks, especially indicating marks for directories that no longer
# exist.
#
# persistent marks are stored in ~/.dirs
set storage = ~/.dirs
# set to use relative directories from $HOME (this means that the
# home directory can be tarred up, preserving persistent marks).
set use_relative = 1
# Check to see if the persistent mark cache directory exists, and create it
# if needed.
if (! -d $storage) then
echo -n "Directory repository $storage doesn't exist, create it? [y] "
set ans = "$<"
if ("$ans" == "y" || "$ans" == "Y" || "$ans" == "") then
echo mkdir -p $storage
mkdir -p $storage
else
exit 1
endif
endif
# if no parameter passed, generate usage line and list available pmarks
if ("$1" == "") then
echo "Usage: $usage"
cd $storage
echo "Current marks are:"
# /bin/ls -lA | perl -lne 'if (/ ([^ ]+ ->.*$)/) { print $1 ; }'
/bin/ls -lA | perl -lne '\
if (/ ([^ ]+) -> (.*$)/) { \
$mark = "(non-existent) "; \
if ( -e $1 ) { $mark = ""; } \
print $mark,$1," => ",$2 ;\
}'
exit 1
endif
# for security, change spaces and slashes to at-signs
# (Otherwise someone could do "pmark /etc/passwd")
set name = "`echo "'"'"$1"'"'" | tr ' /' '@'`"
set dir = "`pwd`"
set fulldir = "$dir"
# if the directory is underneath $HOME, then use a relative
# directory instead, e.g. instead of X -> /home/israel/src,
# X will be linked to ../src.
if ($use_relative == 1 && "$dir" =~ ${HOME}/*) then
# Convert directory to be marked to relative directories
cd ${HOME}
set hname = "`pwd`"
cd $storage
set href = ""
# walk dirs up to $HOME to generate relative link from repository
while ("`pwd`" != "${hname}")
set href = "../${href}"
cd ..
end
# substitute relative link for $HOME in directory reference
set ndir = "`echo $dir | sed -e 's%${home}/%${href}%'`"
set dir = "$ndir"
endif
# go to repository storage, remove the old version of this mark (if
# it exists, and create new link.
cd $storage
rm -f $name
ln -s "$dir" $name
echo "mark $name => $fulldir created."
More information about the Dclug
mailing list