日常写代码反复会用的片段,按语言分组,每条一键复制。代码注释统一用英文,可以直接嵌进任何项目。

更新于 2026-10-15·共 46 条·点语言标签筛选,搜索框按标题、说明与代码全文过滤

Go标准库能搞定的事就不引第三方:超时、并发、解码与文件读取的高频写法。

读取环境变量(带默认值)

Go

变量未设置或为空白时回退到默认值,避免 os.Getenv 直接返回空串造成的误判。

func getEnv(key, fallback string) string {
	if v := strings.TrimSpace(os.Getenv(key)); v != "" {
		return v
	}
	return fallback
}

HTTP 请求加超时(context)

Go

超时或取消都会以 error 从 Do 返回且 resp 为 nil——拿到 err 先返回,不要解引用 resp。

ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()

req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
if err != nil {
	return err
}
resp, err := http.DefaultClient.Do(req)
if err != nil {
	return err // timeout/cancel: context.DeadlineExceeded / Canceled
}
defer resp.Body.Close()

等待并发任务并收集全部错误

Go

WaitGroup 等全部协程结束,互斥锁收集每个任务的错误,最后 errors.Join 合成一条(Go 1.20+)。

var (
	wg   sync.WaitGroup
	mu   sync.Mutex
	errs []error
)
for _, u := range urls {
	wg.Add(1)
	go func(u string) {
		defer wg.Done()
		if _, err := fetch(u); err != nil {
			mu.Lock()
			errs = append(errs, err)
			mu.Unlock()
		}
	}(u)
}
wg.Wait()
return errors.Join(errs...)

按行读文件(兼容超长行)

Go

Scanner 默认单行上限 64 KB,超长行会静默停止而非报错——用 Buffer 调大并检查 sc.Err()。

f, err := os.Open(path)
if err != nil {
	return err
}
defer f.Close()

sc := bufio.NewScanner(f)
sc.Buffer(make([]byte, 0, 64*1024), 1024*1024) // raise the per-line cap to 1 MB
for sc.Scan() {
	line := sc.Text()
	_ = line
}
return sc.Err() // long lines surface here: bufio.Scanner: token too long

严格 JSON 解码(拒绝未知字段)

Go

解析配置或 API 响应时打开它,字段名拼错立刻暴露,不会静默丢字段。

dec := json.NewDecoder(resp.Body)
dec.DisallowUnknownFields() // a misspelled field now fails loudly
var cfg Config
if err := dec.Decode(&cfg); err != nil {
	return err
}

正确停止 Ticker

Go

NewTicker 不配 Stop 会泄漏底层定时器;用 done channel 让循环可退出。

ticker := time.NewTicker(time.Second)
defer ticker.Stop()
for {
	select {
	case <-ticker.C:
		// runs every second
	case <-done:
		return
	}
}

原子计数器(无锁累加)

Go

atomic.Int64(Go 1.19+)比 mutex + int64 轻,也不会忘记加锁。

var hits atomic.Int64
hits.Add(1)
total := hits.Load()

defer 捕获 panic 转为错误

Go

在可能 panic 的边界函数用命名返回值 + defer recover,把崩溃变成可处理的 error。

defer func() {
	if r := recover(); r != nil {
		err = fmt.Errorf("panic: %v", r)
	}
}()

Python数据处理与网络请求里最常写错的几处:保序去重、超时元组、时区与可变默认值。

列表去重且保持顺序

Python

dict.fromkeys 保序去重;set 会打乱顺序,手写循环更慢。

def dedupe(items):
	return list(dict.fromkeys(items))

字典按值排序取前 N

Python

sorted 配 key lambda 按值排,reverse=True 取高频前 10。

top = sorted(counter.items(), key=lambda kv: kv[1], reverse=True)[:10]

requests 自动重试(指数退避)

Python

Session 挂 HTTPAdapter 配置重试;timeout 必须是元组,只写一个数字仅约束读取。

from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry

session = requests.Session()
session.mount("https://", HTTPAdapter(max_retries=Retry(
	total=3, backoff_factor=0.5,
	status_forcelist=[429, 500, 502, 503, 504],
)))
resp = session.get(url, timeout=(3, 10))  # (connect timeout, read timeout)

