Online Book Reader

Home Category

Classic Shell Scripting - Arnold Robbins [171]

By Root 796 0
files would end up belonging to ben, and jhancock wouldn't be too happy upon returning to work the day after the Great Filesystem Reorganization.

Even worse, though, is the case in which users have files that live outside their home directory. /tmp is an obvious example, but consider a source code management system, such as CVS. CVS stores the master files for a project in a repository that is typically not in any home directory, but in a system directory somewhere. Source files in the repository belong to multiple users. The ownership of these files should also be changed over.

Thus, the only way to be sure that all files are changed correctly everywhere is to do things the hard way, using find, starting from the root directory. The most obvious way to accomplish our goal is to run chown from find, like so:

find / -user $user -exec chown $newuid '{ }' \;

This runs an exhaustive file search, examining every file and directory on the system to see if it belongs to whatever user is named by $user. For each such file or directory, find runs chown on it, changing the ownership to the UID in $newuid. (The find command was covered in Section 10.4.3. The -exec option runs the rest of the arguments, up to the semicolon, for each file that matches the given criteria. The { } in the find command means to substitute the found file's name into the command at that point.) However, using find this way is very expensive, since it creates a new chown process for every file or directory. Instead, we combine find and xargs:

# Regular version:

find / -user $user -print | xargs chown $newuid

# If you have the GNU utilities:

# find / -user $user -print0 | xargs --null chown $newuid

This runs the same exhaustive file search, this time printing the name of every file and directory on the system belonging to whatever user is named by $user. This list is then piped to xargs, which runs chown on as many files as possible, changing the ownership to the UID in $newuid.

Now, consider a case where the old-new-list file contained something like this:

juser 25 10

mrwizard 10 30

There is an ordering problem here. If we change all of juser's files to have the UID 10 before we change the ownership on mrwizard's files, all of juser's files will end up being owned by mrwizard!

This can be solved with the Unix tsort program, which does topological sorting. (Topological sorting imposes a complete ordering on partially ordered data.) For our purposes, we need to feed the data to tsort in the order new UID, old UID:

$ tsort << EOF

> 30 10

> 10 25

> EOF

30

10

25

The output tells us that 10 must be changed to 30 before 25 can be changed to 10. As you might imagine, careful scripting is required. However, we have managed to avoid this problem entirely! Remember the case of duplicate UID numbers with different names?

$ cat dupids

abe:x:105:10:Honest Abe Lincoln:/home/abe:/bin/bash

tj:x:105:10:Thomas Jefferson:/home/tj:/bin/bash

dorothy:x:110:10:Dorothy Gale:/home/dorothy:/bin/bash

toto:x:110:10:Toto Gale:/home/toto:/bin/bash

We gave all of these users brand-new UIDs:

$ cat final.passwd

...

abe:x:4:10:Honest Abe Lincoln:/home/abe:/bin/bash

tj:x:5:10:Thomas Jefferson:/home/tj:/bin/bash

dorothy:x:6:10:Dorothy Gale:/home/dorothy:/bin/bash

toto:x:7:10:Toto Gale:/home/toto:/bin/bash

...

By giving them UID numbers that we know are not in use anywhere, we don't have to worry about ordering our find commands.

The final part of our main program generates the list of find and xargs commands. We have chosen to write the list of commands into a file, chown-files, that can be executed separately in the background. This is because the program is likely to take a long time to run, and undoubtedly our system administrator, after spending hours developing and testing the scripts here, wants to start it running and then go home and get some sleep. Here's the script's conclusion:

while read user old new

do

echo "find / -user $user -print | xargs chown $new"

done < old-new-list > chown-files

chmod +x chown-files

rm merge1 unique[123]

Return Main Page Previous Page Next Page

®Online Book Reader