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

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
  • 9,312
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
  • 475
2

Move the homedir contents up a directory:

cd /home
for i in */homedir; do /bin/mv $i/* $i/.. && /bin/rmdir $i; done
Cakemox
  • 26,021
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.

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
0

I would simple

cd /home/user/
mv homedir/* .