Dave, how can I flatten a directory structure? I’ve got a directory “foo”, with many more sub-directories and files within. I want to take all the files from all levels beneath foo and put them in foo itself.
I’ve tried this: find . *.mp3 -print0 | xargs -0 mv . but I get an error on the mv command that I can’t figure out. Help!
You’re definitely on the right path here. To extract all files in a subdirectory the find command is the correct program, and using the Mac-specific extension “-print0” coupled with the xargs “-0” lets you handle those annoying filenames with spaces in them.
There are two problems with what you have here, though: first off, the syntax of mv is essentially move a to b, but you’re ending up supplying the arguments in the wrong order, because xargs appends the filenames to what you specify, essentially creating “mv . folder/file1 folder/file2”, etc. You can see where that’s going to confuse the command!
The second problem is a bit more subtle: as the program proceeds, I wouldn’t bet that the ‘.’ is going to be interpreted properly and I would strongly suggest that instead you specify the directory name of your target anyway.
Unfortunately, the xargs man page is pretty darn confusing and doesn’t help much. What you want to do is use the -J flag, specify a pattern (or single character), then embed that in the subsequent command. Everwhere the pattern appears, it’ll be replaced by the stream of filenames produced by the find command.
This’ll make more sense with an example, so let’s jump in!
$ pwd /Users/taylor/Desktop/foo $ find . -type f -name "*mp3" -print0 | \ xargs -0 -J% mv % /Users/taylor/Desktop/foo $
That will do what you seek. When you’re ready to delete all the now-empty directories, don’t forget you can use find again, just specify -type d to match directories but not files.
Good luck!
-print0 is a GNU/BSD extension !
The ‘find | xargs’ examples should run a lot faster then a ‘find … –exec’ operation as you’d have to create one process for each file as opposed to xargs which will make as few processes as possible based on the length of the data on the stdin.
On Solaris I usually will do something like ‘find … | sed -e “s/.*/’&’/g” | xargs …’. This will wrap each line in single quotes.
Keep up the good tips.
David, I don’t think that’ll work because he’s trying to pull files from lots of different subdirectories at the same time, and even in “list” view the Finder has a tendency to want to move the folders that files are in when you do large multi-directory grabs…
Um … how about using the Finder?
In the Finder, File:Find…
Search in: “Specific Places”
Click on “Add” button and choose the foo folder
Search for items whose name ends with .mp3
The select all the results and drag them to foo.
Okay, Ken, this is new to me, even though I’m a big fan of my two iPods and iTunes on all my Macs. Are you asking if there’s a way to unprotect the files and/or defeat the Playfair DRM?
“Protected” in the context “protected iTunes music files” refers to files of type .m4p, which are AAC files (ordinarily .m4a) that are “protected” with Apple’s Playfair DRM wrapper.
Great. Thanks for coming back and completing the loop. I knew that the GNU version of xargs had to have some solution to this.
Here’s the final, working command for linux:
find . -type f -name “*mp3” -print0 | xargs -0 –replace=% mv % `pwd`
“I’d like to make a cron job that backs up only the protected iTunes music files in my iTunes directory complete with directory structure.”
I don’t know what “protected” means in this context, but I’ll say that if you want to back up an entire directory structure, you can typically use either cp -R or cpio (use ‘man cpio’ to see how this useful program works).
Good point, Avi, but you ironically demonstrate why I avoid the curly brace notation in find by your mistake: In fact, you need to escape the curly braces AND you need to have an escaped semicolon too for it to work properly, as in:
find . -type f -name “*.mp3” -exec cp \{\} /Users/taylor/Desktop/foo \;
A good solution, though, if you’re willing to fiddle around and get the backslashes right. 🙂
Linux also has -print0 in find and -0 in xargs. -J is not in Linux but I guess that –replace can be used instead. The one qustion I have though is why not use
find . -type f -name “*.mp3” -exec cp {} \
/Users/taylor/Desktop/foo
instead of find and xargs?
How Can I Flatten A Directory Structure In Mac OS X?
Dave, how can I flatten a directory structure? I’ve got a directory “foo” with many more sub-directories and files within. I want to take all the files from all levels beneath foo and put them in foo itself. I’ve tried this: find . *.mp3 -print0 | xar…
How can I flatten a directory structure in Mac OS X?
Dave, how can I flatten a directory structure? I’ve got a directory “foo” with many more sub-directories and files within. I want to take all the files from all levels beneath foo and put them in foo itself. I’ve tried this: find . *.mp3 -print0 | xar…
Thanks for responding!
OK, that looks great, but -print0 is implemented on my Suse 9.1 box just fine (not a Mac!). Also, -J isn’t implemented in Suse 9.1, so I’m still stuck.
I’d also add `pwd` to the end so as to not have to hard code the destination so it can be put in a shell script.
How about one step further?
I’d like to make a cron job that backs up only the protected iTunes music files in my iTunes directory complete with directory structure. I’ve tried various cp commands and never can seem to figure it out.
How would you go about doing that?
How can I flatten a directory structure in Mac OS X?
Dave, how can I flatten a directory structure? I’ve got a directory “foo”, with many more sub-directories and files within. I want to take all the files from all levels beneath foo and put them in foo itself. I’ve tried this: find . *.mp3 -print0 | xa…