222

I am migrating my home directory from an old system to a new one, and the tarball I made contains everything, including hidden files like .bashrc. However, when I move the contents of the unpacked tarball (which are in /tmp) to my new home directory, the hidden files do not copy (mv /tmp/home/rcook/* /home/rcook/). How can I get mv to move them?

Actually, I think the problem is not with mv, but with bash's globbing. If I do this:

mkdir a
mkdir b
touch a/.foo
touch a/bar
mv a/* b/
ls -a a/ b/

I see this:

a/:
.  ..  .foo

b/:
.  ..  bar

a/.foo did not move. So how can I get the * wildcard to find hidden files?

Yes, I suppose I could decompress the tarball directly into my home directory, but the tarball decompresses into home/rcook/..., and I want to be sure I overwrite the new .bashrc, etc. with the old, customized versions, and knowing how to find and move hidden files is a worthwhile skill. Suggestions?


Some answers suggest doing something like mv src/.* dest/. However, I tried this on my test directories and got errors. Starting with:

rcook$ ls -a a/ b/
a/:
.  ..  bar  .foo

b/:
.  ..
rcook$ mv a/.* b/
mv: cannot move 'a/.' to 'b/.': Device or resource busy
mv: cannot remove 'a/..': Is a directory
rcook$ ls -a a/ b/
a/:
.  ..  bar

b/:
.  ..  .foo

What am I doing wrong?

Braiam
  • 69,112
Randall Cook
  • 4,155
  • 4
  • 19
  • 21

10 Answers10

236

You can do this :

shopt -s dotglob
mv /tmp/home/rcook/* /home/rcook/

You can put

shopt -s dotglob

in your ~/.bashrc if you want it to be the default.

See http://mywiki.wooledge.org/glob


Another approach to copy the dot files:

mv /tmp/home/rcook/.[!.]* /home/rcook/

Don't use the pattern ..* as it matches .. (pointer to the parent directory). If there are files whose name begin with two dots (..something), also use the pattern ..?*.

77

In your additions, you got errors but the code still worked. The only thing to add is that you told it only to copy the dot files. Try:

mv src/* src/.* dst/

You will still get the errors for the . and .. entries, which is fine. But the move should succeed.

~/scratch [andrew] $ mv from/* from/.* to/
mv: cannot move ‘from/.’ to ‘to/.’: Device or resource busy
mv: cannot remove ‘from/..’: Is a directory
~/scratch [andrew] $ ls -a from/ to/
from/:
.  ..

to/:
.  ..  test  .test
17

If you ls -l in a directory, you will see . and .. among listed files. So, I think mv .* /dest takes those pointers into account. Try:

mv /tmp/home/rcook/{*,.[^.]*,..?*} /home/rcook/

this will ignore those current and parent dir pointers.

You will get an error if any of the three patterns *, [^.]* or ..?* matches no file, so you should only include the ones that match.

Azamat
  • 436
13

I know this is old, but, one simple way to do it is:

mv a/{.*,*} b/

Explanation

This is called brace expansion. In this case, the shell expands the path you give by concatenating the string given before the braces (in this case a/) followed by each variation given in the list surrounded by the braces. The variation list is composed by values separated by commas and, again, surrounded by curly braces. So, in this case we have .* and * as elements, so the list must be {.*,*}.

mv a/{.*,*} b/

The above expression can be read this way: move all files starting with a dot (.*) AND anything else (*) in the a/ directory to the b/ directory.

More hints

This is very helpful and can be used in various scenarios. For example, imagine you have to create 100 files with the following naming pattern: file1, file2, file3...file100. You can easily achieve it by doing this

touch file{1..100}

In this case, you can see a range was given. You can do this by separating the first and last values by two dots (..).

Going Beyond!

I'm new to the Unix world and, even knowing this works in bash and zsh (the shells I've used so far), I can't guarantee this will work in any shell.

You can see more about expansions an globbing in your shell's documentation (using: man bash or man zshall, for bash and zsh respectively).

Hope this helps future readers!

tcarv
  • 131
  • 1
  • 3
12

Two possible solutions I can think of. The first is to use cp instead with its recursive option, copying the current directory to the destination.

cp -Rp . /desired/directory

then you can remove the source files in the current directory

Alternatively, if you know the files are sanely named (no spaces, wildcards, non-printable characters), you can do something like this

mv $(ls -A) /desired/directory
5

There isn't really such a thing as "hidden" files on Linux. Files which begin with a dot are just hidden from file listings by default.

To copy files even with a glob, you need to prefix the file with . such as mv -u .* foo and then .foo will appearn as foo/.foo when moved.

The -u option will only move the files when the source is newer, or the destination is missing. Or you could just ignore the errors about moving . and .. as they are special files and cannot be moved, but do get caught in the .* glob by the shell.

dobey
  • 41,650
1

An alternate way is to use (GNU) find:

find /tmp/home/rcook/ -mindepth 1 -maxdepth 1 -exec mv {} /home/rcook/ \;
0

In my case I was simply trying to mv a single hidden file and this was the solution:

FILE=.myHiddenFile
mv "$FILE" dest/

Explanation

At first I was trying:

mv .myHiddenFile dest/

But it kept producing the error:

mv: cannot stat '.myHiddenFile': No such file or directory

Even though a ls -al showed that the file and dest/ existed.

The reason appears to be because the shell was interpreting the "." in the file name, so by moving it to a variable it avoids the interpretation. There are probably other ways to resolve this, but this was enough for us.

Isaiah
  • 101
0

To move all files and directories from the current directory to its parent directory you can use

mv * ../

Note: It will not work if you want to move hidden files or directories, If you need to move hidden files to then use below command.

mv * .[^.]* ../
0

So we have:

a
|-- .foo
`-- bar

and:

b

I took a huge time finding better solution than all the answers here.

Then I took 30 seconds for this command:

$ mv --help

Then I found my love:

$ mv a/ b/ -T

Note: I don't even understand the argument -T. It just did what I needed. Here it says:

-T, --no-target-directory treat DEST as a normal file