116

I am at the path:

/myuser/downloads/

And I create a sub folder:

/myuser/downloads/new

Now I want to move all files and folders/sub-folders in the downloads folder to the sub-folder.

how can I do this?

I tried:

mv -R *.* new/

But move doesn't take the -R switch it seems.

Blankman
  • 8,275
  • 6
    You do not need -R. Just mv * new Though, it will whine "cannot move new to new" or something similar. Perhaps mv -i * new to you prevent overwrites. –  Jan 01 '12 at 00:59

6 Answers6

165

The command

mv !(new) new

should do the trick. If it doesn't work, run shopt -s extglob first.

To also move hidden files/directories (that beginning with a dot), run also shopt -s dotglob first.
So, to sum up:

shopt -s extglob dotglob
mv !(new) new
shopt -u dotglob

(It is always better to unset dotglob to avoid bad surprises with shopt -u dotglob)

Adil
  • 3
enzotib
  • 93,831
  • 7
    can you mention what is the significance of !(something) ? – Adeerlike Feb 01 '16 at 12:46
  • 2
    @adeer that is just saying not(something). In other words, copy everything but not something. Clear as mud? – RockyMountainHigh Feb 25 '16 at 03:33
  • 5
    very. do you happen to know the equivalent for zsh ? – Adeerlike Mar 07 '16 at 10:16
  • 1
    I don't get it, e.g. what If I try to move the content of the folder dev to my current location, do I have to execute mv !(dev) dev ?? I get -bash: !: event not found – Black Jul 05 '18 at 09:59
  • 8
    @adeer very similar. setopt extendedglob to set the extended glob mode in zsh. ^ is the exclusion symbol, so mv ^new new would do the trick. then to see all the options set in zsh you can use setopt and to disable extended glob mode you can do unsetopt extendedglob – Mr. T Jul 21 '18 at 05:07
  • @Mr.T I'm running zsh and I tried what you said with the ^ character and didn't get a result yet. Thanks for the info regarding the ^ though. – ConstantFun Feb 14 '19 at 05:44
  • It somehow does not work with a full path in the !(...) statement. I made it work by cding into the superfolder. Thanks for the tip though. – V. Courtois Dec 23 '19 at 15:02
  • The equivalent ZSH command would be as follows: setopt extendedglob; mv ^new(D) new unsetopt extendedglob. ^ negates the new pattern, and (D) includes dot files. See the docs for more information. – William Brawner Oct 20 '23 at 15:08
27

I found something like this but is a bit simpler to understand, and it might work well for you too:

ls | grep -v new | xargs mv -t new

Adding an explanation to the above solution:

From man pages:

  • mv -t

    -t, --target-directory=DIRECTORY
          move all SOURCE arguments into DIRECTORY
    
  • grep -v

    -v, --invert-match
          Invert the sense of matching, to select non-matching lines.
    

Explained by step:

  • ls will list the files in current directory
  • grep -v new will return piped to that is not match new
  • xargs mv -t new will move the files piped to it from grep -v to the target directory
