2

I have a directory

foo/
   bar.txt
   baz.yzw
   wun/
      a.out

Now, I would like to basically add a directory in between, i.e. I would like to make it

foo/
   var1/
      bar.txt
      baz.yzw
      wun/
         a.out

with the intent of also adding other stuff to foo, but kept separate from the old contents.

I could of course do it like this:

$ mkdir foo-new
$ mv foo foo-new
$ mv foo-new foo

or

$ cd foo
$ mkdir var1
$ mv $(ls | grep -v var1) var1

but both seem inelegant and are error-prone.

Is there a better way to do it?

5
$ cd foo
$ mkdir var1
$ mv * var1

The shell and mv command are smart enough to not try to move the var1 directory into itself.

  • I should have just tried this... easy! It feels a bit inelegant because of the error message mv: cannot move 'var1' to a subdirectory of itself, 'foo/var1', but on the command-line this is clearly the way to go. – leftaroundabout May 25 '18 at 16:02
5

You can simply make var1 directory, move all files/folders in foo to var1 and finally move var1 inside foo

    $ mkdir var1 
    $ mv foo/* var1
    $ mv var1 foo/
2

I would go with renaming the directory and moving it inside a new one, approximately as you did. To insert var1/ between foo and its contents:

d=$(mktemp -d ./tmp.XXXXXX)
mkdir "$d"; mv foo "$d"/var1; mv "$d" foo

That works for the directory contents. However, it's a bit more complicated if we also consider the owner, access bits, ACLs, extended attributes, inode number etc. of the directory.

If we want to keep foo intact, then we'll need to create var1 anew, and move the contents. Using Bash:

cd foo
mkdir var1
shopt -s dotglob    # to get any dotfiles
mv * var1           # ignore the error about moving var1 to itself

Or, to avoid the error, use extglob:

shopt -s extglob 
mv !(var1) var1

Note that $(ls | grep -v) will not include dotfiles, and it will not work if you have filenames with whitespace or newlines (for two different reasons).

1

You can use rsync which will create the destination directory (only last level) if that doesn't already exist, but this will result empty directories which you will delete it later or keep.

rsync -rv [--remove-source-files] foo/* foo/var1

There is an option -m, --prune-empty-dirs. that prune empty directory chains from file-list in my rsync man page, but I didn't get that work!

I put --remove-source-files within braces to be [warn] about that.

to delete empty directories:

find foo/ -depth -type d -empty -execdir rmdir "{}" \;

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.