微信文件变成「只读」?我用 ChatGPT 找到了解决方法

:本文记录了作者在没有过多终端脚本基础的情况下,通过「咨询」ChatGPT 从无到有实现自动修复微信文件权限的过程。如果你对终端环境比较熟悉,文中提到的步骤和方法可能有些基础,解决方案也不一定是最优的。但我们认为这个探索过程是很有意义的,也体现了生成式 AI 与自动化结合的应用场景,值得推荐。如果你需要本文方案的最简化、一次性实现,可以在终端运行:find “$HOME/Library/Containers/com.tencent.xinWeChat/Data/Library/Application Support/com.tencent.xinWeChat” -type f -path */*/Message/MessageTemp/*/File/* -exec chmod 644 {} \;引言不知道从什么时候开始,在 macOS 上微信收到的文件都变成了只读,每次打开新文件,都需要另存一份。对于每天都需要用微信收发大量 Word 文档的我,这个改动非常折磨人。当然,最好的办法是不用微信,但这显然不是打工人可以决定的。看到这行字就难受好在,经过一些研究,我发现这个问题还是能够简单解决的,这里也将探索的过程分享给大家。需要说明的是,我不是相关方面的专业人士,本文所展示的几种方法未必是最合适的解决方案,特别是相关脚本是否存在风险还请自行判断。

一、自动操作此前,我曾经在网上学过一个将 Word 文档保存为 PDF 文件的快速操作(Quick Action)指令,用途是批量将 Word 转换为 PDF 文档。因此,这次我首先也想到通过 macOS 系统里自带的自动操作(Automator)来实现这个目标。修改 Word 文档等文件的权限,至少有三种方法。第一种是打开文档然后复制或另存文件;第二种是对文档右键选择「显示简介」,拉至下方将用户的权限修改为读和写(Read & Write)。第三种方法看起来更像是适合批量操作的,用到的是 chmod 命令,具体而言,打开终端(Terminal.app),输入chmod 644 /path/to/file即可赋予用户读写的权限。其中,644 三个数字分别对应用户(所有者)、用户组和其他(所有人)的权限。4 代表读的权限,2 代表写的权限,1 代表执行权限。用二进制写出来的话,就不难想象 6(二进制 110)可以表示读(4,二进制 100)加上写(2,二进制 010)的权限。至于文件路径,把需要的文件拖进终端就可以,注意 chmod、644 与文件路径之间均有空格。图片来自网络知道这个命令以后,我开始写快捷操作。一开始,我按照之前 Word 文档转 PDF 文件的经验使用了 AppleScript,告诉(tell)终端(Terminal.app)去运行这行 chmod 命令。问题是,不仅运行时候会有个终端弹出,运行结束后终端也不会关闭,一直保留在 dock 栏上,即使按照网上的说法,使用 exit 命令加上修改 profiles 也不能完全解决。奇怪于网络上为什么几乎没有人遇到相似问题,我才在检索中发现,根本没有必要使用 AppleScript 打开终端,在自动操作中运行一个 Shell 脚本就可以了。完整的操作过程如下:• 打开 macOS 自带的应用程序「自动操作(Automator)」,在弹出的新建窗口中双击选择「快速操作(Quick Action)」。• 在左侧的工具中找到,或者直接搜索「Shell」找到「运行 Shell 脚本(Run Shell Script)」并双击或将其拖至右侧窗口。• 在右侧窗口设置为收到「文件或文件夹(files or folders)」,位于「访达(Finder.app)」,下方传递输入修改为「作为自变量(as arguments)」,然后在下方自动生成的模板中,将 echo “$f” 这一行替换为 chmod 644 “$f” 就可以了。整体如下图所示:Shell 中引用变量需要加 $,就写下面那点东西的时间里我可能就忘了 50 次吧最后,保存并命名(比如:「修改权限」)。此外,也可以在上面的设置中修改图标和颜色。之后,在访达中,对任意文件右键选择「快速操作(Quick Action)」,点击刚才添加的「修改权限」操作,就能够立即将文件权限变更为读和写。到这一步,问题得到了初步解决,在每次保存微信文件后,我只需要通过右键点击目标文件,再从快捷操作中选择上面创建的内容,就可以让随后打开的文档不再是「只读」。只不过,这样仍然有点麻烦,特别是忘记这一操作径直打开文档时,仍然有不通畅的感觉。

二、监控文件变动:认识 fswatch 工具

我的想法是,当我将微信中的文件保存到某个文件夹,如果这个文件夹里发生的变化能够被监测到,而发生变动后系统就自动运行一次上面的授权脚本,那么就不需要我手动点击快速操作按钮。从这一步开始,我想到了 ChatGPT。过去,我拿 ChatGPT(GPT3.5 版本)测试回答工作中遇到的法律问题,得到的结果并不令人满意。但考虑到 ChatGPT 在分析代码方面的能力,我非常期待它在帮助我这样外行人时的效果。结果是,ChatGPT 的表现令我感到惊喜,在很多时候比搜索得到的结果更贴合我的问题,也更详细和耐心。不过,在部分问题中,ChatGPT 仍然会犯错,需要自行甄别或通过网络去检索更多和更新(尤其是在 ChatGPT 数据集截止日期以后)的解决方法。

(一)fswatch 工具的安装

我询问 ChatGPT 的第一个问题是在 macOS 中是否有监控文件夹内文件发生变动就运行脚本的方法,ChatGPT 告诉我 fswatch 工具可以实现这个目标。ChatGPT 告诉我 fswatch 需要通过 Homebrew 安装,fswatch 的 GitHub 页面也给出了其他安装方法。在安装 Homebrew 后,在终端中输入 brew install fswatch 即可安装 fswatch。只是,在对话过程中,ChatGPT 偶尔会忘记我在使用 macOS 这一前提,比如给我推荐 inotifywait 工具,在我尝试失败并检索后才发现,这是一个 Linux 上的命令。欺骗了我的一些感情和一点时间

(二)选择被监控的文件范围

在脚本中,只需要输入 fswatch 文件路径 就可以监控指定文件夹,同时,也可以添加一些更细化的参数以选择监控文件的范围。在这方面问题上,可能由于 fswatch 自身的版本更新,ChatGPT 有时候能够迅速解决了我的问题,但有时候也会犯错导致我走弯路。比如,我希望只在添加文件时触发事件,ChatGPT 明确告诉我可以使用 –event Create 参数来实现。但在我想要使用 -i 参数将添加的文件类型限制于 .docx 等文档时,ChatGPT 就明显弄反了 -i(include,包含)和 -e(exclude,排除)两个参数。这个回答有不止一处错误考虑到授权陌生文件可能带来的风险,如何只监控特定文件恰恰是我比较关注的事项(当然,都监控然后加一个文件名判断也不是难事)。ChatGPT 在这个问题上没有给我什么帮助。这里需要特别记录一下的是,fswatch 命令中,只写 -i 并不意味着可以只监控符合规则的文件,必须首先写上 -e 并排除一切文件,再写一个 -i 包含需要的文件,才能够实现监控符合条件文件的目标。上图的另一个错误在于,fswatch 使用的匹配规则并非通配符,而是正则表达式(regex),当然这也可能是由于 ChatGPT 使用过时资料库导致的。在正则表达式中,匹配任意单字符的不是 * 而是 .,任意数量字符则是 .*。在学习和测试后,我将匹配部分写成如下形式:# 文件类型,可随意修改添加

types=(“doc” “docx” “ppt” “pptx” “xls” “xlsx”)

# 生成正则表达式

re=””

for i in ${types[*]};do re=”$re|$i$”;done

re=${re:1}

# 监控满足条件的文件变动

fswatch -e “.*” -EIi $re –event Created $path

其中,re 经过循环后生成的值为 doc$|docx$|ppt$|pptx$|xls$|xlsx$,表示匹配以 doc、docx 等结尾的文件名。顺便一提,Shell 在赋值的时候,= 左右侧不能加空格,这写起来也太难受了。fswatch 的参数中,-e “.*” 表示排除所有(.*)文件。-EIi $re 表示包含上述文件类型的文件,E(extended)和 I(insensitive)的含义分别为使用扩展的正则表达式(否则无法使用或逻辑 |),以及不区分大小写。$path 则是目标文件夹,我事先定义了文件路径。

(三)对监控的文件做出修改

监控文件发生变化后,在如何修改变动文件权限的问题上,我一开始又使用了笨方法。我询问 ChatGPT 如何找到最后添加到文件,它在两次提问中分别给了我使用 ls 命令和 find 命令的方法,但我测试后发现这似乎找到的是最后修改文件,而非最后添加文件,这与我的需求不太符合。于是我使用了 find $path -type f -perm 444 来查找整个文件夹内权限为「444」(只读)的文件并批量进行修改。但当我返回查看 ChatGPT 最初给我的示例时,我意识到我可能把事情想复杂了。这个例子帮了大忙虽然暂时不知道 |  的具体作用,但上面这行示例代码的意思显然是,fswatch 发现变动后,通过 while 循环和 echo 命令显示变动的文件,这看起来很符合我的要求。为此,我认真学习了管道 | 的用法……才怪咧,并没有那么多时间。显然,这里我只需要把 echo $file 修改为 chmod 644 $file 就可以了。在 read file 的过程中遇到了一个小问题,就是读取的文件名中如果有空格那么就会截断为两个变量。通过检索,将环境变量 IFS 修改为 $\n(换行)就能够让 read 变为逐行读取,解决这个问题。整个脚本的代码展示如下:# ChatGPT 说,这是一个称为 shebang 的特殊注释,用于指定脚本解释器的路径# 如果该注释不存在或指定的路径不正确,则操作系统将尝试使用默认的解释器来执行脚本#!/bin/bash# 自定义路径,这里以 download 文件夹为例# 实际上应该也可以直接修改为微信的缓存文件夹(可以通过右键点击微信内接收到的文件查看路径)

path=”/Users/用户名/Downloads”

# 生成正则表达式,用于匹配文件

types=(“doc” “docx” “ppt” “pptx” “xls” “xlsx”)

re=””

for i in ${types[*]};do re=”$re|$i$”;done

re=${re:1}

# 修改环境变量,让后面的 read 能够逐行读取

IFS=$\n

# 监控添加文件的事件,将变动文件传递至下面的 chmod 命令

fswatch -e “.*” -EIi $re –event Created $path |

while read file;do

# 额外增加一个文件存在和不可写的判断,排除打开 Word 文档时产生的临时文件,避免报错

if [ -f $file ] && ! [ -w $file ];then

chmod 644 $file

fi

done

最后,将文件保存为 wxpermfix.sh(名称可以自己选择,保存路径按照惯例,可以放在自己主目录下的 bin 目录,即 ~/bin );并在终端里用 chmod 命令赋予其执行权限:chmod +x ~/bin/wxpermfix.sh然后,就可以将它拖入终端中回车运行。

三、添加启动项文件:认识 launchd

问题和麻烦从此刻开始不断涌现。在写完以上脚本后,我打算让脚本自己跑起来,并在开机自动启动,ChatGPT 推荐使用 launchd 工具。注意修改 Label 和 ProgramArguments 下 <string> 和 </string> 包围的内容即可可在我模仿上面的文件修改完毕(实际上只需要复制文件夹内随便一个其他文件就可以)后,出现了各种问题,包括但不限于:• 以为需要反复重启/登出用户来测试效果——实际上只需要 launchctl unload 卸载 plist 文件后 launchctl load 重新加载文件即可。• 加载时告诉我没有足够的权限——修改脚本和 plist 文件的权限,比较奇怪的是,似乎 plist 文件权限修改为 744 是不可以的,必须得 644• 放在什么位置——在 ChatGPT 提到的两个目录中,一开始我放在了 /Library/LaunchDaemons,但那是系统级别的守护进程所在位置,放在那里会提示权限不足,实际上应该放在 ~/Library/LaunchAgent,以当前用户身份运行就行了• 通过 launchd 启动不生效,但在终端中启动就可以——原因是 launchd 启动的进程的环境变量 PATH 与交互式终端下不同,无法找到 fswatch,解决方式是找到 fswatch 命令的绝对路径,放在脚本里。我早就把在脚本、plist 文件中使用的目录都设置为绝对路径,所以一直没有理解 ChatGPT 发出的有关要使用绝对路径的提示,直到这里我才意识到是 fswatch 要使用绝对路径chmod 报错权限不足——一开始我听信了 ChatGPT 的说法,以为需要在脚本中加上 sudo,可那就意味着每次都需要输入密码,这显然是不符合我自动运行脚本需求的。好在我最后在一个论坛里发现了解决方法,通过赋予 bash 完全磁盘访问权限(full disk access),可以让通过 launchctl 运行的脚本中的 chmod 命令有权限修改其他文件的权限。具体步骤还是可以问到:至此,我终于有了一个可以自动启动的,不断监控目标文件夹的脚本。让我在微信自己解决这个问题以前,免受只读文件之苦。如后文所述,因为不太想设置自动启动,我自己最后没有用这套方案。但如果有人想要用这套方案,请参考下列步骤,并请特别注意各处权限设置,否则会无法生效1. 按照上面所示步骤赋予 bash 完全访问磁盘权限。2. 进入 ~/Library/LaunchAgents 文件夹,创建一个内容如后附的 wxpermfix.plist 文件(除扩展名外名称可以自定)。打开后参照上面 ChatGPT 给出的示例,删去不需要的部分,修改 Label 和 ProgramArguments 字段中 <string> 和 </string> 包围的内容:前者就是一个单纯用于区分的名称(理论上可以随便填写,但惯例是采用反向 DNS 方式命名);后者则修改为上面 wxpermfix.sh 脚本的路径(注意把 [USERNAME] 部分换成自己的用户名)。

Label

com.example.wxpermfix

ProgramArguments

/Users/[USERNAME]/bin/wxpermfix.sh

RunAtLoad

</plist>3. 打开终端(Terminal.app),输入 launchctl load -w ~/Library/LaunchAgents/wxpermfix.plist 后回车,其中 wxpermfix.plist 的路径也可以不用手动输入,从 Finder 将其图标拖入终端窗口即可。4. 如果这么做没有生效,那么参照上面 ChatGPT 给出的方案,将 wxpermfix.sh 脚本中的 fswatch,替换为运行 which fswatch 命令找到的绝对路径,并重新加载一遍 LaunchAgent:

launchctl unload ~/Library/LaunchAgents/wxpermfix.plist

launchctl load ~/Library/LaunchAgents/wxpermfix.plist

替代方案:不自动启动也不是不可以

在上面询问 ChatGPT 如何赋予 bash 完全磁盘访问权限时,ChatGPT 提到了「存在一定的安全风险」。也许有的读者会介意这里的「安全风险」,因此我也就不推荐所有人采用上面的 launchd 方案。我回想了一下平时的电脑使用习惯,我平时以待机为主,不经常关机,开机自动启动脚本也不是非要不可,只要启动后可以安静地自动运行就够了。所以,我又开始探索是否可以免去自动启动的要求。在这里,我又走了一点弯路,我回到了自动操作(Automator),试图通过打包一个应用程序(Application)来实现目标,但运行时显示在上方状态栏的齿轮符号让我打消了这个念头。于是,我问了 ChatGPT 最后一个问题,它的回答也一如既往的简洁和清楚。当然,我自己是记不住 nohup 命令后面那几个参数的,所以我又写了一个新的 .sh 文件存放这条启动脚本的命令。等开机后,就将这个文件通过终端运行一次,然后就可以退出终端,让修改权限脚本保持运行。与之对应的是,我也写了一条关掉脚本的指令备用,存在同一个文件夹的另一个 .sh 脚本中。这两个文件分别为,启动:#!/bin/bash# 启动 xxx.sh,替换为正确路径即可nohup xxx.sh >/dev/null 2>&1 &关闭:#!/bin/bash# 关闭 xxx.sh,替换为正确文件名即可pkill -f xxx.sh题外话在此之前,我从来没有写过 Shell 脚本,否则也不会在一开始干出在自动操作中 tell “Terminal.app” 的事情。虽然学习过程中也遇到了不少问题,但整体上还是比较愉悦和流畅的。一个原因可能在于,对于从零开始的学习者而言,ChatGPT 在每个问题上都提供了详尽有用的解释和非常具体的示例,非常直观和有效。当然,ChatGPT 好用也不意味着可以对其无条件信任,在解决问题时,查看文档和搜索引擎仍然是有必要的。并且,比起高度抽象地描述问题让它给出完整方案,一步步提问可能对学习和检验都更有帮助。一点额外的感想是,就像我在 ChatGPT 中提问自身专业知识时,很容易看出它从什么时候开始胡编乱造,相信计算机专业人士在使用 ChatGPT 中也能更高效地甄别信息的真伪,更直接地去利用那些更有价值的东西,扬弃错误和冗余的部分。我想,这某种程度上或许也说明了专业人士并不那么容易因 AI 发展而被替代,反而是比起一般人在利用 AI 解决相关问题时更有优势吧。原文链接:https://sspai.com/post/79351?utm_source=wechat&utm_medium=social

作者:GoodTheme

责编:PlatyHsu

题图来自 Unsplash:@imkirk/ 更多热门文章 /

Leave a Reply