lockwobr
  • 507
  • I tried this method but I am getting: mv: illegal option -- t. Is this because I am using zsh? – ConstantFun Feb 14 '19 at 05:32
  • @ConstantFun It could be zsh but I would guess it's your implementation of mv, (meaning your version of mv is different them what I was using) what os are you using? I believe when I wrote this answer I was on linux distro centos using bash. – lockwobr Feb 19 '19 at 20:41
  • 2
    Just testing this on osx and I get the same error mv: illegal option -- t which is because osx's mv does not have a -t option. – lockwobr Feb 19 '19 at 20:44
  • 1
    (I have not tried this, and don't plan on it.) This link https://apple.stackexchange.com/questions/69223/how-to-replace-mac-os-x-utilities-with-gnu-core-utilities tells you how to install the gnucore which should help you get gnu core cli utils, osx ships with bsd cli tools which are different the the ones that come with linux. – lockwobr Feb 19 '19 at 20:51
  • I am on OSX running zsh, so it is probably the OS then..? Thanks for clarifying! – ConstantFun Feb 20 '19 at 02:39
  • Thanks, I was just missing the - t in my command – Vinay Kumar Shukla May 14 '21 at 06:58
  • -t is not valid in shell. Trying in docker alpine image. mv says it has only -f -i and -n – The Fool Aug 11 '21 at 05:19
  • 1
    @TheFool that is because Alpine linux uses busybox and the busybox implementation of mv does not have -t as an option. https://github.com/brgl/busybox/blob/master/coreutils/mv.c – lockwobr Aug 18 '21 at 19:09
  • Ah, thanks for the info. – The Fool Aug 18 '21 at 19:12
  • this does not move secret files (eg - files that start with . like .gitignore. Use ls -A | ... to also include hidden files and folder. – TVK Sep 26 '23 at 07:45
21

Just use mv * subdir1 and ignore the warning.

You can just use mv * subdir1. You will see a warning message relating to trying to move subdir1 into itself, like this:

mv: cannot move 'subdir1' to a subdirectory of itself, 'subdir1/subdir1'

But it will move all of the other files and directories to subdir1 correctly.

An example:

$ ls
$ mkdir dir1 dir2 dir3      
$ touch file1.txt file2.txt file3.txt
$ mkdir subdir1
$ ls
#=> dir1  dir2  dir3  file1.txt  file2.txt  file3.txt  subdir1
$ mv * subdir1
#=> mv: cannot move 'subdir1' to a subdirectory of itself, 'subdir1/subdir1'
$ ls
#=> subdir1
$ ls subdir1
#=> dir1  dir2  dir3  file1.txt  file2.txt  file3.txt
Joshua Pinter
  • 537
  • 3
  • 11
  • If you really don't like the warning (maybe this is in a bigger script where the warning would be confusing), mv * subdir1 2>/dev/null. This is not always desirable, though, since there might be other errors reported by mv (e.g. if you don't have permissions). – RoundTower Dec 09 '20 at 00:22
  • 1
    this does not move secret files (eg - files that start with . like .gitignore – NotSoShabby Dec 27 '21 at 14:48
  • @NotSoShabby Interesting! Thanks for posting. I didn't even think of that. Any idea how those can be included as well? – Joshua Pinter Dec 28 '21 at 15:08
  • @NotSoShabby You can use mv * .* subdir1 to move all hidden and non-hidden files into subdir1. – FWDekker Dec 05 '23 at 17:02
11

Simple idea. Assuming you are in /myuser, rename downloads to new, create a new downloads directory then move new into it.

mv downloads new # downloads is now called new
mkdir downloads # create new directory downloads
mv new downloads # move new into it.
jpezz
  • 1,120
1

You can try this alternative process –– remain in the path

/myuser/downloads/

but, instead of first creating the /myuser/downloads/new/ directory, instead create a folder in the /myuser/ directory, with the command mkdir ../new, then move all the files in downloads to new, and finally move new into downloads. You can do this in one line, while in the /myuser/downloads/ path, with the command:

mkdir ../new && mv * ../new && mv ../new ../downloads

In this case, you don't have to worry about any sort of "filtering" of files/folders, since new is on the same level of the path as downloads, so you can just move everything in downloads to new, and then move new into downloads`.

However, if you already have the subfolder new created and don't want to create another one, not to worry –– just change the mkdir command on the left-hand side of the first && in the command shown above to an mv command, pushing new up in the path; in other words, while you're still in /myuser/downloads/, you can change mkdir ../new to mv new ... Then the subfolder new [in the path /myuser/downloads/new/] gets pushed up to /myuser/new/, at the same level as /myuser/downloads/, and then you can run the rest of the command as it is shown above. All together, we have, starting from the path /myuser/downloads/:

mv new .. && mv * ../new && mv ../new ../downloads

and, since you wanted to "move all files and folders/sub-folders in the downloads folder to the sub-folder [new]", you're done! If you had wanted to move only files (or only folders or [insert more granular object movement]), then you'd have to use other commands that can "filter" objects, such as grep. The commands written above are sufficient for your purposes, though.

adamcatto
  • 111
  • If the new folder is nested like CONTEXT_PATH=/foo/bar/baz then this wont work I guess. I had same idea but I put mv $(echo "$CONTEXT_PATH" | cut -d/ -f1-2)" /some/ in the mix. – The Fool Aug 11 '21 at 05:13
1

If you want to move all the files from a folder to one of its subfolders you can use the following command:

find /myuser/downloads/ -type d -name 'new' -prune -type f | xargs mv -t /myuser/downloads/new

It will find all the files and then move them to your subfolder.

@waltinator: added -type d -name 'new' -prune to prevent traversal of /myuser/downloads/new.

waltinator
  • 36,399
demian
  • 11
  • 1
  • I created a subdirectory named new and tried this command, I get mv: missing file operand and Try 'mv --help' for more information – mook765 Oct 13 '16 at 15:42