时区安全的当前时间

Python

不带时区的 now() 是本地墙上时间,跨时区部署会错;用 zoneinfo 显式指定(3.9+,无需 pytz)。

from datetime import datetime
from zoneinfo import ZoneInfo

now = datetime.now(ZoneInfo("Asia/Shanghai"))

递归查找文件并按时间排序

Python

pathlib 的 rglob 递归匹配;配 stat().st_mtime 按修改时间排序。

from pathlib import Path

logs = sorted(Path("var/log").rglob("*.log"), key=lambda p: p.stat().st_mtime)

批量重命名文件

Python

enumerate 从 1 编号,:03d 补零保证字典序;先 sorted 保证结果可复现。

from pathlib import Path

for i, p in enumerate(sorted(Path("photos").glob("*.jpg")), 1):
	p.rename(p.with_name(f"photo-{i:03d}.jpg"))

dataclass 的可变默认值

Python

字段默认值必须不可变,列表/字典用 default_factory,直接写 [] 会 ValueError。

from dataclasses import dataclass, field

@dataclass
class Cart:
	items: list = field(default_factory=list)  # not items: list = []

计时上下文管理器

Python

perf_counter 是单调时钟,不受系统时间调整影响;finally 保证异常时也输出耗时。

from contextlib import contextmanager
from time import perf_counter

@contextmanager
def timer(label):
	start = perf_counter()
	try:
		yield
	finally:
		print(f"{label}: {perf_counter() - start:.3f}s")

with timer("query"):
	run_query()

JavaScript现代浏览器原生能力优先:structuredClone、AbortSignal、URLSearchParams。

深拷贝(原生)

JavaScript

structuredClone 支持 Date、Map、Set 与循环引用,不需要再引三方库。

const copy = structuredClone(obj);

防抖

JavaScript

停止触发 ms 毫后才执行,适合搜索框;要第一次就响应需改 leading 版。

function debounce(fn, ms = 300) {
	let t;
	return (...args) => {
		clearTimeout(t);
		t = setTimeout(() => fn(...args), ms);
	};
}

Promise 失败自动重试

JavaScript

指数退避(delay × 2^i),最后一次失败才向上抛。

async function retry(fn, times = 3, delay = 500) {
	for (let i = 0; i < times; i++) {
		try {
			return await fn();
		} catch (e) {
			if (i === times - 1) throw e;
			await new Promise((r) => setTimeout(r, delay * 2 ** i));
		}
	}
}

fetch 加超时

JavaScript

AbortSignal.timeout 一行搞定;Safari 16.4+/Node 17.3+ 才支持,旧环境用 AbortController + setTimeout。

const resp = await fetch(url, { signal: AbortSignal.timeout(5000) });

解析 URL 查询参数

JavaScript

get 缺失返回 null,用 ?? 给默认值;直接 Number(null) 会得到 0 掩盖错误。

const params = new URLSearchParams(location.search);
const page = Number(params.get("page") ?? 1);

数组分块

JavaScript

Array.from 一次生成全部块,比反复 splice 原数组安全。

const chunk = (arr, size) =>
	Array.from({ length: Math.ceil(arr.length / size) }, (_, i) =>
		arr.slice(i * size, i * size + size));

可中断的 sleep

JavaScript

轮询等待比 setInterval 干净;需要响应外部取消信号时改用 AbortSignal 版。

const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
await sleep(1000);

数组去重

JavaScript

Set 一行去重,NaN 也能正确去重;对象按引用去重。

const uniq = [...new Set(arr)];

Shell脚本健壮性与跨平台差异:空格文件名、sed -i、退出码与临时目录清理。

严格模式

Shell

-e 遇错即停、-u 未定义变量即停、pipefail 让管道中途失败也报错;改 IFS 防空格拆词。

set -euo pipefail
IFS=$'\n\t'

安全遍历含空格的文件名

Shell

-print0 + read -d '' 用 NUL 分隔,空格、换行都不会拆开文件名。

