[net.sources] "nmkdir" in the Bourne shell at a Unix machine near you.

blm@cxsea.UUCP (Brian Matthews) (08/26/85)

> ** If you want it, it's yours.  All I ask in return is that if you
> ** figure out how to do this in a Bourne Shell script you send me
> ** a copy.

Here 'tis.  It basically uses expr to hack apart the requested path, and
do mkdir's on all the intermediate components.  I wrote it a while ago,
but haven't used it much, but I do think it works well.  There are three
known problems:

    1.  It's slow.
    2.  It doesn't trap any signals, so you can hit delete and have half
	of a path made.  This doesn't hurt anything, you may just have to
	do a little cleaning up.
    3.  It's slow.

From the looks of the C source, it fixes all of the problems, so I may
switch to it and toss this.

I called it md, for Make Directory, kind of like Change Directory.

#!/bin/sh
#
# md directories - make all components in a given path
#
# written by Brian L. Matthews, 17-Jul-84
#
# md makes a directory path.  Unlike mkdir, it makes all necessary
# components of the path.  It uses mkdir, so it should fail and succeed in
# the same ways as mkdir.
# md tends to be slow, and should be recoded in C someday. Oh well...
#
if [ $# = 0 ]				# check for no arguments.  If none,
then					# print same stuff and use same
    echo "$0: arg count"		# exit status as mkdir
    exit 2
fi
while [ -n "$1" ]			# Loop through all arguments by
					# looking at the first and shifting
					# when done with it.
do
    made=				# $made tracks the components made
					# so far
    need=$1				# $need tracks what's left to make
    if [ `expr $need : '/.*'` = 0 ]	# if $need doesn't start with a slash,
    then				# add a slash, and say we've made
	made=`pwd`			# the current working directory.
	need=/$need
    fi
    while [ -n "$need" ]		# keep going until we've got it all
    do
	made=$made`expr $need : '\(.[^/]*\)/*'`
					# add the first component of $need to
					# $made.  If $made was /u and $need
					# was /blm, $made would become
					# /u/blm
	if [ ! -d "$made" -o ! -w "$made" ]
	then				# if $made isn't a directory or it's
	    mkdir $made			# not writable (i.e. it doesn't
	    if [ $? != 0 ]		# exist), do a mkdir.  If the mkdir
	    then			# fails, exit with it's exit code.
		exit $?			# mkdir has already printed the error.
	    fi
	fi
	need=`expr $need : '.[^/]*\(/*.*\)'`
					# remove the first component of $need.
    done				# we've now made a path, so scoot the
    shift				# arguments down, and do the next one.
done
exit 0


Brian L. Matthews
...uw-beaver!ssc-vax!cxsea!cxsea2!arrakis!blm

stan@tikal.UUCP (Stan Tazuma) (08/27/85)

In article <309@cxsea.UUCP> blm@cxsea.UUCP (Brian Matthews) writes:
>> ** If you want it, it's yours.  All I ask in return is that if you
>> ** figure out how to do this in a Bourne Shell script you send me
>> ** a copy.
>
>Here 'tis.  It basically uses expr to hack apart the requested path, and
>do mkdir's on all the intermediate components.  I wrote it a while ago,
>but haven't used it much, but I do think it works well.  There are three
>known problems:
>
>    1.  It's slow.
>    2.  It doesn't trap any signals, so you can hit delete and have half
>	of a path made.  This doesn't hurt anything, you may just have to
>	do a little cleaning up.
>    3.  It's slow.

Just to illustrate some possibly little known features of the
Bourne shell, I thought I'd followup with another version of nmkdir.
Expr and test can be slow enough that the use of the shell pattern
matching features (the case statement) may be preferred.  Also,
creative use of IFS can be a big help.  Here is my version:

#! /bin/sh
cwd=`pwd`
IFS=/
    # after setting IFS, anything with '/' has to be quoted.
    # also, IFS will take care of repeated '/'s properly.
for arg
{
    case "$arg"
    {
	/*) cd "/" ;;
    }

    for d in $arg
    {
	if test ! -d $d
	then
	    if mkdir $d
	    then	: fall through
	    else
		echo "Error making directory $d in dirpath $arg"
		exit 1
		#### break
	    fi
	fi

	if cd $d
	then : ok, go on
	else
	    echo "Error, can't chdir to $d in dirpath $arg"
	    exit 1
	fi
    }
    cd "$cwd"
}
exit

naiman@pegasus.UUCP (Ephrayim J. Naiman) (08/28/85)

<munch, munch>

Here's another nmkdir which makes all the directories in a given path.
It doesn't use expr so it's not so slow.

==========================================================

if [ $# = 0 ]
then
    echo "$0: arg count"
    exit 2
fi
CWD=`pwd`
for i in $*
do
    IFS=/
    BUILD=.
# If the directory starts with a slash we have to move there.
    if [ "`echo "$i" | cut -c1`" = "/" ]
    then
	cd "/"
    fi
    for j in $i
    do
	BUILD=${BUILD}/${j}
	if [ ! -d "$BUILD" ]
	then
	    mkdir "$BUILD"
# Remember the exit code because the test operator has its own exit code.
	    EXIT=$?
	    if [ $EXIT != 0 ]
	    then
		exit $EXIT
	    fi
	fi
    done
# cd back in case there are more arguments on the argument list that do
# not begin with slash.
    cd "$CWD"
done
-- 

==> Ephrayim J. Naiman @ AT&T Information Systems Laboratories (201) 576-6259
Paths: [ihnp4, allegra, mtuxo, maxvax, cbosgd, lzmi, ...]!pegasus!naiman

naiman@pegasus.UUCP (Ephrayim J. Naiman) (09/02/85)

	<munch, munch>

>	if cd $d
>	then : ok, go on
>	else
>	    echo "Error, can't chdir to $d in dirpath $arg"
>	    exit 1
>	fi

I think in the bourne shell a bad cd will terminate the shell script
no matter what its return code.  Therefore, as soon as a bad cd occurs,
termination before any test.
-- 

==> Ephrayim J. Naiman @ AT&T Information Systems Laboratories (201) 576-6259
Paths: [ihnp4, allegra, mtuxo, maxvax, cbosgd, lzmi, ...]!pegasus!naiman

rbp@investor.UUCP (Bob Peirce) (09/02/85)

In order to keep things on disk that need to be accessed from time-to-
time but never need changed, we created a parallel directory tree to
which to move such files.  These are backed up once a week or so rather
than every night.  To build the files we had to make directories on the
fly and used the following approach:

	if [ ! -d $H$D ]	#  directory doesn't exist
	then
		#  make required directories
		L=`echo $H$D | tr "/" ' '`
		I=""
		for i in $L
		do
			I=$I/$i
			if [ ! -d $I ]
			then
				mkdir $I
			fi
		done
	fi

$H$D is the path, ex the file name.
-- 

		 	Bob Peirce, Pittsburgh, PA
		uucp: ...!{allegra, bellcore, cadre, idis}
		  	 !pitt!darth!investor!rbp
				412-471-5320

		NOTE:  Mail must be < 30K  bytes/message

gwyn@brl-tgr.ARPA (Doug Gwyn <gwyn>) (09/03/85)

> >	if cd $d
> > ...
> I think in the bourne shell a bad cd will terminate the shell script
> no matter what its return code.  Therefore, as soon as a bad cd occurs,
> termination before any test.

There is a simple way around this; use a subshell as in (cd $d).
By way of illustration, here the the .funcs file that I source
in my .profile.  (Warning: the suspend() function is for the BRL SVR2
job-control Bourne shell and will not work with the AT&T SVR2 shell.)

# shell functions

DIRSTACK=...	# for pushd, popd

ch(){
	if [ $# -lt 1 ]
	then	cd
	else	cd $1
	fi
	PS1=`uname`:`pwd`' $ '
}

j(){ (set +u; exec jove $*); }

l(){ (set +u; exec ls -bCF $*); }

not(){	$*
	if [ $? -eq 0 ]
	then	return 1
	else	return 0
	fi
}

popd(){
	set $DIRSTACK
	if [ $# -ge 2 ]
	then	DIRSTACK="$*"
		ch $1
		set $DIRSTACK	# ch clobbered $*
		shift
		DIRSTACK="$*"
	fi
}

print(){ (set +u; pr $* | lpr); }

pushd(){
	DIRSTACK=`pwd`" $DIRSTACK"
	if [ $# -lt 1 ]
	then	set $HOME
	fi
	if (cd $1)
	then	cd $1 >&-
		echo $DIRSTACK
		PS1=`uname`:`pwd`' $ '
	else	popd
	fi
}

if not pdp11
then
suspend(){
	case "$-" in
	*J*)	set +j
		kill -18 $$
		set -J
		;;
	*)	kill -18 $$	# SIGSTOP
		;;
	esac
}

readonly suspend
fi

swapd(){
	DIRSTACK=`pwd`" $DIRSTACK"
	set $DIRSTACK
	if [ $# -ge 3 ]
	then	DIRSTACK="$*"
		ch $2
		set $DIRSTACK	# ch clobbered $*
		DIRSTACK="$1"
		shift 2
		DIRSTACK=$DIRSTACK" $*"
		echo $DIRSTACK
	else	shift
		DIRSTACK="$*"
		echo 'swapd: No previous directory' >&2
		return 1
	fi
}

readonly ch j l popd print pushd swapd