Ubuntu 双击运行 Shell 脚本:权限、文件管理器与安全启动器

在 Ubuntu 中,“双击脚本”至少跨越四层:脚本内容和解释器、Unix 执行权限、挂载选项,以及文件管理器或桌面的启动策略。当前 GNOME Files 仍可提供“运行、查看或每次询问”的可执行文本选项,但 Ubuntu 版本、桌面环境和文件管理器不同,菜单与默认行为也会不同。最可诊断的入口始终是终端;需要稳定 GUI 入口时,再为已经审查的本地脚本创建标准桌面启动器。

本文维护层于 2026 年重写。2014 年原文只有旧版 Nautilus 的首选项路径和两张截图,完整正文保存在文末惰性归档中,不作为当前界面保证。

1. 先决定是否应该运行

Shell 脚本是程序,不是“只读文档”。双击前先确认来源、用途、预期输入输出、网络访问、写入路径和回退方式。下载、聊天附件、邮件或共享盘中的脚本,不应因为图标看起来正常就执行。

先做只读检查;路径只是占位符:

file -- script.sh
stat -- script.sh
sed -n '1,80p' -- script.sh
sed -n '1l' -- script.sh
sha256sum -- script.sh

阅读完整文件,而不只是前 80 行。哈希只能标识字节;必须与可信发布方通过独立渠道公布的值比较,才有来源意义。不要使用 curl ... | sh,也不要把未知脚本交给 sudo 试运行。

2. 解释器、shebang 与换行符

直接执行脚本时,第一行 shebang 决定解释器。按脚本实际语法选择,例如 Bash 脚本可写:

#!/usr/bin/env bash
set -u
printf 'script=%s\n' "$0"
printf 'working_directory=%s\n' "$PWD"
printf 'argument_count=%s\n' "$#"

#!/bin/sh 只适用于确实符合目标系统 sh 的脚本。/usr/bin/env bash 从启动环境的 PATH 找 Bash,便于不同安装位置,但也意味着 PATH 必须可信;固定受管主机可按部署规范使用绝对解释器路径。

bad interpreter 后出现 ^M 通常说明文件带 CRLF。先用 filesed -n '1l' 证明,再在副本中用可信编辑器转换;不要在未检查完整内容前批量改写。调用 bash script.sh 会绕过 shebang,因此只能在确认它确实是 Bash 脚本时用于诊断,不能证明直接执行路径正确。

3. 只给所有者执行权限

把已审查脚本保存到本地、由自己控制且不被其他用户写入的位置,例如 $HOME/.local/bin/double-click-demo。然后只增加所有者执行位:

install -d -m 700 -- "$HOME/.local/bin"
chmod u+x -- "$HOME/.local/bin/double-click-demo"
ls -l -- "$HOME/.local/bin/double-click-demo"
"$HOME/.local/bin/double-click-demo"
printf 'exit_status=%s\n' "$?"

不要用 chmod 777,也不要仅为增加自己的执行位使用 sudo。脚本文件有 x 仍不够:父目录需要搜索权限,文件系统可能以 noexec 挂载,ACL 或安全策略也可能拒绝执行。FAT、NTFS、网络盘和压缩包中的权限语义可能不同;把脚本安装到本地原生文件系统比反复放宽权限更可控。

若只想在审查后测试一个尚未加执行位的 Bash 文件,可显式调用解释器:

bash --noprofile --norc -- "$HOME/.local/bin/double-click-demo"

这会跳过用户 Bash 启动文件,但不会隔离脚本的文件、网络或进程权限。它不是沙箱。

4. 终端执行是诊断基线

从终端运行能保留标准输出、错误输出和退出码。先切换到脚本预期的工作目录,或让脚本使用绝对路径;不要假定双击时的 PWD 与交互终端相同。

如果脚本会改文件,先提供 --dry-run 或只读模式,并把输出限定在新的测试目录。若没有预演、备份或明确回退,先不要运行。需要密码、管理员权限、磁盘分区、账户、网络规则或生产数据的脚本,不适合作为无提示双击入口。

5. GNOME Files 的直接双击选项

GNOME 的当前帮助仍描述可执行文本文件的三种策略:打开时运行、打开时查看、每次询问。文件还必须在“属性 → 权限”中允许作为程序执行。实际 Ubuntu 版本可能把选项放在 Files 菜单的“首选项 → 常规/行为”,也可能使用没有该选项的其他文件管理器。

若界面提供这些策略,优先选择“每次询问”,先查看再运行。直接运行的缺点是工作目录、环境变量和错误输出不明显,终端程序可能瞬间关闭。若选项不存在,不要修改隐藏设置或安装旧版文件管理器来复刻截图;使用终端或下一节的标准启动器。

6. 为已审查脚本创建应用启动器

需要可重复的 GUI 入口时,把脚本保留在不可被其他用户写入的固定路径,再用 Desktop Entry 规范创建 $HOME/.local/share/applications/lazying-shell-demo.desktop。下面三个 USERNAME 必须替换为真实本地用户名;Exec 不会展开 $HOME,也不会像 shell 一样解释管道、重定向或 &&

[Desktop Entry]
Type=Application
Name=Shell Script Demo
Comment=Run the reviewed local demo in a terminal
TryExec=/home/USERNAME/.local/bin/double-click-demo
Exec=/home/USERNAME/.local/bin/double-click-demo
Path=/home/USERNAME
Terminal=true
Icon=utilities-terminal

