欢迎各位兄弟 发布技术文章

这里的技术是共享的

You are here

mv move 移动 所有文件 包括包含隐藏文件和非隐藏文件点开头的文件 How to move all files including hidden files and non-hidden into parent directory via * 有大用 有大大用

Its must be a popular question but I could not find an answer.

How to move all files via * including hidden files as well to parent directory like this:

mv /path/subfolder/* /path/

This will move all files to parent directory like expected but will not move hidden files. How to do that?

7 Answers 正确答案

up vote152down voteaccepted

You can find a comprehensive set of solutions on this in UNIX & Linux's answer to How do you move all files (including hidden) from one directory to another?. It shows solutions in Bash, zsh, ksh93, standard (POSIX) sh, etc.


You can use these two commands together:

mv /path/subfolder/* /path/   # your current approach
mv /path/subfolder/.* /path/  # this one for hidden files

Or all together (thanks pfnuesel):

mv /path/subfolder/{.,}* /path/
mv ./{*,.*} /destination/directory/here

Which expands to:

mv /path/subfolder/* /path/subfolder/.* /path/

(example: echo a{.,}b expands to a.b ab)

Note this will show a couple of warnings:

mv: cannot move ‘/path/subfolder/.’ to /path/.’: Device or resource busy
mv: cannot remove /path/subfolder/..’: Is a directory

Just ignore them: this happens because /path/subfolder/{.,}* also expands to /path/subfolder/. and /path/subfolder/.., which are the directory and the parent directory (See What do “.” and “..” mean when in a folder?).


If you want to just copy, you can use a mere:

cp -r /path/subfolder/. /path/
#                     ^
#                     note the dot!

This will copy all files, both normal and hidden ones, since /path/subfolder/.expands to "everything from this directory" (Source: How to copy with cp to include hidden files and hidden directories and their contents?)

  • 13
    To merge both commands: mv /path/subfolder/{.,}* /path/ – pfnueselNov 25 '13 at 12:13
  • 2
    The braces are just a short cut for mv /path/subfolder/* /path/subfolder/.* /path/, not strictly necessary to combine the two commands into one. – chepner Nov 25 '13 at 13:48
  • 6
    I get the following error: mv: overwrite `/path/.'? y mv: cannot move `/path/subfolder/.' to `/path/.': Device or resource busy mv: overwrite `/path/..'? y mv: cannot move `/path/subfolder/..' to `/path/..': Device or resource busy – Dejan Mar 28 '14 at 18:25 
  • @Dejan Just ignore it. . denotes current directory and .. denotes updirectory. You must have noticed that all other files are moved. – DebiprasadMay 16 '16 at 7:59
  • 4
    "Just ignore the warning" may not always be a good idea. Right now I'm having a problem with a script in which I need to stop execution if any step fails - since this solution always causes an error, it kills my script. I need a way to determine if the mv command failed or not... – MarioVilas May 8 '17 at 9:55

This will move all files to parent directory like expected but will not move hidden files. How to do that?

You could turn on dotglob:

shopt -s dotglob               # This would cause mv below to match hidden files
mv /path/subfolder/* /path/

In order to turn off dotglob, you'd need to say:

shopt -u dotglob
  • Very helpful. Wanted to find out more but shopt is a builtin so man shoptdoesn't work and help shopt is very brief. But you can do bashman () { man bash | less -p "^ $1 "; } and then bashman shopt to read all about it straightforwardly. (Might have to hit n to jump down to the command if there are lines starting with shopt, as I found.) – Nick Rice Nov 20 '14 at 16:22
  • 1
    this will also affect all other commands like ls.. thus not really what you'd want, probably – Blauhirn Mar 14 '16 at 21:12
  • Thanks, this worked for me. – Harry Matharoo Dec 10 '16 at 9:39

I think this is the most elegant, as it also does not try to move ..:

mv /source/path/{.[!.],}* /destination/path

Alternative simpler solution is to use rsync utility:

rsync -vuar --delete-after path/subfolder/ path/

The advantage is that the original folder (subfolder) would be removed as well as part of the command, and when using mv examples here you still need to clean up your folders, not to mention additional headache to cover hidden and non-hidden files in one single pattern.

In addition rsync provides support of copying/moving files between remotes and it would make sure that files are copied exactly as they originally were (-a).

The used -u parameter would skip existing newer files, -r recurse into directories and -v would increase verbosity.

  • Best solution ever! In my case I just removed the -u parameter, because I wouldn't like to update the root folder. Thanks – Thales Ceolin Jul 13 '15 at 17:10 

Let me introduce you to my friend "dotglob". It turns on and off whether or not "*" includes hidden files.

$ mkdir test
$ cd test
$ touch a b c .hidden .hi .den
$ ls -a
. ..  .den  .hi .hidden a b c

$ shopt -u dotglob
$ ls *
a b c
$ for i in * ; do echo I found: $i ; done
I found: a
I found: b
I found: c

$ shopt -s dotglob
$ ls *
.den  .hi .hidden a b c
$ for i in * ; do echo I found: $i ; done
I found: .den
I found: .hi
I found: .hidden
I found: a
I found: b
I found: c

It defaults to "off".

$ shopt dotglob
dotglob         off

It is best to turn it back on when you are done otherwise you will confuse things that assume it will be off.

By using the find command in conjunction with the mv command, you can prevent the mv command from trying to move directories (e.g. .. and .) and subdirectories. Here's one option:

find /path/subfolder -maxdepth 1 -type f -name '*' -exec mv -n {} /path \;

There are problems with some of the other answers provided. For example, each of the following will try to move subdirectories from the source path:

1) mv /path/subfolder/* /path/ ; mv /path/subfolder/.* /path/
2) mv /path/subfolder/{.,}* /path/ 
3) mv /source/path/{.[!.],}* /destination/path

Also, 2) includes the . and .. files and 3) misses files like ..foobar, ...barfoo, etc.

You could use, mv /source/path/{.[!.],..?,}* /destination/path, which would include the files missed by 3), but it would still try to move subdirectories. Using the find command with the mv command as I describe above eliminates all these problems.

My solution for this problem when I have to copy all the files (including . files) to a target directory retaining the permissions is: (overwrite if already exists)

yes | cp -rvp /source/directory /destination/directory/

yes is for automatically overwriting destination files, r recursive, v verbose, pretain permissions.

Notice that the source path is not ending with a / (so all the files/directory and . files are copied)

Destination directory ends with / as we are placing contents of the source folder to destination as a whole.

来自 https://stackoverflow.com/questions/20192070/how-to-move-all-files-including-hidden-files-into-parent-directory-via



How do I move all files in a directory (including the hidden ones) to another directory?

For example, if I have a folder "Foo" with the files ".hidden" and "notHidden" inside, how do I move both files to a directory named "Bar"? The following does not work, as the ".hidden" file stays in "Foo".

mv Foo/* Bar/

Try it yourself.

mkdir Foo
mkdir Bar
touch Foo/.hidden
touch Foo/notHidden
mv Foo/* Bar/

migrated from stackoverflow.com Jan 24 '11 at 19:33

This question came from our site for professional and enthusiast programmers.

9 Answers 正确答案

up vote134down voteaccepted

Zsh

mv Foo/*(DN) Bar/

or

setopt -s glob_dots
mv Foo/*(N) Bar/

(Leave out the (N) if you know the directory is not empty.)

Bash

shopt -s dotglob nullglob
mv Foo/* Bar/


shopt -s dotglob && mv /everything/from/here/* /to/somehere/else/.

Ksh93

If you know the directory is not empty:

FIGNORE='.?(.)'
mv Foo/* Bar/

Standard (POSIX) sh

for x in Foo/* Foo/.[!.]* Foo/..?*; do
  if [ -e "$x" ]; then mv -- "$x" Bar/; fi
done

If you're willing to let the mv command return an error status even though it succeeded, it's a lot simpler:

mv Foo/* Foo/.[!.]* Foo/..?* Bar/

GNU find and GNU mv

find Foo/ -mindepth 1 -maxdepth 1 -exec mv -t Bar/ -- {} +

Standard find

If you don't mind changing to the source directory:

cd Foo/ &&
find . -name . -o -exec sh -c 'mv -- "$@" "$0"' ../Bar/ {} + -type d -prune

Here's more detail about controlling whether dot files are matched in bash, ksh93 and zsh.

Bash

Set the dotglob option.

$ echo *
none zero
$ shopt -s dotglob
$ echo *
..two .one none zero

There's also the more flexible GLOBIGNORE variable, which you can set to a colon-separated list of wildcard patterns to ignore. If unset (the default setting), the shell behaves as if the value was empty if dotglob is set, and as if the value was .* if the option is unset. See Filename Expansion in the manual. The pervasive directories . and .. are always omitted, unless the . is matched explicitly by the pattern.

$ GLOBIGNORE='n*'
$ echo *
..two .one zero
$ echo .*
..two .one
$ unset GLOBIGNORE
$ echo .*
. .. ..two .one
$ GLOBIGNORE=.:..
$ echo .*
..two .one

Ksh93

Set the FIGNORE variable. If unset (the default setting), the shell behaves as if the value was .*. To ignore . and .., they must be matched explicitly (the manual in ksh 93s+ 2008-01-31 states that . and .. are always ignored, but this does not correctly describe the actual behavior).

$ echo *
none zero
$ FIGNORE='@(.|..)'
$ echo *
..two .one none zero
$ FIGNORE='n*'
$ echo *
. .. ..two .one zero

You can include dot files in a pattern by matching them explicitly.

$ unset FIGNORE
$ echo @(*|.[^.]*|..?*)
..two .one none zero

To have the expansion come out empty if the directory is empty, use the N pattern matching option: ~(N)@(*|.[^.]*|..?*) or ~(N:*|.[^.]*|..?*).

Zsh

Set the dot_glob option.

% echo *
none zero
% setopt dot_glob
% echo *
..two .one none zero

. and .. are never matched, even if the pattern matches the leading . explicitly.

% echo .*
..two .one

You can include dot files in a specific pattern with the D glob qualifier.

% echo *(D)
..two .one none zero

Add the N glob qualifier to make the expansion come out empty in an empty directory: *(DN).


Note: you may get filename expansion results in different orders (e.g., none followed by .onefollowed by ..two) based on your settings of the LC_COLLATELC_ALL, and LANG variables.

  • 1
    Why nullglob? It would make more sense to abort the mv command (like fish, csh/tcsh, zsh (without (N)) do, or bash with failglob) than running a mv /Bar/ command which makes little sense. – Stéphane Chazelas Feb 21 '16 at 16:29
  • 1
    I'd say your GLOBIGNORE description is inaccurate and misleading. Setting GLOBIGNORE to a non-empty value turns on dotglob and makes that . and .. are never matched (like in other sensible shells like zsh, fish, pdksh and derivatives) even if you turn dotglob back off afterwards. (GLOBIGNORE=:; shopt -u dotglob; echo .* won't output . and ..). setting GLOBIGNORE to .:.. or . or : have the same effect. – Stéphane Chazelas Feb 21 '16 at 19:37 
  • ksh93 always ignoring . and .. seems to have been broken in between ksh93k+ (where it worked) and ksh93m (where it no longer works). Note that it's bad in that ksh93 will take the value of FIGNORE from the environment, so a FIGNORE='!(..)' env var for instance can create havoc. – Stéphane ChazelasFeb 21 '16 at 21:05
#!/bin/bash

shopt -s dotglob
mv Foo/* Bar/

From man bash

dotglob If set, bash includes filenames beginning with a '.' in the results of pathname expansion.

  • With the caveat that this command returns an error code if the directory was empty (even though the command actually performed as intended). – Gilles Jan 24 '11 at 20:28
  • 1
    @Gilles, the error will then be from mv complaining that that file called Foo/* doesn't exist. Interestingly, if Foo is searchable, but not readable, and there is a file called * in there, you'll get no error, that file will be moved, but not the other ones in the directory. bash has a failglob option so it behaves more like zshfish or csh/tcsh and abort the command with an error when a glob cannot be expanded instead of that bogus behaviour of leaving the glob as-is. – Stéphane Chazelas Feb 22 '16 at 10:01 

A simple way to do this in bash is

mv {Foo/*,Foo/.*} Bar/

But this will also move directories.

If you want to move all files including hidden but don't want to move any directory you can use a for loop and test.

for i in $(ls -d {Foo/*,Foo/.*});do test -f $i && mv -v $i Bar/; done;

One way is to use find:

find Foo/ -type f -exec mv -t Bar/ {} \+

The -type f restricts the find command to finding files. You should investigate the -type-maxdepth, and -mindepth options of find to customize your command to account for subdirectories. Find has a lengthy but very helpful manual page.

Try the copy command cp:

$ cp -r myfolder/* destinationfolder

cp -r means copy recursive, so all folders and files will be copied.

You can use the remove command rm to remove a folder:

$ rm -r myfolder

See more: move all files from a directory to another one.

Rsync is another option:

rsync -axvP --remove-source-files sourcedirectory/ targetdirectory

This works because in rsync the trailing slash matters, sourcedirectory/ refers to the content of the directory, while sourcedirectory would refer to the directory itself.

The disadvantage of this method is that rsync will only cleanup the files after the move, not the directory. So you are left with an empty sourcedirectory tree. For workarounds for that, see:

Move files and delete directories with rsync?

So while this might not be optimal for move operations, it can be extremely useful for copy operations.

Answer for bash/fish

Here's a way to do it using wildcards:

.[!.]* ..?* will match all hidden files except . and ..

.[!.]* ..?* * will match all files (hidden or not) except . and ..

And to answer the particular example of this question you need cd foo && mv .[!.]* ..?* * ../bar

Explanation

.[!.]* matches file-names starting with one dot, followed by any character except the dot optionally followed by any string. This is close enough but it misses files starting with two dots like ..foo. To include such files we add ..?* which matches file-names starting with two dots, followed by any character, optionally followed by any string.

Test

You can test these wildcards with the commands below. I've tried them successfully under bash and fish. They fail under sh, zsh, xonsh.

mkdir temp
cd temp
touch  a  .b  .bc  ..c  ..cd  ...d  ...de
ls .[!.]* ..?*
# you get this output:
          .b  .bc  ..c  ..cd  ...d  ...de
# cleanup
cd ..
rm -rf temp

You might also be able to find and grep with backquotes to select files for the move command. Pass those into mv.

I.e. For hidden files

find Foo -maxdepth 1 | egrep '^Foo/[.]' # Output: .hidden

So

mv `find Foo -maxdepth 1 | egrep '^Foo/[.]'` Bar # mv Foo/.hidden Bar

Moves only selected hidden files into Bar:

mv `find Foo -maxdepth 1 | egrep '^Foo/.'` Bar # mv Foo/.hidden Foo/notHidden Bar

Moves all files in Foo to Bar since the '.' in the egrep command acts as a wildcard without the square brackets.

The ^ character ensures the match starts from the beginning of the line.

Some details of egrep pattern matching can be found here.

Using maxdepth 1 stops find from going into subdirectories.

Inspired from this answer:

Without copying the files...

rsync -ax --link-dest=../Foo/ Foo/ Bar

Caveats:

  • --link-dest path must be absolute or relative to DESTINATION (Bar in the example)

  • You've to put / after SOURCE (Foo/ in the example), otherwise it will copy the SOURCE folder instead of contents of it.

来自  https://unix.stackexchange.com/questions/6393/how-do-you-move-all-files-including-hidden-from-one-di...



普通分类: