分类: '经验技巧' 的归档
Keychain和security简介
写 CCAS 的时候要做的事其实很本质:在多个 Claude 账号之间切换。第一个要弄清楚的是 —— Claude Code 把自己的 OAuth credentials 存在哪?
翻一下之前泄露的代码很容易就知道了:在 macOS Keychain 里,service 名字是 Claude Code-credentials。你现在就可以在终端里看一眼:
|
1 |
security find-generic-password -s "Claude Code-credentials" -w |
会打印出一坨 JSON,里面就是 access token、refresh token 这些。
这不是 Claude Code 的”小众”选择 —— 各种 IDE、CLI、Apple 自家的 Safari Mail,所有在 macOS 上要存”密码、token、密钥”的程序,默认就该放进 Keychain。它是这台机器上事实标准的敏感数据存储。
那它到底是什么?

Keychain 是什么
最直白的描述:Keychain 是 macOS 自带的加密键值数据库,专门为存敏感数据设计。每条记录由系统加密,挂在登录用户的身份下,由 securityd 这个常驻守护进程管理。
它的物理形态是磁盘上的几个文件,常见的有:
|
1 2 |
~/Library/Keychains/login.keychain-db # 用户登录 keychain /Library/Keychains/System.keychain # 系统级(如 WiFi 密码) |
你不会直接读这些文件 —— 它们是加密的 SQLite,主密钥跟你的登录密码绑定。读写都要走系统 API,由 securityd 解锁后再返回明文。
它解决了什么
如果不用 Keychain,自己拿个 JSON 文件存 token 行不行?技术上可以,但你要面对几个问题:
1. 磁盘加密
文件丢在 ~/.config/ 下是明文。任何能读你 home 目录的进程都能拿到(包括别的 app、误装的恶意脚本)。即便你给文件 chmod 600,磁盘镜像被拷走照样能看。
Keychain 用 AES 加密,主密钥来自你的登录密码 —— 机器没登录、密码不对,磁盘镜像里的内容就解不出来。
2. 进程隔离
文件系统只有”用户级”权限。但同一用户跑的所有 app 是平等的:Claude Code 写下的 token,理论上你装的任何一个三方 app 都能读。
Keychain 的每条记录带 ACL(access control list),记录”谁创建的、谁可以读”。当一个 app 试图读另一个 app 创建的条目时,系统会弹窗让用户确认。这是文件系统本身做不到的。
3. 用户可见、可管理
打开 Keychain Access.app,你能看到所有条目、谁创建的、什么时候改的,可以手动删。要 export 还得再输一次登录密码。
4. 跨设备同步
勾上 “iCloud Keychain” 之后,相应条目会端到端加密同步到你登录同一 Apple ID 的其他 Mac、iPhone、iPad。Safari 的密码、WiFi 密码、Apple Pay 卡号走的都是这个。
数据模型
Keychain 里有几类 item,最常用的是 generic password(任意 key-value 的密钥/token,多数 app 自定义存储用这个)和 internet password(带 protocol/host/port/path 字段,Safari 存网站密码用)。此外还有 certificate、key(公私钥对)、identity(cert + key 对)。
generic password 的核心字段就两个:
- service:通常用 reverse-DNS 风格的命名空间,例如
com.apple.account或Claude Code-credentials。 - account:同一 service 下区分多条记录的标识,通常是用户名或邮箱。
service + account 唯一定位一条记录。Password 字段本身可以是任意字节(你完全可以塞一整段 JSON 进去,Claude Code 就是这么干的)。
怎么读写:security 命令
Apple 给开发者两套接口:
- C API:
SecItem*这一族(旧的SecKeychain*已经不推荐)。Swift / Objective-C app 一般用这个。 - 命令行
/usr/bin/security:脚本和调试用。
CCAS 走的是后者,省得对接 C API;好处是用户自己在终端就能验证一切。最常用的三条:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
# 写入 security add-generic-password \ -s "com.example.myapp" \ -a "alice@example.com" \ -w "the-secret" # 读出 security find-generic-password \ -s "com.example.myapp" \ -a "alice@example.com" \ -w # 删除 security delete-generic-password \ -s "com.example.myapp" \ -a "alice@example.com" |
-s 是 service,-a 是 account,-w 表示”只把 password 内容打到 stdout”。不加 -w 会打印所有属性元数据,但隐去 password。
更多有用的子命令:
|
1 2 3 4 5 |
security list-keychains -d user # 列出当前 keychain search list security unlock-keychain # 手动解锁 security dump-keychain # 列出当前 keychain 中所有 item 的元数据 security add-internet-password ... # 写网站密码 security find-certificate ... # 找证书 |
几个不那么直觉的细节
search list:系统其实管理着一个 keychain “搜索列表”,security find-* 默认遍历整个列表。常见情况下只有 login.keychain-db,但用户/MDM 可能加挂别的。如果某条记录意外落在搜索列表外的 keychain 里,会出现”dump 看得到、find 找不到”的现象,这时候要在命令末尾显式带上 keychain 路径。
密码存二进制:用 -w "value" 写入时整个参数被当作 UTF-8 字符串。如果 value 里有换行、引号、控制字符,shell 转义会很烦人。更可靠的方式是 -X hex,把内容先转 hex 再交给 security,由它自己 decode 后存原始字节。
-U 不一定是真正的 upsert:add-generic-password -U 文档说”如果存在则更新”,但匹配条件偏严,遇到某些属性差异会判定为”新条目”再插一条。同 service+account 的重复记录会越积越多。要 idempotent 的话,先 delete 再 add 更稳。
account 不是严格过滤:find-generic-password -s SVC -a ACCT 如果 account 没精确匹配,可能 fallback 命中同 service 下别的 account —— 不报错,直接返回别人的内容。读完后再校验返回值是个好习惯。
小结
Keychain 是 macOS 上存敏感数据的标准答案。它做的事不复杂 —— 加密、隔离、用户可控、可同步 —— 但是把这四件事做齐了,且对开发者基本零成本。
下次写 Mac app 要存 token、API key 或者密码,不用犹豫,直接 SecItem / security 走起,比自己掂量”放哪、加什么密、谁能读”省太多事。
uv 的 inline script metadata 太香了
最近又玩了下 uv,感觉它在“小工具开发”这个场景里特别顺手。
uv 本质上是一个把 Python 依赖管理、虚拟环境、脚本运行这些事情揉在一起的工具。它最大的优点当然是快,但更让我喜欢的是:很多原本零碎又烦的动作,被压缩成了几个很好记的命令。
比如平时写个小脚本,常常只是想:
- 调一下接口
- 批量处理一点文件
- 验证一个第三方库
- 给自己做个顺手的小命令
这类东西通常不值得认真建个项目,但又往往会依赖 requests、httpx、rich 之类的包。以前一般要先建 venv,再装依赖,可能还要改个 .gitignore,多少有点麻烦。
这时候 uv 很方便的一个点,就是可以直接用 --with:
|
1 |
uv run --with requests demo.py |
或者:
|
1 |
uv run --with httpx --with requests foo.py |
这种方式特别适合刚开始写小工具的时候。先跑起来,先验证思路,不用一上来就准备完整项目。
不过,--with 也有个问题:依赖信息是在命令里,不在脚本里。
今天你记得怎么跑,过几天自己可能都忘了。发给别人时,也得额外告诉对方要带哪些依赖。
这时候真正的大招就来了:inline script metadata。
你可以直接把依赖用这样的格式,写到脚本头部:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 |
# /// script # requires-python = ">=3.11" # dependencies = [ # "httpx", # "rich", # ] # /// import httpx from rich import print resp = httpx.get("https://luy.li") print(resp.status_code) |
可以看到,去掉# /// script这些包裹层之后,里面的内容,其实就是TOML的语法。
这样之后就不用再写长命令了,直接:
|
1 |
uv run foo.py |
就行。这个体验非常好。因为脚本自己就说明白了:
- 需要什么 Python 版本
- 依赖哪些包
- 应该怎么运行
更进一步,你甚至可以再加个 shebang,把它写成一个可以直接执行的脚本:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 |
#!/usr/bin/env -S uv run --script # /// script # requires-python = ">=3.11" # dependencies = [ # "httpx", # "rich", # ] # /// import httpx from rich import print print(httpx.get("https://luy.li").status_code) |
这样给脚本加上执行权限(chmod +x)之后,连 uv run foo.py 都可以省掉,直接 ./foo.py 就可以跑起来了。
最后总结一下:
- 刚开始随手写个小工具,用
uv run --with ... - 觉得这个脚本值得留下来,就上
inline script metadata - 再彻底一点,可以用
#!/usr/bin/env -S uv run --script这个shebang
OpenClaw分享
以下PPT和内容,来源是我跟一群爱学习的朋友一起学时下大火的OpenClaw小龙虾,我给大家做了个小分享。
PPT几乎是OpenClaw输出的内容,下面的会议总结也是GPT根据会议录屏总结的。
需要注意的是,我其实没有将小龙虾玩得很深,因为我从心底里是不信任AI的,也就不敢给它太多权限。所以内容也都比较浅,请见谅。
OpenClaw 会议总结
这次分享的核心,不是在介绍一个“聊天机器人”,而是在介绍一套可自托管、可扩展、可执行任务的个人 AI 基础设施。分享者把 OpenClaw 定位为“装进手机里的 AI 助手”:用户通过 WhatsApp、Telegram 等聊天入口发出请求,背后由部署在自己电脑或服务器上的 OpenClaw 网关完成会话管理、记忆注入、工具调用和本地执行,再把结果返回到聊天端。PPT 对这一定位和整体链路描述得很清楚。
点击查看全文 »
局域网影音解决方案——Jellyfin
先交代下背景:我本来是打算在家里搞个NAS的,但由于最近硬盘和内存都疯狂涨价,加上其实需求也不是那么迫切,就一直没有去折腾。
另外,家里其实已经有一个小主机(对,本blog就跑在上面呢)了,它带了1TB的SSD。所以偶尔有下载电影、在不同的屏幕(手机、pad、电脑、电视机等)看电影的需求,就想着把这个小主机再压榨一下吧。
于是有了以下的折腾:
下载的需求,其实很好搞定。但下载完之后,电影在小主机上,要在各种设备上播放,我一个想到的是用SMB在局域网里共享目录,这确实是一个方案。但这个方案有两个问题:
- 不是所有的(屏幕)设备都能找到支持SMB的播放器。
- 有些设备的解码能力太弱,遇到高码率的4K视频,就会卡住
所以,最好的方式就是再充分压榨这个小主机,因为它其实有个显卡(CPU:AMD Ryzen 3 7330U(集成 Radeon iGPU)),可以用于服务端转码。于是就给它装了个 Jellyfin。
Jellyfin是个开源的、完全自托管的影音解决方案,特别适合喜欢折腾的人。
安装过程我也偷懒了,没有去编译源码、调试环境,而是直接采用docker运行了,在我的Ubuntu下,大致如下:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 |
# 添加docker的key和源 curl -fsSL https://download.docker.com/linux/ubuntu/gpg | \ sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg echo \ "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] \ https://download.docker.com/linux/ubuntu \ $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \ sudo tee /etc/apt/sources.list.d/docker.list > /dev/null # 安装 docker 和 compose 插件 sudo apt update sudo apt install docker-ce docker-ce-cli containerd.io docker-compose-plugin |
接着,建一个 Jellyfin 的空目录,在里面建一个 docker-compose.yml 内容如下:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
services: jellyfin: image: jellyfin/jellyfin:latest container_name: jellyfin ports: - "8096:8096" volumes: - ./config:/config - ./cache:/cache - /home/shared:/media:ro devices: - /dev/dri:/dev/dri environment: - TZ=Asia/Singapore - LIBVA_DRIVER_NAME=radeonsi restart: unless-stopped |
其中,/home/shard 是存电影的目录。
然后,在此目录下,执行 docker compose up -d,不出意外的话,你的Jellyfin就应该能正常启动了。这时候就可以访问 http://192.168.你的.IP:8096 进入web管理界面进行进一步的设置了。
设置完管理员账号之类的信息以后,需要注意的一点,就是开启转码的硬件加速。做法是在 Dashboard → Playback → Transcoding 里
Hardware acceleration 选择 Video Acceleration API (VAAPI),这是AMD的芯片+Linux系统的选项,其他的硬件、软件的组合,可以参考官方文档。
VAAPI device 就选择 /dev/dri/renderD128,如果前面的 docker-compose.yml 没有写错,这里应该是可以选到的。
这么做了以后,在客户端有播放视频的时候,即使是播放的4K视频,CPU利用率也不应该太高。如果是CPU持续打满,就建议再看看硬件加速的配置。
配置好以后,家里的几个屏就都可以无缝切换看小主机里的电影了!因为每个屏至少都有浏览器可用,直接浏览器打开上述地址就可以了。当然,安装最佳实践来说的话,在有条件的情况下,最好使用官方客户端来看,而不是只用浏览器。因为用浏览器就会把所有的解码压力,都丢给服务端。尤其是你的端侧算力很强(比如Mac)的时候,用客户端可以直接拿原始视频到端侧解码!这样不仅服务器的压力更小,而且画质也是无损的。体验也会更好!
那么,如果大家还有比较全、质量又比较高的电影下载源(最好是magnet协议的)的话,也请留言告诉我!谢谢!
终于搞清楚Google账号的所属国家的逻辑了
如果你买过YouTube premium,或者加过其他人买的YouTube premium family套餐,后续又因为种种原因(比如Google 封禁了假的土耳其区等)退出了premium计划,那你再想加入另一个family的时候,大概率会看到这么一个提示:告诉你“您似乎与邀请您的人不在同一个国家/地区。”,从而“无法加入家人群组”。

