返回资讯中心
外部精选
软件工程
#开发者实时流

开发者发布用于对比 iTerm 配置的脚本

来源标题:Show HN: My Script to Diff iTerm Configs

开发者 hagemt121 在 Show HN 上分享了一个用于对比 iTerm 配置的脚本,并以 MIT 协议开源。作者称自己目前仍在使用 iTerm 且体验良好,但也注意到不少用户转向 Hyper 或 Ghostty 等终端。该脚本以 Gist 形式发布,可下载或克隆使用。

Hacker News 最新hagemt2026/11/13 · 今天30 分钟阅读

以下正文由 AI 翻译自 Hacker News 最新(原文版权归原站所有),可能存在偏差,重要内容请以原文为准。

即时分享代码、笔记和片段。 显示 Gist 选项 下载 ZIP 收藏 0 (0) 你需要登录才能收藏 gist Fork 0 (0) 你需要登录才能 fork gist 嵌入 选择一个选项 嵌入 将此 gist 嵌入到你的网站。 分享 复制此 gist 的可分享链接。 通过 HTTPS 克隆 使用 Web URL 克隆。 未找到结果 了解更多关于克隆 URL 的信息 在 <script src="https://gist.github.com/hagemt121/14caecfd23dc14b97e0d2eaec01ab3a4.js"></script> 处克隆此仓库 将 hagemt121/14caecfd23dc14b97e0d2eaec01ab3a4 保存到你的电脑,并使用 GitHub Desktop 打开。 代码 修订 1 嵌入 选择一个选项 嵌入 将此 gist 嵌入到你的网站。 分享 复制此 gist 的可分享链接。 通过 HTTPS 克隆 使用 Web URL 克隆。 未找到结果 了解更多关于克隆 URL 的信息 在 <script src="https://gist.github.com/hagemt121/14caecfd23dc14b97e0d2eaec01ab3a4.js"></script> 处克隆此仓库 将 hagemt121/14caecfd23dc14b97e0d2eaec01ab3a4 保存到你的电脑,并使用 GitHub Desktop 打开。 下载 ZIP 从一个人的 iTerm 配置里能看出很多关于他的信息。 原始文件 itermdiff.py 此文件包含隐藏或双向 Unicode 文本,可能被解释或编译为与下方显示不同的内容。若要查看,请在一个能显示隐藏字符的编辑器中打开该文件。 了解更多关于双向 Unicode 字符的信息 显示隐藏字符 #!/usr/bin/env python3 """itermdiff:在每台 Mac 上对 iTerm2 设置做快照,然后与基线进行对比。 在每台 Mac 上:python3 itermdiff.py snapshot # -> iterm-<host>.json 任意位置:python3 itermdiff.py diff BASE A B [--md] # BASE 在前;目标数量不限 diff 接受快照或原始 plist(例如来自备份或 dotfiles 仓库的 com.googlecode.iterm2.plist)。Profile 按名称匹配,GUID 引用会解析 为 profile 名称,颜色以十六进制显示,并且默认忽略机器本地噪声。 只读:绝不会写入 iTerm 的偏好设置。仅使用 Python 3 标准库。 from future import annotations import argparse import datetime as dt import hashlib import json import platform import plistlib import re import subprocess import sys from collections import Counter from itertools import groupby from pathlib import Path DOMAIN = "com.googlecode.iterm2" PREFS_FILE = Path.home() / "Library/Preferences" / f"{DOMAIN}.plist" DYNAMIC_DIR = Path.home() / "Library/Application Support/iTerm2/DynamicProfiles" DEFAULT_IGNORE = [ r"^NoSync", # iTerm 的机器本地键(按设计从不同步) r"^NS(Window Frame|SplitView|TableView|Toolbar|NavPanel|StatusItem)", # 窗口/UI 几何信息 r"^SU(LastCheckTime|HasLaunchedBefore|UpdateRelaunchingMarker|SkippedVersion|LastProfileSubmissionDate)$", r"^kCPK", # 取色器 UI 状态 r"^iTerm Version$", # 改为在头部显示 r"^Window Arrangements", # 已保存的窗口布局 RGB = ("Red Component", "Green Component", "Blue Component") class _Mark: def init(self, s: str): self.s = s UNSET = _Mark("—") # 键不存在 -> 应用 iTerm 的内置默认值 ABSENT = _Mark("·") # 整个 profile 不存在(在其分区行上报告一次)

