views:

1833

answers:

3

So, if I'm in my home directory and I want to move foo.c to ~/bar/baz/foo.c , but those directories don't exist, is there some way to have those directories automatically created, so that you would only have to type

mv foo.c ~/bar/baz/

and everything would work out? It seems like you could alias mv to a simple bash script that would check if those directories existed and if not would call mkdir and then mv, but I thought I'd check to see if anyone had a better idea.

+5  A: 

You can use mkdir:

mkdir -p ~/bar/baz/ && \
mv foo.c ~/bar/baz/

A simple script to do it automatically (untested):

#!/bin/sh

# Grab the last argument (argument number $#)    
eval LAST_ARG=\$$#

# Strip the filename (if it exists) from the destination, getting the directory
DIR_NAME=`echo $2 | sed -e 's_/[^/]*$__'`

# Move to the directory, making the directory if necessary
mkdir -p "$DIR_NAME" || exit
mv "$@"
strager
When I run "$ dirname ~?bar/baz/", I get "/home/dmckee/bar", which is not what yo want here...
dmckee
@dmckee, Ah, you are right. Any idea on how to solve this? If you input ~/bar/baz you either (a) want to copy to ~/bar/ and rename to baz, or (b) copy to ~/bar/baz/. What's the better tool for the job?
strager
You could use '[ -d "$2" ]' to sort it out, but that's not as pretty...
dmckee
@dmckee, I've used regexp/sed to come up with a solution. Does it work to your liking? =]
strager
Nice, except $2 is not the last argument unless $# = 2. I have a program, la, that prints its last argument. I used to use it in a version of the cp command (to add the current directory if the last argument wasn't a directory). Even modern shells support $1..$9 only; a Perl script may be better.
Jonathan Leffler
@Leffler, Not true -- I know bash supports more than 9 arguments (using ${123} is one method). I don't know Perl, so feel free to make an answer yourself. =]
strager
And even if you're using a plain vanilla /bin/sh, you can loop over 'store=${store} " " $1; shift;' until only one argument remains... Though at that point we've lost any semblance of elegance the script might have had.
dmckee
dmckee, I was going to try shift, but then found a more elegent solution. See update.
strager
A: 

The following shell script, perhaps?

#!/bin/sh
if [[ -e $1 ]]
then
  if [[ ! -d $2 ]]
  then
    mkdir -p $2
  fi
  mv $1 $2
fi

That's the basic part. You might want to add in a bit to check for arguments, and you may want the behavior to change if the destination exists, or the source directory exists, or doesn't exist (i.e. don't overwrite something that doesn't exist).

Chris Lutz
With your code, the move isn't performed if the directory does not exist!
strager
And you missed the "-p" flag to mkdir.
dmckee
If a directory doesn't exist, why create it if you're just going to move it? (-p flag fixed)
Chris Lutz
I mean, when you run the script and the directory $2 does not exist, it is created but the file is not copied.
strager
he gave a simple construct and added information on how it should be expanded. Why vote it down?!
ypnos
Closer. But why are you making the construction of the target directory conditional on the non-existence of the file to be moved?
dmckee
@ypnos: Because so far it doesn't do what it is supposed to?
dmckee
And I haven't fixed it myself for pedagogical reasons.
dmckee
Ah, heck. I want my point back.
dmckee
The logic here: No point in try the move unless $1 exists, then if the target does not exist, make it. Might want to add a test so we don't overwrite an existing regular file named '~/bar/baz'.
dmckee
+8  A: 

How about this one-liner (in bash):

mkdir -p ./some/path/; mv yourfile.txt $_

Breaking that down:

mkdir -p ./some/path

creates the directory (including all intermediate directories), after which:

mv yourfile.txt $_

moves the file to that directory ($_ expands to the last argument passed to the previous shell command, ie: the newly created directory).

I am not sure how far this will work in other shells, but it might give you some ideas about what to look for.

Here is an example using this technique:

$ > ls
$ > touch yourfile.txt
$ > ls
yourfile.txt
$ > mkdir -p ./some/path/; mv yourfile.txt $_
$ > ls -F
some/
$ > ls some/path/
yourfile.txt
KarstenF
Nice with the '$_'.
dmckee
Agreed, but this is a less general solution. My solution could serve as a mv substitude (thus an alias in bash) (assuming it works properly -- still untested!).
strager