3

I have about 200 directories in /home/. The problem is that they all exist in this way:

/home/{user}/homedir/

While it should just be:

/home/{user}/

With what command can I mass move all content of homedir one directory up for each user?

Thanks for the help.

Sander
  • 31
  • 3

6 Answers6

5

This might be different depending on your shell, but assuming bash:

for a in $(find /home -maxdepth 1 -type d); do mv $a/homedir/* $a/; rmdir $a/homedir; done

Run the find command separately to verify the list is what you expect before you run the full command and remove the rmdir part if you want to keep the empty homedir in each folder.

SmallClanger
  • 8,947
  • 1
  • 31
  • 45
  • 1
    This will break on directories that contain newlines or spaces. – jordanm Jul 06 '12 at 16:56
  • 5
    Yes it will; but if you're the sort of person that creates user accounts on a system with spaces and newlines in them, then you're already beyond help. :) – SmallClanger Jul 06 '12 at 21:01
  • 1
    its still bad practice, you could have just have easily done this with -exec bash -c '....'. Not everything in /home has to be a user's home directory. – jordanm Jul 06 '12 at 21:21
3

Unless you exceed the command-line length with the globs mv /home/*/* /home should work. Note if you have files in /home/user those will also be moved to /home.

Thor
  • 465
  • 5
  • 14
2

Move the homedir contents up a directory:

cd /home
for i in */homedir; do /bin/mv $i/* $i/.. && /bin/rmdir $i; done
Cakemox
  • 24,141
  • 6
  • 41
  • 67
  • 1
    It might not work for hidden files. Use `shopt -s dotglob` to match those as well. General note: it is assumed that there are no malicious directories and files. For example, a `.ssh` directory with loosely configured SSHD might unexpectedly give shell access to users. – Lekensteyn Jul 07 '12 at 10:25
2

Here's a more paranoid script that avoids all usernames (uses "@@@" for the tmp dir), preserves the dotfiles that the "mv $a/homedir/*" discards, stops if the @@@ dir isn't consistent, and shouldn't have a command length problem. The remaining weakness is pathological user names.

cd /home
ls -1 | while read user ; do   # allow many user dirs without imploding
    [ -d @@@ ] && { echo tmp already present, aborting 1>&2 ; exit 1 ; }
    mv $user @@@
    mv @@@/homedir $user
    rmdir @@@ || { echo tmp dir for $user not empty, aborting 1>&2 ; exit 2 ; }
done

The find() approach is a good choice if the entire per-directory command were buried in the -exec, as in:

cd /home && find -name . -o -type d -prune -print \
     -exec mv '{}' @@@ ';' \
     -exec mv @@@/homedir '{}' ';' \
     -exec rmdir @@@ ';'

Trying something like "for f in $(find ....)" is fragile, since shells will want to expand that entire command line first. The finds shown here depend on {} being expanded by find even within strings, which I'm not sure all finds support.

or even

cd /home && find -name . -o -type d -prune -print -exec \
    sh -c 'mv {} @@@ && mv @@@/homedir {} && rmdir @@@' ';'

Testing:

mkdir -p {fred,jan,alice}/homedir
touch {fred,jan,alice}/homedir/.dotstuff
touch {fred,jan,alice}/homedir/stuff

The let one rip (minus the "cd /home") and see how it does. All seem to work well here.

Alex North-Keys
  • 531
  • 4
  • 6
1

SmallClanger's solution can be changed so that blanks aren't a problem. The trick is to change Bash's internal field separator, which is a variable callend IFS and usually contains a blank and a newline, to just contain a newline. Such as:

IFS=$'\n'

This would change the IFS to a newline character for your current shell.

After that you can run SmallClanger's command, or something the like:

for item in $(find ./homedir -mindepth 1 -maxdepth 1 -type d); do
echo " --> \"$item\"" >&2
mv "$item" .
done

This is assuming your working directory is "/home/{user}".

You may omit the "mv" line when you try it for the first time to see what $item contains. If it looks good, you run it with "mv". Don't forget the double quotes around "$item".

This way you will only run into problems if any of your file or folder paths contain newlines, which I consider to be far more unusual than them containing blanks.

Some information about the internal field separator: Bash: Internal variables

I also think that you can make the "find" program put out its results separated by NULs. If it is possible to set the IFS to NUL, you should be on the safe side for almost anything, as usually NUL is not an allowed code point for files/directories in most file systems. I have never attempted this myself, though.

eomanis
  • 11
  • 2
0

I would simple

cd /home/user/
mv homedir/* .