3

Question

Assuming I have

dir 1/dir 2/file 1
dir 3/dir 4/file 2

(spaces in directory and file names are intentional)

I want to end up with

dir 1/file 1
dir 3/file 2

Related but incomplete answers

This answer moves all the files into the current parent directory (instead of each file's parent directory)

find . -maxdepth 1 -exec mv {} .. \;

This answer prints out each parent directory but I'm not sure how to do the moving.

find . -name '.user.log' | xargs -I{} dirname {}
nwarp
  • 133
  • 3

3 Answers3

7
find -type f -execdir mv "{}" ../ \;

The -execdir is like -exec but does its job in a directory where the particular file is, so the ../ part works as you need.

To get rid of empty dir2 and dir4 in your example you need another specialized command, but the main task is done.

Kamil Maciorowski
  • 69,815
  • 22
  • 136
  • 202
0

for x in */* ; do [ -d $x ] && ( echo cd $x ; echo pwd ; echo mv * .. ; echo cd ../.. ) ; done

Like what you see? Then remove the word echo (from multiple locations)

Might not work in every case, e.g. if symbolic links are used. Tested with OpenBSD ksh (preliminary results looked good on manual visual inspection -- I did not remove echo commands).

Note that [ is a command ; it shares the same man page as test (although since it is an internal command for your shell, you'd want to use the man page for your shell and search for documentation on the command.)

TOOGAM
  • 15,243
  • 4
  • 41
  • 58
  • 1
    shorter: `for x in * ; do mv "$x"/*/* "$x"/ ; done` – quixotic Mar 23 '17 at 05:14
  • 1
    note you won't need the final `cd ../..` in your version; changing directory in a subshell (within the parentheses `( ... )`) won't change the working directory of the external shell. (at least that's how it works in `bash`.) – quixotic Mar 23 '17 at 05:17
  • I dont think this will work unless you first issue IFS=$'\n' or similar (because of the space delimiter) – davidgo Mar 23 '17 at 07:59
  • @quixotic - yup. I was pretty tired when I wrote that; I'm glad to see someone was able to improve on that. I really don't feel like getting reputation benefit/credit from your excellence, though. I'd rather see you post an answer and get well-deserved up-votes. – TOOGAM Mar 24 '17 at 00:31
  • @davidgo it actually does work without `IFS` tweaking. `for x in 'foo a' 'bar b' ... ` wouldn't. i assume the globbing provides the magic. @TOOGAM my answer got bogged down in explaining `IFS` and drowned. hard pass. – quixotic Mar 24 '17 at 00:39
0

You're started correctly. However you can extend it a little in conjunction with the predicate -type to get just files. Using xargs and the bountiful powers of bash you have

find . -maxdepth 3 -mindepth 3 -type f |xargs -n1 bash -c 'cd $(dirname $0) ; mv $0 ..'

$0 is the positional argument passed to bash by xargs. To watch it as it happens you can use the -t option to xargs as -tn1

Note: I've limited the depth of search by using -mindepth $D and -maxdepth $D, feel free to change as per your requirements.

Note 2: Kamil's answer (using -execdir is actually a much better solution).

Samveen
  • 300
  • 2
  • 10