创建一个.sh脚本,用于设置新的用户名、主机名和密码。

我正在为初学者编程课程的学生分发一个虚拟机。为了提高速度,我计划给他们一个完全设置好的镜像,而不是让他们从头开始安装Ubuntu。
为了进行设置,我已经给它一个牺牲性的主机名、用户名和密码。当学生们开始运行时,最干净的方法是如何更改这些信息呢?
我已经做到了这一步gist,在主机名方面有一些不可靠的进展,但用户名却不愿意改变。
我希望这个过程尽可能地无痛苦,因为这应该是他们第一次实验。
完全有可能有一个完全不同的方法,我对所有建议都持开放态度。
#!/bin/bash
clear
toilet  "     CODE1161     "  --metal --font future
echo    ""
toilet  "Let's get started!"  --metal --font future
echo    ""
echo    "We need to make this computer be YOURS"
echo    "We need a name for you and your computer, make it short or it'll take up a lot of space."
echo    "My computer is called um and my name is ben, so I get ben@um:~$ as my command prompt."
echo    ""
read -p "Enter a name for your computer:"  compname
read -p "Enter your name: "  username
echo    ""
toilet  "$username@$compname~$"  --gay --font wideterm
echo    ""
echo    "What do you think? If you hate it, press ctrl+c and do this again"
read -p "otherwise, just press enter." sacrificial

# set the host name, in a million places, for some unknown reason.
echo "1"
sed -i "/127\.0\.1\.1\s*vc/ { c \
127.0.1.1   $compname
}" /etc/hosts
echo "2"
sudo hostname -b $compname
echo "3"
sudo hostnamectl set-hostname $compname
echo "4"
sudo groupadd $username
echo "5"
sudo usermod -d /home/$username -m -g $username -l $username ben

a screen shot of the output progress


1使用 expect - http://www.admin-magazine.com/Articles/Automating-with-Expect-Scripts - Panther
1也许我错了,但我只用过expect来将交互式进程变为非交互式。基本上,就是创建一个脚本,期望某些输出,并在脚本中已经知道如何响应。Ben想要等待用户输入并根据输入执行命令。当然,我每天至少会犯一次错误,所以这可能是个例外情况。 - user508889
其实,对于接收用户输入的部分,我已经做得很好了。一旦我获取到主机名和用户名,它们就会被存储为变量。麻烦的是之后的部分,也就是从注释开始的那一段。 - Ben
1这种方法有一些缺陷。首先,你无法更改当前用户的路径,这是不可能的。当主机名发生变化时,bash提示符不会更新,它只在终端打开时读取一次。你可以使用exec bash,但实际上它会打开一个嵌套的终端。 - user508889
为了解决您的主机名问题,我建议将您的 sed 命令替换为以下内容:sudo sed -i "s/127.0.1.1/& $compname/" /etc/hosts。这将更新 127.0.1.1 行,并在原有内容后追加新的主机名。它会保留旧的主机名,但是允许将多个主机名分配给一个 IP 是符合规范的。这样可以避免您使用其他 sudo 命令更改主机名时出现无法解析的问题。 - user508889
2个回答

为什么不直接使用原始设备制造商(OEM)安装呢?虽然不太美观,但可以完成任务。如果你想选择这种方式,请参考izx对这个问题的出色回答:如何为他人预安装Ubuntu(OEM安装)? 基本上,系统在首次启动时会提示用户输入一系列信息来个性化他们的机器。包括语言、地区、用户名、主机名、密码等等。基本上,就是你的计算机通常在安装过程中会提示你输入的所有内容。
这正是OEM镜像的用途,它们做得非常好。而且,你将让学生们体验到“安装Ubuntu”的小小滋味。尽管Ubuntu的安装并不痛苦。你可能可以在一个课堂上完成安装,并给学生一些作业来个性化和探索他们的虚拟机。此外,分区也是有趣的!

回到手头的问题(以及对你实际问题的回答).....

让我们查看 man usermod 来了解正在发生的情况:

注意事项
   在执行此命令时,您必须确保指定的用户没有执行任何进程,如果要更改用户的数字用户ID、用户名或用户的主目录。usermod 在 Linux 上会检查这一点,但在其他架构上只会根据 utmp 检查用户是否登录。

基本上,这意味着如果用户已登录,我们无法更改用户的 UID、用户名、主目录或类似的任何内容。

因此,我们可以通过在系统下次启动时进行设置来完成一些有趣的操作。不是你的脚本执行工作,而是我们让你的脚本生成另一个脚本来完成所有繁重的工作和操作。以下是一个示例:

# Make the first-run script
touch /etc/init.d/firstrun-setup.sh

# Add in user modifier
echo "usermod -d /home/$username -m -g $username -l $username ben" >> /etc/init.d/firstrun-setup.sh

# Add in group rename
echo "groupmod -n $username ben" >> /etc/init.d/firstrun-setup.sh

# Add in password expiry
echo "passwd -e $username" >> /etc/init.d/firstrun-setup.sh

# Add in file self-destruct
echo "rm /etc/init.d/firstrun-setup.sh" >> /etc/init.d/firstrun-setup.sh

# Mark the file as executable
chmod a+x /etc/init.d/firstrun-setup.sh

# Reboot the computer
reboot

基本上,通过这样做,你可以将事件“排队”以在系统的下一次启动时运行。这将在用户空间发生之前运行,因此系统将以root身份执行这些命令。因此,"ben"用户不会登录,系统也不会抱怨。
您可能仍然需要添加其他命令来允许更多个性化设置(例如设置实际名称等)。但是,这是可选的,只有在您希望在安装脚本期间进行时才需要完成。
虽然,为学生完全创建一个新用户然后保留现有的牺牲账户可能是个好主意。这样,如果发生任何问题,您可以进入并修复/管理他们的计算机,如果有必要的话。当然,如果您给予学生对他们的虚拟机的管理员访问权限,这就有点无关紧要了,因为他们可能会删除“教师”用户,因为他们是学生。

我本打算建议“进行OEM安装”,然后我找到了这个答案 :-) - sudodus
感谢您回答了如何实现我的目标的根本问题,我将选择原始设备制造商安装。 - Ben

在登录界面上,让他们按下ctrlaltF2,然后从打开的终端中以ben的身份登录并运行。
sudo passwd

给他们都设置相同的root密码,即课程编号,这样如果出现问题,你可以进去修复。然后让他们通过输入exit退出登录,并使用新的root密码以root身份登录并运行脚本(可能需要修改脚本以更改ben用户而不是root)。
或者,让他们在登录屏幕上按下ctrlaltF2从终端作为ben用户登录,并从那里运行脚本。这可能有效,因为正如之前的回答中Muru和Kaz Wolfe指出的那样,用户不能运行任何进程,并且通过终端登录不会启动任何进程,例如XUbuntu-Desktop,或其他与usermod冲突的用户空间进程。(我不知道这是否有效,我没有尝试过,但值得一试)。