TryExec 让桌面在目标不存在或不可执行时隐藏/忽略入口;Path 明确工作目录;Terminal=true 保留终端界面。若路径含空格,必须按 Desktop Entry 规范引用,而不是复制 shell 的任意转义。更简单的做法是使用无空格的受控路径。

用文本编辑器保存后,逐行检查 ExecTryExecPath。若已安装 desktop-file-utils,可只读验证并从应用菜单测试:

desktop-file-validate -- "$HOME/.local/share/applications/lazying-shell-demo.desktop"
gtk-launch lazying-shell-demo

desktop-file-validate 没有输出通常表示语法通过,不代表脚本安全。GNOME Shell 可能需要重新索引或重新登录后才显示新应用。把启动器复制到桌面时,桌面扩展还可能要求在 UI 中“允许启动”;只对自己刚审查的文件授权,不要用元数据命令批量信任下载内容。

7. GUI 与终端环境为什么不同

应用启动器通常不读取交互 shell 的 aliases、函数或全部启动文件,PATH、locale、代理、显示会话和密钥代理也可能不同。脚本应:

  • 为外部工具使用已验证的绝对路径或设置最小可信 PATH
  • 显式处理工作目录,不把启动位置当作脚本所在目录;
  • 引用每个可能含空格或通配字符的参数;
  • 把预期错误写到终端或受控日志,并返回非零状态;
  • 不把密钥写进脚本、启动器、命令行参数或公开日志。

不要在 Exec 中拼接未信任文本,也不要为了获得 shell 语法而使用 sh -c 包裹用户输入。若必须传文件参数,应阅读 Desktop Entry 的字段代码规则,并在脚本中验证数量、类型、所有权和允许目录。

8. 故障证据矩阵

症状 先查什么 不应做什么
双击只打开编辑器 文件管理器策略、执行位、当前桌面版本 不要假定 2014 截图仍对应当前菜单
Permission denied 文件/父目录权限、ACL、noexec、文件系统类型 不要 chmod 777 或用 root 绕过
bad interpreter shebang 路径、CRLF、解释器是否安装 不要随意改成 Bash 掩盖语言差异
终端可用、GUI 失败 PWDPATH、locale、会话总线、显示环境 不要把交互配置全量复制进启动器
窗口瞬间关闭 Terminal=true、终端直接运行、退出码与 stderr 不要加入无限 sleep 掩盖错误
应用菜单无入口 Desktop Entry 语法、文件名/应用 ID、会话索引 不要批量信任所有 .desktop 文件
外置盘可读但不能执行 挂载选项、权限模型、组织策略 不要重新挂载为可执行而忽略边界

只读诊断可从以下命令开始;输出可能含用户名和路径,公开前脱敏:

namei -l -- "$HOME/.local/bin/double-click-demo"
findmnt -T -- "$HOME/.local/bin/double-click-demo"
head -n 1 -- "$HOME/.local/bin/double-click-demo"
journalctl --user -b --since "10 min ago" --no-pager

某些精简系统没有 nameifindmnt 或用户日志;命令不存在也是环境证据,不要安装随机包只为让清单全绿。

9. 可逆停用与回退

先停用启动器,再撤销脚本执行位,不需要删除文件:

ScriptPath="$HOME/.local/bin/double-click-demo"
LauncherPath="$HOME/.local/share/applications/lazying-shell-demo.desktop"
test ! -e "$LauncherPath.disabled" || exit 1
mv -- "$LauncherPath" "$LauncherPath.disabled"
chmod u-x -- "$ScriptPath"

重新登录或刷新应用菜单后确认入口消失。若脚本曾经修改数据、账户或系统配置,仅撤销执行位不会回滚那些影响;必须使用脚本事先定义并测试的应用级恢复方案。

10. 停止条件

  • 无法说明脚本来源、完整内容、解释器或预期副作用。
  • 脚本要求 sudo、密码、令牌、关闭安全控制或修改生产数据,却没有审批和回退。
  • 只能通过全局放宽权限、启用可执行外置盘或批量信任启动器才能运行。
  • 终端运行报错,GUI 却隐藏错误;先修复终端路径。
  • 文件由其他用户写入、来自同步/共享位置,或运行期间可能被替换。
  • 桌面、文件管理器或企业策略与本文不同,且没有当前官方文档支持该做法。

这些情况应交给脚本维护者或系统管理员。便捷入口不应扩大脚本本身的权限或信任边界。

11. 官方资料

资料核验于 2026-09-01。Ubuntu、GNOME Files、桌面扩展与组织策略会变化;执行前以已安装版本的帮助和当前官方文档为准。

12. 2014 年原文档案(仅供溯源)

以下围栏逐字保留 source_export 的完整可见正文;没有尾随空白规范化、链接删除或私密值遮盖。旧版 Nautilus 菜单和截图仅作为惰性历史证据,不是当前界面保证。

~~~~markdown

打开一个文件夹,单击左上角的Home Folder(主目录)->单击Preferences(首选项)

%%LAZYBLOG_INLINE_0%%

按照下图所示设置:

%%LAZYBLOG_INLINE_0%%
~~~~

Leave a Reply