然后,你可以会在Google账号的后台管理账号做各种尝试,包括用不同国家的VPN,拿到特定的IP地址、修改账号的住址信息、添加不同国家的信用卡作为付款方式等等,但我猜这大概率都解决不了你的问题!
我曾经也因此折腾了很久,知道最近,才明白怎么查看以及修改这个字段。先说好:方法仅供参考,也别拿大号玩得太花,不然被封就嘎了~
查看:
先得有办法确认当前的账号所属国家,才知道要不要去做修改。
这个方法很简单,登陆Google Play的web页面,登陆以后,确定右上角的头像是要查看的账号头像,然后把页面拉到最底部,右下角就会显示 国家(语言)。
这可能是唯一精确知道目前账号所属国的办法。
修改:
如果确定了你的国家不是你想要的,要怎么修改呢?
点击查看全文 »
WordPress文章如何同步到X
其实,十几年前,我的blog就有这个功能的,比如:
luy.li 新文章: mysql主备部署笔记 ( https://luy.li/2011/05/11/mysql_replication/ )
— 骨头 (@senob_) May 11, 2011
当时用的是一个叫 WP2Twitter 的 WordPress插件,当时的逻辑也非常简单,有新post的时候,就把标题和URL拼一个字符串,直接往Twitter发了,而且那时候Twitter的权限也比较松。
最近重拾blog以后,想着也把这个功能恢复一下,而且现在不是有AI了嘛,其实还可以更进一步,不仅可以输出标题,还顺便把文章的内容做个大致的摘要,可以让有兴趣的朋友点进来,也可以避免浪费对此内容不感兴趣的朋友的时间。
搜了一下IFTTT、RSS转X等方案,其实倒是有很多平台都能实现,但要么功能过于强大,搞得很复杂;要么需要订阅,要交一笔费用。后来眼光还是回到WordPress插件上来了,还真让我找到一个至少非常适合我的插件,分享给大家,如果有需要的话,也可以参考。
这个插件叫 Automator,其功能非常强大,可以连接WP和各种社交媒体和平台,部分功能也需要订阅pro,但发布到X,则是免费版就够用的。
点击查看全文 »
自主控制WordPress的图片尺寸
你往WordPress上传一张图片的时候,它有可能会给你在后台生成7、8张不同尺寸的图片,比如这样:

其实这对于大部分人来说,都是一个蛮好的功能。比如小白可能会把手机拍的几个MB的图片直接上传上来,那对于web显示可能就太大了。有个自动缩略就很合适了,既能节省带宽,页面加载又快。
但我偏偏是一个喜欢“手动档”的人,我想自己来控制这些尺寸,上传前就会对图片做适当的压缩。这样服务器里也就不用存储这多张图片了。
问了GPT,以下方法对我是有效的:
- WordPress后台 → 设置 → 媒体,把缩略图/中等/大的宽高都设为 0,保存。//这里能少3个图片
- 在WP主题的
functions.php里,加入以下代码:
|
1 2 3 4 5 6 7 8 |
// 移除不必要的图片尺寸 add_filter('big_image_size_threshold', '__return_false'); add_filter('intermediate_image_sizes_advanced', function ($sizes) { foreach (['medium_large', '1536x1536', '2048x2048'] as $s) { unset($sizes[$s]); } return $sizes; }); |
完事,现在你传什么,服务器就只存什么了。
PS:查资料的过程中,还了解到现在浏览器有个srcset响应式图片,大致是,HTML的img标签里,除了正常的src以外,还会提供一个srcset,然后浏览器就可以根据当前屏幕的尺寸来决定具体请求那个资源了,下面是个例子,听起来是挺不错的。
|
1 2 3 4 5 6 7 |
<img src="IMG_2184-768x384.jpeg" srcset=" IMG_2184-300x150.jpeg 300w, IMG_2184-768x384.jpeg 768w, IMG_2184.jpeg 1214w" sizes="(max-width: 768px) 100vw, 768px" /> |
折腾自己的blog,而不是直接用平台,就是有这些乐趣(如果你也觉得这是乐趣的话),哈哈~
找回丢失的磁盘空间
经常接触linux,尤其是多人共用的服务器上的linux的朋友,也许会经常遇到这样的问题:
收到一个磁盘告警,说某某分区已经满了,然后登录服务器 df 一看,发现磁盘确实快满了,然后你就想找到具体是哪个目录满了,于是 du -s * 一看,却发现所有子目录的大小总和却和df显示的总已使用磁盘空间对不上,有时候甚至还相差很多,于是就纳闷了:我的磁盘空间去哪了呢?
这里就列一下我所知的3种情况:
- 隐藏文件
- 非空目录被mount
- 空洞文件
linux系统把文件名以.(点号)开头的文件视为隐藏文件,而类似bash里*这样的操作符是不会匹配隐藏文件的,所以如果根目录下有个较大的隐藏文件的话,是不会被du -sh * 统计到的,解决办法就是: du -sh .[^.]* 。
一般挂载其他分区的时候都是建议mount到一个空目录的,那么如果mount到一个非空的目录,情况会怎么样呢?比如/mnt目录本来里面是有文件的,然后执行了sudo mount /dev/sda6 /mnt。其实这个mount命令完全能正常执行,被mount的分区也能正常访问,只是原先在/mnt里的文件,现在已经访问不到了,包括du也看不到大小了,但是磁盘空间却还是被占着,因为如果你 umount /mnt 以后,原来的文件都还会回来的。
一个文件的大小和所占磁盘空间也不一定完全一致,比如某个程序一直打开着一个叫log的文件在写,而中间有人用 > log 命令清除了log的内容,就会产生这样的文件。这里有这种文件的介绍。
上面3种是我所知的,应该还有其他情况,欢迎留言补充。
用RG100A实现ADSL拨号
最近又搬家了,需要重新办理宽带。问了下电信和网通的价格,虽然华数网通便宜很多,但是那句“一分钱,一分货”还真是一点都没错,之前用过才知道网通不是一般的垃圾,详见我以前的牢骚,所以还是办理了电信宽带。
这电信的入户方式和网通不一样,是电话线入户的,也就是说局端接过来的是一个RJ11的水晶头,然后电信会给一个modem,一般的上网方式就是在modem后面接一个无线路由,再用路由拨号。
但是这样显然不够方便,因为我的路由器是淘宝上淘的RG100A-AA,上面是有RJ11接口的,如果能用路由器直接拨号,就可以抛开电信给的modem了,这样会环保很多(至少modem的电源就不需要了)。
然而,我的路由器默认刷的是OpenWRT的固件,这固件是完全开源的,功能非常强大,什么电驴、BT、samba、FTP、print server统统支持,但是唯独不支持adsl拨号。。。查其原因,据说是BCM系列芯片的adsl驱动是dlink享有版权的,并且不开源。想要用adsl功能必须给路由器刷上基于dlink的固件。
了解到这个信息以后,就开始搜索靠谱的dlink固件,最后我用的是这里下载(需注册)的,这个版本有web界面(英文)、有ssh。基本就可以玩了。
下载后在原来的OpenWRT的web界面里直接上传文件,再重启就刷好固件了,这步真是超乎想象的简单。
再次登录,就是dlink的界面了,默认的用户名和密码都是admin,进去以后,就可以进行一堆设置了,诸如wifi的ssid、密码之类的,都是大同小异,唯一值得一提的是ADSL拨号的设置:
要先在’Advanced Setup’里选择’Layer2 Interface’,就是OSI7层模型里的第二层了,下面有个’ATM Interface’,需要add一条记录,add的时候,会让你填两个数值,就是 VPI 和 VCI ,这两个值各地的运营商都会不一样,比如我这里是杭州余杭电信,这两个值是8/35,具体的值可以到网上查或者是打10000询问电信(不知道电信会不会告诉你)。关于ATM层的更多介绍详见这里。
‘ATM Interface’ 设置好以后,就可以设置’WAN Service’了,这里需要选择刚刚设置好的ATM设备,比如我这是 ATM0/0_8_35 ,然后下一步就是填个账号和密码之类的,保存就会自动拨上ADSL了,整个过程还是蛮有趣的,还可以顺便学习一下底层的网络知识。
另外,据说已经有人把dlink的adsl模块二进制地移植到了OpenWRT了,并且已经可用了,说不定哪天我就回OpenWRT了,哈哈。
sudo的配置
大家都知道,root权限是linux系统中的最高权限,有了root权限,就可以对系统做任何操作。
但是,很多情况下,这样一个笼统的root权限并不能很好地满足需求,比如,有时候想让系统的某几个用户有装包的权限(就是执行apt-get或者yum什么的),但是不能随便更改其他系统配置;又比如,想让某个用户有杀死指定另外一个用户的进程的权限(比如www用户什么的),但是也不能随便杀其他用户的进程。
这样一来,便有了细化这个“最高权限”的需求了。于是,权限管理的一大利器——sudo——便应运而生了。
可能是由于sudo的需求本来就比较复杂,看我上面说的例子,用口语表达都比较拗口;也可能是有sudo需要的安全级别比一般的程序要高一些。导致sudo的配置,看上去有点凌乱和摸不着北,所以这里稍微解释一下。
首先,sudo的配置文件是 /etc/sudoers,虽然你也可以手工打开、编辑、保存。但还是建议使用visudo命令来编辑。这是因为:
- 它能够防止多个用户同时修改它
- 它能进行有限的语法检查
- 它能避免因权限位出错而不被sudo认可
然后,打开配置文件以后可以看到有这几部分:
- Host_Alias、User_Alias和Cmnd_Alias,分别是主机、用户和命令的别名。
- Defaults一些默认的特性,比如默认要不要重设环境变量,重设哪几个环境变量,默认以谁的身份执行等等。
- 后面就是重头戏了,类似这样的一行:
root ALL=(ALL) ALL
其实这行是 user machine=(users) commands 这样的格式,也就是,允许在machine登录的 user 用户以users的身份来执行commands命令。这里的machine、users呵呵commands就可以用Host_Alias、User_Alias和Cmnd_Alias来代替了。 %group ALL=(ALL) ALL
和上面的一样,只不过这个按组来限制权限。
还是用例子来说话吧,接着上面的两个例子。
例1. 让系统的某几个用户有装包的权限(就是执行apt-get或者yum什么的),但是不能随便更改其他系统配置。
做法就是将这几个用户加入某个特殊的组里(当然,如果你愿意也可以一个个用户分别设置,哈),比如建了yumer组,把用户都加进去了,然后sudo的配置加上:
%yumer ALL=/usr/bin/yum
例2. 让某个用户(dev)有杀死指定另外一个用户的进程的权限(比如www用户什么的),但是也不能随便杀其他用户的进程。
配置如下:
## Processes
Cmnd_Alias PROCESSES = /bin/kill, /usr/bin/kill, /usr/bin/killall, /usr/bin/pkill
Defaults:dev runas_default=www
dev ALL=(www) PROCESSES
先把几个命令alias成一个 PROCESSES,然后指定dev用户,有以www用户的名义执行PROCESSES里的程序的权限。
本来,dev用户必须使用 sudo -u www kill 1111 来杀死www用户的1111号进程的,但是加个-u显然麻烦,所以有了一行: Defaults:dev runas_default=www
这行的意思,是让sudo知道,只要是dev用户执行的,默认就是www的身份,而不是一般的root身份。
这里只是通过两个简单的例子介绍了sudo最常用的功能,其实sudo还有很多其他的有趣功能。比如,sudo还可以实时将非法操作检测出来,以多种方式记录到日志里,不只是本地日志,还可以通过http等方式传到别的机器等。更多功能,当然得参见man页了,哈哈。