find . -name '*.log' -print0 | while IFS= read -r -d '' f; do
	gzip "$f"
done

解析命令行参数

Shell

getopts 是 POSIX 内置,处理 -f value / -v 最稳;最后 shift 吃掉已解析部分。

while getopts "f:v" opt; do
	case "$opt" in
	f) file="$OPTARG" ;;
	v) verbose=1 ;;
	*) echo "usage: $0 [-f file] [-v]" >&2; exit 1 ;;
	esac
done
shift $((OPTIND - 1))

跨平台原地替换(sed -i)

Shell

macOS 的 sed -i 要求一个后缀参数,照抄 Linux 写法会报 undefined variable。

# GNU sed (Linux)
sed -i 's/foo/bar/g' file.txt
# BSD sed (macOS): empty suffix = no backup
sed -i '' 's/foo/bar/g' file.txt

并行批处理

Shell

-P 指定并行度(nproc 取核数),-I{} 占位符独立拼命令,适合 CPU 密集任务。

ls *.jpg | xargs -P "$(nproc)" -I{} convert {} -resize 800x {}.webp

临时目录自动清理

Shell

mktemp -d 建唯一目录,trap 绑 EXIT 保证任何退出路径都清理,避免 /tmp 堆积。

tmp="$(mktemp -d)"
trap 'rm -rf "$tmp"' EXIT

查看端口占用

Shell

lsof -i :端口 列出占用进程;精简容器没有 lsof 时用 ss 替代。

lsof -i :8080         # macOS / Linux
ss -tlnp | grep 8080 # Linux fallback when lsof is missing

后台常驻并写日志

Shell

nohup + & 脱离终端,>> 追加日志、2>&1 合并 stderr;务必存 PID,否则事后找不到进程。

nohup ./worker >> worker.log 2>&1 &
echo $! > worker.pid

SQLPostgreSQL 方言:深分页、窗口函数、upsert 与递归查询。

Keyset 分页(替代 OFFSET)

SQL

OFFSET 越翻越慢;以上一页最后一行为游标做行值比较,直接走索引。

SELECT id, title FROM posts
WHERE (created_at, id) < (:cursor_at, :cursor_id)
ORDER BY created_at DESC, id DESC
LIMIT 20;

每组取最新一行

SQL

ROW_NUMBER 窗口函数按分组排序取 rn=1,比 GROUP BY 后自连接更直观更快。

SELECT * FROM (
	SELECT *, ROW_NUMBER() OVER (
		PARTITION BY user_id ORDER BY created_at DESC
	) AS rn
	FROM orders
) t WHERE rn = 1;

存在则更新(upsert)

SQL

ON CONFLICT 是 PostgreSQL/SQLite 写法;MySQL 用 ON DUPLICATE KEY UPDATE。

INSERT INTO stats (day, views) VALUES (DATE '2026-10-15', 1)
ON CONFLICT (day) DO UPDATE SET views = stats.views + 1;

递归查询树形结构

SQL

锚点(根节点)+ 递归成员(接上前一层);忘记终止条件会无限递归。

WITH RECURSIVE tree AS (
	SELECT id, parent_id, name, 0 AS depth
	FROM categories WHERE parent_id IS NULL
	UNION ALL
	SELECT c.id, c.parent_id, c.name, t.depth + 1
	FROM categories c JOIN tree t ON c.parent_id = t.id
)
SELECT * FROM tree ORDER BY depth, name;

累计求和

SQL

窗口默认帧就是 UNBOUNDED PRECEDING→CURRENT ROW,显式写出避免读代码的人猜。

SELECT day, views,
	SUM(views) OVER (
		ORDER BY day
		ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
	) AS total
FROM daily_views;

按周/月聚合

SQL

date_trunc 把时间截到月/周/日;GROUP BY 1 按位置引用第一列。

SELECT date_trunc('month', created_at) AS month, COUNT(*)
FROM orders
GROUP BY 1 ORDER BY 1;

计算每行占比

SQL

SUM(...) OVER () 空帧即全表总和;整数相除会截断,先转 numeric。

SELECT category, amount,
	amount::numeric / SUM(amount) OVER () AS share
FROM sales;

查找重复记录

SQL

加唯一索引前先跑它确认没有存量重复,否则建索引直接失败。

SELECT email, COUNT(*) FROM users
GROUP BY email
HAVING COUNT(*) > 1;

Git日常救援与历史整理:撤销、拆分、排查与清理。

撤销提交但保留改动

Git

reset --mixed 只影响本地;提交已推远端且别人可能拉过时必须用 revert。

git reset --mixed HEAD~1   # changes back to worktree, commit gone
# if pushed and possibly pulled, revert instead:
git revert HEAD

修改最近一次提交

Git

--amend 重写最近提交;已推送的提交 amend 后需强推,先确认没人基于它工作。

git commit --amend -m "a more accurate commit message"
# include the file you forgot
git add forgotten.txt && git commit --amend --no-edit

只提交文件的一部分

Git

-p 按 hunk 交互暂存,把一个文件里的两处改动拆成两个提交。

git add -p            # interactive hunk-by-hunk staging
git add -p path.txt   # only this file

二分查找引入 bug 的提交

Git

二分把 N 次排查降到 log₂N;脚本能自动判断时用 git bisect run。

git bisect start
git bisect bad                 # HEAD is bad
git bisect good v1.2.0         # this tag is good
# git checks out candidates; just mark good/bad, then:
git bisect reset

清理已合并分支

Git

--merged 只列已合入的;grep 排除当前分支与主分支自己,-d 保证未合并删不掉。

git branch --merged main | grep -v '^\*\|main' | xargs -r git branch -d

从历史中删除文件

Git

误提交密钥后必须改历史 + 强推 + 吊销密钥,三步缺一不可;用 filter-repo 而非已废弃的 filter-branch。

git filter-repo --path secrets.env --invert-paths
# then force push; rotate the key - rewriting != un-leaking

常用代码片段

日常写代码反复会用的片段,按语言分成六组:Go、Python、JavaScript、Shell、SQL 与 Git,共 46 条。每条都是解决一个具体问题的最小完整写法,而不是语法抄录。

复制按钮在每段代码右上角;顶部语言标签用于筛选,搜索框按标题、说明与代码全文过滤。所有内容在构建时写进页面,禁用 JavaScript 也能完整阅读。

主要功能

使用步骤

  1. 点顶部的语言标签(Go / Python / JavaScript / Shell / SQL / Git)筛选某一组。
  2. 或在搜索框输入关键词,如「超时」「去重」「分页」「rebase」,即时过滤。
  3. 点代码右上角的「复制」按钮,粘贴进项目后按需调整。
  4. 格式化粘贴进来的代码可以用本站的代码格式化工具;Go 命令速查见 Go 速查表。

常见问题

这些片段可以直接用在生产项目里吗?

可以。它们都是各语言标准库或通用写法的直接应用,不依赖特定框架,也没有授权限制,复制后按项目风格调整命名即可。生产使用前请按自己的边界条件测试一遍——片段解决的是常见问题,不是替代你的测试。

代码注释为什么是英文的?

这个页面有中英两个版本。注释写中文就得维护两份代码,任一份漏改都会让另一种语言的读者看到半翻译产物;统一用英文注释可以从源头避免这个问题,中英文项目都能直接用。

会支持更多语言吗?

会。 Rust、Java、C#、Kotlin 等语言的片段在计划中,优先级按本站读者的实际使用情况排。你也可以通过页面右下角的反馈按钮提名想要的语言或具体片段。

片段多久更新一次?

页首标注了更新时间。语言大版本发布(如 Go、Python 的新稳定版)后会校订受影响的条目,平时发现更好的写法也会即时更新。

相关工具与阅读

问题反馈

本站是纯前端静态站,没有后端也没有账号系统,反馈走 GitHub Issues、讨论区或邮件。

粘贴到 issue 或邮件里能帮我更快定位问题,其中不含你输入的任何内容

其它:查看已有反馈 · 邮件反馈(无需 GitHub 账号):278975598@qq.com