type: learning-note
title: Linux:建组建用户、umask、Python 日志统计脚本
date: 2026-09-17
description: groupadd / useradd / usermod -aG 建组建用户、passwd 设密码与密码三态(L/P/NP)、umask 默认权限(目录 777 / 文件 666 起算)、Python 脚本统计日志各级别条数并落到 kb/产出/
2026-09-17 学习笔记
> 📌 我的 GitHub:https://github.com/AR-firefly
笔记区
核心观点
**建组 = 往 `/etc/group` 追加一行;建用户 = 往 `/etc/passwd` 追加一行**
昨天学的是**看**身份(`id` / `/etc/passwd` / `/etc/group`),今天学**造**身份。造的时候动的还是昨天那两个文件,只不过从"读"变成"写"。
**`useradd` 建一个用户,实际动两个文件**
| 文件 | 加了什么 | 内容 |
| ------------- | ------------------ | ---------------------------------------------- |
| `/etc/passwd` | 一行(这个人) | `s1tester:x:1001:1003::/home/s1tester:/bin/sh` |
| `/etc/group` | 一行(一个同名组) | `s1tester:x:1003:` |
**同名组会自动建,而且直接成为这个用户的主组**------证据是 `/etc/passwd` 第 4 段那个 `1003` 就是 `/etc/group` 里的 `s1tester` 组。
`man useradd` 原文:
> `By default, a group will also be created for the new user`
**同一个 GID,在两个文件里坐的位置不一样**(不是搞错了,是"两个人称视角"):
| 文件 | 那行 | 数字在第几段 | 这行的意思 |
| ------------- | -------------------------- | ------------ | ------------------- |
| `/etc/passwd` | `s1tester:x:1001:1003:...` | 第 **4** 段 | 这个人的主组是 1003 |
| `/etc/group` | `s1tester:x:1003:` | 第 **3** 段 | 这个组的编号是 1003 |
关键方法/流程
**① 建组建用户三件套**
| 命令 | 干什么 | 例子 |
| ------------------------------ | ---------------------------- | ---------------------------------- |
| `sudo groupadd 组名` | 建一个组 | `sudo groupadd s1test` |
| `sudo useradd 用户名` | 建一个用户(**不建家目录**) | `sudo useradd s1tester` |
| `sudo useradd -m 用户名` | 建用户**并建家目录** | `sudo useradd -m s1tester2` |
| `sudo usermod -aG 组名 用户名` | 把用户加进附加组 | `sudo usermod -aG s1test s1tester` |
-
`groupadd` 要 `sudo`:它要往 `/etc/group` 这个系统文件里**写**,普通用户没有写权限
-
`-m` = **create-home**,建家目录
**② `useradd` 默认不给家目录**
`man useradd` 原文:
> `By default, if this option is not specified and CREATE_HOME is not enabled, no home directories are created.`
⚠️ 但 `/etc/passwd` 第 6 段**照样写着** `/home/s1tester` ------ **字段是"登记表",不是"实物"**。登记表上填了地址,不代表房子真盖了。
**③ `usermod -aG` 和那个大坑**
| 部分 | 全称 | 意思 |
| --------- | -------------------- | ---------------- |
| `usermod` | user + mod(modify) | 改一个用户的信息 |
| `-a` | **a**ppend | 追加 |
| `-G` | **G**roups | 附加组列表 |
`usermod --help` 两条原文摆一起看:
-
`-G, --groups GROUPS` → **new list** of supplementary GROUPS
-
`-a, --append` → append the user to the supplemental GROUPS **without removing the user from other groups**
**`-G` 说的是 "new list"(新列表)------不加 `-a` 光写 `-G`,是把附加组列表整个换掉。**
⚠️ **最危险的地方**:我自己的账号在 `sudo` 组里。哪天敲成 `sudo usermod -G docker ar-26710`(少个 `a`),`sudo` 组当场被顶掉,**下一条 sudo 直接拒我,把自己锁在门外**。而 Linux 不会拦,命令会"成功"返回。
**④ 大小写是两个东西:`-g` vs `-G`**
-
小写 `-g, --gid GROUP` → force use GROUP as **new primary group**(改**主组**)
-
大写 `-G, --groups GROUPS` → **new list of supplementary GROUPS**(改**附加组**)
跟第 2 天学的 `-R`(递归)/ `-r`(反向排序)是同一类坑------**同一个字母,大小写两个意思**。
**⑤ 新用户没有密码**
`useradd` 建出来的用户**没有可用密码**,登不进去。设密码:
```
sudo passwd 用户名
```
(注意:`sudo passwd s1tester` 改的是 **s1tester** 的密码,不是我的。要输**两次**新密码,**两次必须完全一致**。)
`passwd -S 用户名` 看状态,第二列三种值:
| 值 | 意思 | 能密码登录吗 |
| ------ | ------------------ | --------------------------------------- |
| **P** | 有可用密码 | ✅ 能 |
| **L** | locked,锁着 | ❌ 不能 |
| **NP** | 没密码(字段空的) | ⚠️ **反而能------不用密码直接进**(最危险) |
`man passwd` 原文:
> `The second field indicates if the user account has a locked password (L), has no password (NP), or has a usable password (P).`
**"锁着"锁的是密码,不是账号**。原理:`/etc/shadow` 里密码字段**前面多一个感叹号**。
`man shadow` 原文:
> `A password field which starts with an exclamation mark means that the password is locked.`
> `...the user will not be able to use a unix password to log in (but the user may log in the system by other means).`
**别的登录方式(比如 SSH 密钥)不受影响。**
**为什么 `useradd` 默认给 `L` 而不是留空**------留空等于开了个不设防的门(`man shadow`:`This field may be empty, in which case no passwords are required to authenticate`)。加感叹号才是把门锁死。
**⑥ umask:新建的文件/目录,权限是谁定的**
每次 `touch` 建文件、`mkdir` 建目录,**从来没指定过权限**------系统有个默认设置管这事,叫 `umask`。
**看**:敲 `umask`
**算**:
| | 满权限起算 | 减掉 umask | 结果 |
| ---- | ---------- | ---------- | ---- |
| 目录 | **777** | 022 | 755 |
| 文件 | **666** | 022 | 644 |
**注意文件那行:起点不是 777,是 666**------文件**天生少一个 `x`**。
用第 3 天学的 **r=4 / w=2 / x=1** 拆 `022`(三位分别对 属主 / 属组 / 其他人):
| 位 | 值 | 挖掉什么 |
| ------ | --- | ---------- |
| 属主 | 0 | 什么都不挖 |
| 属组 | 2 | 挖掉 `w` |
| 其他人 | 2 | 挖掉 `w` |
目录 777 → 属主 `rwx`、属组 `r-x`、其他 `r-x` = **755**
文件 666 → 属主 `rw-`、属组 `r--`、其他 `r--` = **644**
**改**:`umask 077` → 之后新建的变成 600(文件)/ 700(目录)。
⚠️ **`umask` 改动只影响当前这个终端窗口**,关掉重开自动恢复成 022。
**为什么文件从 666 起算**:建文件时,程序(`touch`)跟系统要的就是"读 + 写",**没要执行**。文件**能**执行(`chmod +x` 就行),只是**默认不发**执行权------不然下载的、存下来的每个文件都会变成可运行的程序。
(源码依据:coreutils 仓库 `src/system.h` 里 `#define MODE_RW_UGO` 那个宏,字面上只有读写六个位、一个执行位都没有;`mkdir.c` 里用的是 `S_IRWXUGO`(=777)。"为什么这么定"只有通行说法、没找到权威原文。)
实战记录
**建组 / 建用户 / 加组**
-
`sudo groupadd s1test` → 无输出(成功)
-
`grep s1test /etc/group` → `s1test:x:1002:`(第 4 段空,刚建还没人)
-
`sudo useradd s1tester` → 无输出
-
`grep s1tester /etc/passwd /etc/group` →
-
`/etc/passwd:s1tester:x:1001:1003::/home/s1tester:/bin/sh`
-
`/etc/group:s1tester:x:1003:`(**同名组是 useradd 顺手建的**)
-
`ls /home/` → 只有 `ar-26710`,**没有 s1tester**(没加 `-m`)
-
`sudo useradd -m s1tester2` → `ls -ld /home/s1tester2` 出来 `drwxr-x--- 2 s1tester2 s1tester2 4096 ...`
**加组 / 撞坑 / 加回来**
-
`sudo usermod -aG s1test s1tester` → `id s1tester` = `groups=1003(s1tester),1002(s1test)`
-
`sudo usermod -G s1tester2 s1tester`(**故意不带 `-a`**)→ `groups=1003(s1tester),1004(s1tester2)` ------ **1002 被顶掉了**
-
`sudo usermod -aG s1test s1tester`(这次带 `-a`)→ `groups=1003(s1tester),1002(s1test),1004(s1tester2)` ------ **1004 留住了**
**密码**
-
`sudo passwd -S s1tester` → `s1tester L 2026-09-17 0 99999 7 -1`(**L = 锁着**)
-
第一次设密码两次输入不一致 → `Sorry, passwords do not match.` / `passwd: password unchanged`(**密码没改**)
-
重来一次一致 → `passwd: password updated successfully`
-
再 `-S` → `s1tester P 2026-09-17 0 99999 7 -1`(**P = 有可用密码**)
**umask 对照**
| umask | 新建文件 | 新建目录 |
| ------------ | ---------------- | ---------------- |
| 0022(默认) | `-rw-r--r--` 644 | `drwxr-xr-x` 755 |
| 0077 | `-rw-------` 600 | `drwx------` 700 |
**Python 脚本**
-
样本日志 `~/s1-practice/app.log`(8 行):4 个 INFO、3 个 ERROR、1 个 WARN
-
脚本跑出来:`INFO: 4` / `ERROR: 3` / `WARN: 1`
-
落到 `kb/产出/块1-Python日志统计.py`,在产出目录里跑通(日志路径当参数传)
**踩的坑(真实报错)**
| 报错 | 什么情况 | 怎么修 |
| -------------------------------------------------------------- | -------------------------------------------------------------- | -------------------------------------------------------------- |
| `TabError: inconsistent use of tabs and spaces in indentation` | 缩进里 **Tab 和空格混用**(`for` 那行用空格、`if` 那行用 Tab) | 缩进统一:重写一遍、用 `nano -E`(Tab 自动转空格) |
| `id: 's1tester、': no such user` | 复制时**带进了中文顿号**,整串被当成一个用户名 | 去掉那个顿号 |
| `Sorry, passwords do not match.` | `passwd` 两次输入不一致 | 重来,两次输一模一样 |
| nano 里出不来 | 不知道按什么键 | `Ctrl+X` → 有提示按 `Y`(存)/`N`(不存);屏幕像冻住先按 `Ctrl+Q` |
线索区(学完后合上材料,自问自答)
**Q: 建一个用户,系统动了哪两个文件?各加了一行什么?**
A: `/etc/passwd` 加一行(这个人,7 段);`/etc/group` 加一行(**一个同名组**,是 `useradd` 自动建的)。
**Q: 新用户的主组是谁?为什么?**
A: 是**自动建的那个同名组**。证据:`/etc/passwd` 第 4 段的 GID 就等于 `/etc/group` 里同名组的编号。
**Q: `useradd 用户名` 建出来的用户,有家目录吗?**
A: **没有**。默认不建(`man useradd` 原文),但 `/etc/passwd` 第 6 段**照样写着** `/home/用户名` ------ 字段是登记表,不是实物。要家目录加 `-m`。
**Q: `usermod -G 组A 用户名`(没有 `a`)会发生什么?**
A: 把这个人**现有的附加组全部顶掉**,换成组A。`-G` 的官方说明是 "**new list**"。加了 `-a` 才是"往现有的里追加"。最危险的情况:顶掉自己的 `sudo` 组 → 把自己锁在门外。
**Q: `-g` 和 `-G` 有什么区别?**
A: 小写 `-g` 改**主组**(primary group),大写 `-G` 改**附加组**列表。
**Q: `passwd -S` 出来的 `L` / `P` / `NP` 分别什么意思?**
A: `P` 有可用密码(能登录)、`L` 锁着(不能用密码登录,但别的登录方式不受影响)、`NP` 没密码(**反而不用密码就能进,最危险**)。
**Q: "密码锁着"锁的是什么?**
A: 锁的是**密码**,不是账号。原理是 `/etc/shadow` 里密码字段前面加了个感叹号 `!`。
**Q: 新建一个文件,权限是 644。这个 6 是哪来的?**
A: 文件的**满权限起点是 666**(读+写+写...),减掉 umask 022 里的两个 2(挖掉属组和其他人的 `w`)→ 644。`6` = `rw-` = 4+2,**没有 `x`**。
**Q: 为什么目录的起点是 777,文件是 666?**
A: 目录的 `x` 是**进目录、访问里面东西**必须的,所以要给;文件默认不给 `x`,因为大多数文件是数据不是程序,**默认发执行权太危险**。文件**能**有 `x`(`chmod +x` 就行),只是 umask 不会自动给。
**Q: `umask 077` 之后新建的文件、目录分别是什么权限?改完要做什么?**
A: 文件 **600**(`-rw-------`)、目录 **700**(`drwx------`)。改完记得 `umask 022` 改回去------**这个改动只影响当前终端窗口**,关掉重开自动恢复。
**Q: 脚本里 `open('app.log')` 和 `open(sys.argv1)` 有什么区别?**
A: 前者把日志路径**写死**在脚本里,脚本一挪地方就找不到;后者把路径**从命令行传进来**(`python3 脚本.py 某个日志`),脚本和日志解绑,换任何日志都能用。
总结(50字以内,用自己的话概括今天学到了什么)
会用 groupadd / useradd / usermod -aG 建组建用户,知道 `-G` 不加 `-a` 会顶掉附件组;懂 umask 控新建权限(目录 777、文件 666 起算);写出日志统计脚本并落到产出目录。
复习卡片
| 概念 | 一句话 | 我的场景 |
| --------------------- | ------------------------------- | ------------------------------- |
| `groupadd` | 建组,往 `/etc/group` 加一行 | `sudo groupadd s1test` |
| `useradd` | 建用户,往 `/etc/passwd` 加一行 | `sudo useradd s1tester` |
| 自动同名组 | `useradd` 顺手建,且成为主组 | 看到多出来的 `s1tester:x:1003:` |
| `useradd -m` | 建用户**并建家目录** | 默认不给家目录 |
| `/etc/passwd` 第 6 段 | 家目录**路径**(可能不存在) | 登记表≠实物 |
| `usermod -aG` | 加进**附加**组 | `usermod -aG s1test s1tester` |
| `usermod -G`(无 a) | **顶掉**原有附加组 | 最怕顶掉 sudo 组 |
| `-g` / `-G` | 小写主组、大写附加组 | 大小写两个意思 |
| `passwd` | 设密码,输两次要一致 | `sudo passwd s1tester` |
| `passwd -S` | 看密码状态 | 第二列 L / P / NP |
| `L` / `P` / `NP` | 锁着 / 有密码 / 没密码 | NP 最危险(不用密码能进) |
| 锁密码的原理 | `/etc/shadow` 里加感叹号 `!` | 锁的是密码不是账号 |
| `umask` | 管**新建**的文件目录权限 | `umask` 看,默认 0022 |
| 起算点 | 目录 **777**、文件 **666** | 文件天生少 x |
| `umask 022` 的结果 | 文件 644、目录 755 | 实测对照过 |
| `umask 077` 的结果 | 文件 600、目录 700 | 只有自己能碰 |
| `umask` 作用范围 | **只在当前终端** | 关掉重开就恢复 |
| `sys.argv1` | 命令后面传的第一个参数 | `python3 脚本.py app.log` |
旧知识回收(今天暴露出来的)
| 概念 | 一句话 | 出处 |
| ------------------- | ------------------------------------------ | ------------- |
| `TabError` | Python 缩进里 **Tab 和空格混用**会直接报错 | 今天第一次踩 |
| `nano -E` | `-E` 让 Tab 自动转成空格,避免混用 | 今天第一次用 |
| nano 退出 | `Ctrl+X`(存盘 `Ctrl+O`) | 今天第一次用 |
| r=4 / w=2 / x=1 | 数字法读权限,今天算 umask 时用到了 | 09-08 第 3 天 |
| `ls -l` 的 9 位权限 | 三组 = 属主/属组/其他人 | 09-07 第 2 天 |