How to move directory into a directory with the same name?
To safely create a temporary directory in the current directory, with a name that is not already taken, you can use mktemp -d
like so:
tmpdir=$(mktemp -d "$PWD"/tmp.XXXXXXXX) # using ./tmp.XXXXXXXX would work too
The mktemp -d
command will create a directory at the given path, with the X
-es at the end of the pathname replaced by random alphanumeric characters. It will return the pathname of the directory that was created, and we store this value in tmpdir
.1
This tmpdir
variable could then be used when following the same procedure that you are already doing, with bar
replaced by "$tmpdir"
:
mv foo "$tmpdir"
mv "$tmpdir" foo
unset tmpdir
The unset tmpdir
at the end just removes the variable.
1 Usually, one should be able to set the TMPDIR
environment variable to a directory path where one wants to create temporary files or directories with mktemp
, but the utility on macOS seems to work subtly differently with regards to this than the same utility on other BSD systems, and will create the directory in a totally different location. The above would however work on macOS. Using the slightly more convenient tmpdir=$(TMPDIR=$PWD mktemp -d)
or even tmpdir=$(TMPDIR=. mktemp -d)
would only be an issue on macOS if the default temporary directory was on another partition and the foo
directory contained a lot of data (i.e. it would be slow).
On macOS, you can install the rename
command (a Perl script) using Homebrew:
brew install rename
Then using the -p
(a la mkdir
) to have it make any necessary directories, and -A
to add a prefix:
% mkdir -p foo/bar; touch foo/{a,b}.txt foo/bar/c.txt
% rename -p -A foo/ foo/*
% tree foo
foo
└── foo
├── a.txt
├── b.txt
└── bar
└── c.txt
Run with -n
to show changes without renaming (dry-run):
% rename -p -A foo/ foo/* -n
'foo/a.txt' would be renamed to 'foo/foo/a.txt'
'foo/b.txt' would be renamed to 'foo/foo/b.txt'
'foo/bar' would be renamed to 'foo/foo/bar'
If you have dot files, so that a simple *
won't pick them up, then use other methods with rename
:
With bash, (mis)use
GLOBIGNORE
to get*
to match dot files:$ GLOBIGNORE=.; rename -p -A foo/ foo/* -n 'foo/.baz' would be renamed to 'foo/foo/.baz' 'foo/a.txt' would be renamed to 'foo/foo/a.txt' 'foo/b.txt' would be renamed to 'foo/foo/b.txt' 'foo/bar' would be renamed to 'foo/foo/bar'
Or use
find
with-print0
andrename
with-0
:% find foo -mindepth 1 -maxdepth 1 -print0 | rename -0 -p -A foo/ -n Reading filenames from STDIN Splitting on NUL bytes 'foo/b.txt' would be renamed to 'foo/foo/b.txt' 'foo/a.txt' would be renamed to 'foo/foo/a.txt' 'foo/bar' would be renamed to 'foo/foo/bar' 'foo/.baz' would be renamed to 'foo/foo/.baz'
As long as the contents are not sufficient to exceed the maximum parameter limits, (and you don't mind an "acceptable" error message) then it doesn't need to be any more complicated than this:
mkdir foo/foo
mv foo/* foo/foo
Amendment to handle hidden files:
mkdir foo/foo
mv foo/{.,}* foo/foo