Skip to content

Instantly share code, notes, and snippets.

@linnil1
Last active December 15, 2021 02:29
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save linnil1/a4974de7e9a86e29f3547ceb73f74f25 to your computer and use it in GitHub Desktop.
Save linnil1/a4974de7e9a86e29f3547ceb73f74f25 to your computer and use it in GitHub Desktop.
Change all UID and GID from old system to new system
# Replacing all UID, GID in directorary
# Requirement: python3.6
# Usage: python3 replace_uidgid.py old_etc_passwd old_etc_group new_etc_passwd new_etc_group /home --dry-run -vv
# Author: linnil1
import os
import argparse
from collections import defaultdict
import pathlib
def readUser(file_path):
"""
Read /etc/passwd or /etc/group like file
return:
{name: id}
"""
users = {}
for row in open(file_path):
row = row.split(":")
users[row[0]] = int(row[2])
return users
def old2newMapping(old_user, new_user, default_id, default_name):
"""
Map old uid to new uid and name
return:
{old_id: {'oldname': "", 'newname': "", 'newid': 0}}
"""
id_map = defaultdict(lambda: dict({
'oldname': default_name,
'newname': default_name,
'newid': default_id
}))
for name, id in old_user.items():
id_map[id]['oldname'] = name
if name in new_user:
id_map[id]['newname'] = name
id_map[id]['newid'] = new_user[name]
return id_map
def mappingSummary(id_map):
for i in sorted(id_map.keys()):
v = max(20 - len(id_map[i]['oldname']), 0)
print(f"{i:6}({id_map[i]['oldname']}) {' ' * v} ->"
f"{id_map[i]['newid']:6}({id_map[i]['newname']})")
def walk(path):
"""
Triversal all the file in the path
yield:
(path, uid, gid)
"""
# root
if os.path.exists(path):
stat = os.stat(path)
yield (path, stat.st_uid, stat.st_gid)
# tree
for dirpath, dirnames, filenames in os.walk(path):
for i in filenames + dirnames:
f = os.path.join(dirpath, i)
if os.path.exists(f):
stat = os.stat(f)
yield (f, stat.st_uid, stat.st_gid)
if __name__ == "__main__":
parser = argparse.ArgumentParser(description='Replace all uid,gid')
parser.add_argument('old_passwd', type=pathlib.Path)
parser.add_argument('old_group', type=pathlib.Path)
parser.add_argument('new_passwd', type=pathlib.Path)
parser.add_argument('new_group', type=pathlib.Path)
parser.add_argument('path', type=pathlib.Path)
parser.add_argument('--default-uid', dest="default_uid", type=int, default=1000)
parser.add_argument('--default-gid', dest="default_gid", type=int, default=1000)
parser.add_argument('--default-name', dest="default_name", type=str, default="Anonymous")
parser.add_argument('--dry-run', dest="dry_run", action='store_true')
parser.add_argument('--verbose', '-v', action='count', default=0)
args = parser.parse_args()
# main
old_user = readUser(args.old_passwd)
new_user = readUser(args.new_passwd)
old_group = readUser(args.old_group)
new_group = readUser(args.new_group)
user_map = old2newMapping(old_user, new_user, args.default_uid, args.default_name)
group_map = old2newMapping(old_group, new_group, args.default_gid, args.default_name)
print("UID Summary")
mappingSummary(user_map)
print("GID Summary")
mappingSummary(group_map)
print(f"Replacing UID, GID in {args.path} ...")
for path, uid, gid in walk(args.path):
new_uid, new_gid = user_map[uid]['newid'], group_map[gid]['newid']
if args.verbose > 0:
print(path)
if args.verbose > 1:
print(f" uid: {uid}({user_map[uid]['oldname']}) -> {new_uid}({user_map[uid]['newname']})", end=",")
print(f" gid: {gid}({group_map[gid]['oldname']}) -> {new_gid}({group_map[gid]['newname']})")
if not args.dry_run:
os.chown(path, new_uid, new_gid)
print("Done")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment