bash: สคริปต์คัดลอกผู้ใช้

สคริปต์คัดลอกผู้ใช้จากระบบปัจจุบันไปยังไดเรกทอรีที่ติดตั้งลินุกซ์อีกตัวหนึ่ง มีประโยชน์สำหรับติดตั้งลินุกซ์หลายตัว และต้องการให้ผู้ใช้เหมือนกับระบบปัจจุบัน ตัวอย่างการใช้งานเช่น

  • ติดตั้งลินุกซ์ผ่าน debootstrap
  • ติดตั้งลินุกซ์โดยการเมานต์ live cd แล้วคัดลอก squashfs มาติดตั้งโดยตรง

ข้อกำหนดคือ

  • ต้องใช้สิทธิ์ root ในการรัน
  • ต้องเป็นลินุกซ์ที่มีการเก็บไฟล์ผู้ใช้แบบมาตรฐาน คือเก็บที่ไฟล์ /etc/passwd, /etc/group และ /etc/shadow

สคริปต์มีดังนี้

$ sudo vi /usr/local/sbin/transfer_users.sh
#!/bin/bash

function usage() {
    cat <<EOF
Usage: $0 DESTINATION
Transfer users from current Linux system to DESTINATION directory that hosts another Linux system.
Run as root.
EOF
    exit 1
}
    
DEST=$1

if [ "$UID" != "0" ]; then
    echo "Please run as root."
    usage
fi
if [ ! -d "$DEST" ]; then
    echo "DESTINATION directory not found."
    usage
fi
if [ ! -d "$DEST/etc" ]; then
    echo "DESTINATION/etc directory not found."
    usage
fi

US=`ls /home`
TMP=/tmp/${RANDOM}.txt

#SORT ON UID
for i in $US; do
    UUID=`grep ":/home/${i}:" /etc/passwd | cut -d: -f3`
    echo "${UUID}:${i}" >> $TMP
done

#PROCESS EACH USER
for i in `sort $TMP`; do
    UUID=`echo $i | cut -d: -f1`
    U=`echo $i | cut -d: -f2`

    PASSWDLINE=`grep ":/home/${U}:" /etc/passwd`

    #/etc/group
    GNUM=`echo $PASSWDLINE | cut -d: -f4`
    GROUPLINE=`grep $GNUM /etc/group`
    GNAME=`echo $GROUPLINE | cut -d: -f1`
    OLDGROUP=`grep $GNAME $DEST/etc/group`
    if [ "$OLDGROUP" == "" ]; then
        echo $GROUPLINE >> $DEST/etc/group
    elif [ "$OLDGROUP" != "$GROUPLINE" ]; then
        sed -i "s/$OLDGROUP/$GROUPLINE/g" $DEST/etc/group
    fi

    #/etc/passwd
    OLDPASS=`grep ":/home/${U}:" $DEST/etc/passwd`
    if [ "$OLDPASS" == "" ]; then
        echo $PASSWDLINE >> $DEST/etc/passwd
    elif [ "$OLDPASS" != "$PASSWDLINE" ]; then
        sed -i "s/$OLDPASS/$PASSWDLINE/g" $DEST/etc/passwd
    fi

    #/etc/shadow
    SHADOWLINE=`grep "${U}:" /etc/shadow | grep -v '*'`
    OLDSHADOW=`grep "${U}:" $DEST/etc/shadow | grep -v '*'`
    if [ "$OLDSHADOW" == "" ]; then
        echo $SHADOWLINE >> $DEST/etc/shadow
    elif [ "$OLDSHADOW" != "$SHADOWLINE" ]; then
        sed -i "s#$OLDSHADOW#$SHADOWLINE#g" $DEST/etc/shadow
    fi

    #GROUP MEMBERS
    for g in `groups $U | cut -d: -f2`; do
        if [ "$g" == "$GNAME" ]; then
            continue
        fi

        OLDLINE=`grep "${g}:" $DEST/etc/group`
        if ! echo $OLDLINE | grep $U ; then
            if [ "${OLDLINE: -1}" == ":" ]; then
                sed -i "s/$OLDLINE/${OLDLINE}${U}/g" $DEST/etc/group
            else
                sed -i "s/$OLDLINE/${OLDLINE},${U}/g" $DEST/etc/group
            fi
        fi
    done
    
    #/HOME
    if [ ! -d "$DEST/home/$U" ]; then
        mkdir -p "$DEST/home/$U"
    fi
    chown -R ${U}:${GNAME} $DEST/home/$U
done

#for i in passwd group shadow; do
#    cp $DEST/etc/$i $DEST/etc/${i}-
#done
rm $TMP

ตัวอย่างเช่น เราติดตั้งลินุกซ์อีกอันไว้ที่ /dev/sdaXX

$ sudo mount /dev/sdaXX /mnt/tmp
$ sudo /usr/local/sbin/transfer_users.sh /mnt/tmp

ผู้ใช้ทั้งหมดใน /home/* จะถูกคัดลอกไปยัง /mnt/tmp/home/ ตามต้องการ

Topic: 

Comments

แก้ไขไปแล้ว:

  • `cat $TMP | sort` ปรับเป็น `sort $TMP`
  • ลูปตรง #GROUP MEMBERS ไม่จำเป็นต้องมีชั้นใน

ข้อสังเกต:

  • โค้ดส่วน #SORT ON UID จะเกิดอะไรขึ้นถ้ามี /home/dir โดยที่ dir ไม่ใช่ชื่อ user แต่เป็น directory ที่เอามาฝากไว้ในพาร์ทิชัน /home เท่านั้น?
  • โค้ดส่วน #GROUP MEMBERS จะเกิดอะไรขึ้นถ้า group ในระบบต้นทางไม่มีใน $DEST/etc/group ? คำสั่ง sed -i ... จะมีผลอย่างไร?

สาธุครับ แจ๋วมาก
เดี๋ยวมีเวลาจะกลับมาแก้

Creative Commons License ลิขสิทธิ์ของบทความเป็นของเจ้าของบทความแต่ละชิ้น
ผลงานนี้ ใช้สัญญาอนุญาตของครีเอทีฟคอมมอนส์แบบ แสดงที่มา-อนุญาตแบบเดียวกัน 3.0 ที่ยังไม่ได้ปรับแก้