---------------------------------------------------------------- snapshot

def jsonable(v): if isinstance(v, dict): return {str(k): jsonable(x) for k, x in v.items()} if isinstance(v, (list, tuple)): return [jsonable(x) for x in v] if isinstance(v, bytes): return {"data": hashlib.sha1(v).hexdigest()[:12], "len": len(v)} if isinstance(v, dt.datetime): return {"date": v.isoformat()} if isinstance(v, plistlib.UID): return {"uid": v.data} return v def cmd_snapshot(a) -> int: try: raw = subprocess.run(["defaults", "export", DOMAIN, "-"], capture_output=True, check=True).stdout source = f"defaults export {DOMAIN}" except (OSError, subprocess.CalledProcessError): if not PREFS_FILE.exists(): sys.exit(f"No iTerm2 preferences found at {PREFS_FILE}. Launch iTerm2 once first.") raw, source = PREFS_FILE.read_bytes(), str(PREFS_FILE) prefs = plistlib.loads(raw) dynamic, skipped = {}, [] if DYNAMIC_DIR.is_dir(): for f in sorted(DYNAMIC_DIR.iterdir()): if not f.is_file() or f.name.startswith("."): continue data = f.read_bytes() try: dynamic[f.name] = json.loads(data) except ValueError: try: dynamic[f.name] = jsonable(plistlib.loads(data)) except Exception: skipped.append(f.name) host = platform.node().split(".")[0] or "mac" meta = {"label": a.label or host, "host": host, "source": source, "created": dt.datetime.now().isoformat(timespec="seconds"), "iterm_version": prefs.get("iTerm Version")} if prefs.get("LoadPrefsFromCustomFolder"): meta["custom_prefs_folder"] = prefs.get("PrefsCustomFolder") out = Path(a.out or f"iterm-{meta['label']}.json") if out.exists() and not a.force: sys.exit(f"{out} already exists. Use -o NAME, or --force to overwrite it.") out.write_text(json.dumps({"meta": meta, "prefs": jsonable(prefs), "dynamic": dynamic}, indent=1, sort_keys=True)) print(f"Wrote {out}: {len(prefs)} keys, {len(prefs.get('New Bookmarks', []))} profiles, " f"{len(dynamic)} dynamic-profile file(s), iTerm {meta['iterm_version']}") if skipped: print("Skipped unreadable dynamic-profile files: " + ", ".join(skipped)) if "custom_prefs_folder" in meta: print(f"Note: iTerm loads prefs from {meta['custom_prefs_folder']}; snapshot = what's loaded now.") return 0

---------------------------------------------------------------- normalize

def load(path: str) -> dict: p = Path(path) data = p.read_bytes() if data.lstrip()[:1] == b"{": doc = json.loads(data) if isinstance(doc, dict) and "prefs" in doc and "meta" in doc: doc.setdefault("dynamic", {}) doc["meta"].setdefault("label", p.stem) return doc prefs = doc else: prefs = jsonable(plistlib.loads(data)) return {"meta": {"label": p.stem, "iterm_version": prefs.get("iTerm Version")}, "prefs": prefs, "dynamic": {}} def color(d: dict) -> str: def c(x): return max(0, min(255, round(float(x) * 255)))

(原文较长,此处译出前 6000 字,完整内容请点「打开原文」。)

正文由 探How 从来源站点 RSS 同步,内容未经改写;遇到排版缺失或需要图片、视频时请以原文为准。

打开原文
去社区讨论 →