读取环境变量(带默认值)
Go变量未设置或为空白时回退到默认值,避免 os.Getenv 直接返回空串造成的误判。
func getEnv(key, fallback string) string {
if v := strings.TrimSpace(os.Getenv(key)); v != "" {
return v
}
return fallback
}
日常写代码反复会用的片段,按语言分组,每条一键复制。代码注释统一用英文,可以直接嵌进任何项目。
变量未设置或为空白时回退到默认值,避免 os.Getenv 直接返回空串造成的误判。
func getEnv(key, fallback string) string {
if v := strings.TrimSpace(os.Getenv(key)); v != "" {
return v
}
return fallback
}
超时或取消都会以 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()
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...)
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
解析配置或 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
}
NewTicker 不配 Stop 会泄漏底层定时器;用 done channel 让循环可退出。
ticker := time.NewTicker(time.Second)
defer ticker.Stop()
for {
select {
case <-ticker.C:
// runs every second
case <-done:
return
}
}
atomic.Int64(Go 1.19+)比 mutex + int64 轻,也不会忘记加锁。
var hits atomic.Int64
hits.Add(1)
total := hits.Load()
在可能 panic 的边界函数用命名返回值 + defer recover,把崩溃变成可处理的 error。
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("panic: %v", r)
}
}()
dict.fromkeys 保序去重;set 会打乱顺序,手写循环更慢。
def dedupe(items):
return list(dict.fromkeys(items))
sorted 配 key lambda 按值排,reverse=True 取高频前 10。
top = sorted(counter.items(), key=lambda kv: kv[1], reverse=True)[:10]
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)
不带时区的 now() 是本地墙上时间,跨时区部署会错;用 zoneinfo 显式指定(3.9+,无需 pytz)。
from datetime import datetime
from zoneinfo import ZoneInfo
now = datetime.now(ZoneInfo("Asia/Shanghai"))
pathlib 的 rglob 递归匹配;配 stat().st_mtime 按修改时间排序。
from pathlib import Path
logs = sorted(Path("var/log").rglob("*.log"), key=lambda p: p.stat().st_mtime)
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"))
字段默认值必须不可变,列表/字典用 default_factory,直接写 [] 会 ValueError。
from dataclasses import dataclass, field
@dataclass
class Cart:
items: list = field(default_factory=list) # not items: list = []
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()
structuredClone 支持 Date、Map、Set 与循环引用,不需要再引三方库。
const copy = structuredClone(obj);
停止触发 ms 毫后才执行,适合搜索框;要第一次就响应需改 leading 版。
function debounce(fn, ms = 300) {
let t;
return (...args) => {
clearTimeout(t);
t = setTimeout(() => fn(...args), ms);
};
}
指数退避(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));
}
}
}
AbortSignal.timeout 一行搞定;Safari 16.4+/Node 17.3+ 才支持,旧环境用 AbortController + setTimeout。
const resp = await fetch(url, { signal: AbortSignal.timeout(5000) });
get 缺失返回 null,用 ?? 给默认值;直接 Number(null) 会得到 0 掩盖错误。
const params = new URLSearchParams(location.search);
const page = Number(params.get("page") ?? 1);
Array.from 一次生成全部块,比反复 splice 原数组安全。
const chunk = (arr, size) =>
Array.from({ length: Math.ceil(arr.length / size) }, (_, i) =>
arr.slice(i * size, i * size + size));
轮询等待比 setInterval 干净;需要响应外部取消信号时改用 AbortSignal 版。
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
await sleep(1000);
Set 一行去重,NaN 也能正确去重;对象按引用去重。
const uniq = [...new Set(arr)];
-e 遇错即停、-u 未定义变量即停、pipefail 让管道中途失败也报错;改 IFS 防空格拆词。
set -euo pipefail
IFS=$'\n\t'
-print0 + read -d '' 用 NUL 分隔,空格、换行都不会拆开文件名。
find . -name '*.log' -print0 | while IFS= read -r -d '' f; do
gzip "$f"
done
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))
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
-P 指定并行度(nproc 取核数),-I{} 占位符独立拼命令,适合 CPU 密集任务。
ls *.jpg | xargs -P "$(nproc)" -I{} convert {} -resize 800x {}.webp
mktemp -d 建唯一目录,trap 绑 EXIT 保证任何退出路径都清理,避免 /tmp 堆积。
tmp="$(mktemp -d)"
trap 'rm -rf "$tmp"' EXIT
lsof -i :端口 列出占用进程;精简容器没有 lsof 时用 ss 替代。
lsof -i :8080 # macOS / Linux
ss -tlnp | grep 8080 # Linux fallback when lsof is missing
nohup + & 脱离终端,>> 追加日志、2>&1 合并 stderr;务必存 PID,否则事后找不到进程。
nohup ./worker >> worker.log 2>&1 &
echo $! > worker.pid
OFFSET 越翻越慢;以上一页最后一行为游标做行值比较,直接走索引。
SELECT id, title FROM posts
WHERE (created_at, id) < (:cursor_at, :cursor_id)
ORDER BY created_at DESC, id DESC
LIMIT 20;
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;
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;
锚点(根节点)+ 递归成员(接上前一层);忘记终止条件会无限递归。
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;
窗口默认帧就是 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;
date_trunc 把时间截到月/周/日;GROUP BY 1 按位置引用第一列。
SELECT date_trunc('month', created_at) AS month, COUNT(*)
FROM orders
GROUP BY 1 ORDER BY 1;
SUM(...) OVER () 空帧即全表总和;整数相除会截断,先转 numeric。
SELECT category, amount,
amount::numeric / SUM(amount) OVER () AS share
FROM sales;
加唯一索引前先跑它确认没有存量重复,否则建索引直接失败。
SELECT email, COUNT(*) FROM users
GROUP BY email
HAVING COUNT(*) > 1;
reset --mixed 只影响本地;提交已推远端且别人可能拉过时必须用 revert。
git reset --mixed HEAD~1 # changes back to worktree, commit gone
# if pushed and possibly pulled, revert instead:
git revert HEAD
--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
-p 按 hunk 交互暂存,把一个文件里的两处改动拆成两个提交。
git add -p # interactive hunk-by-hunk staging
git add -p path.txt # only this file
二分把 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
--merged 只列已合入的;grep 排除当前分支与主分支自己,-d 保证未合并删不掉。
git branch --merged main | grep -v '^\*\|main' | xargs -r git branch -d
误提交密钥后必须改历史 + 强推 + 吊销密钥,三步缺一不可;用 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 也能完整阅读。
可以。它们都是各语言标准库或通用写法的直接应用,不依赖特定框架,也没有授权限制,复制后按项目风格调整命名即可。生产使用前请按自己的边界条件测试一遍——片段解决的是常见问题,不是替代你的测试。
这个页面有中英两个版本。注释写中文就得维护两份代码,任一份漏改都会让另一种语言的读者看到半翻译产物;统一用英文注释可以从源头避免这个问题,中英文项目都能直接用。
会。 Rust、Java、C#、Kotlin 等语言的片段在计划中,优先级按本站读者的实际使用情况排。你也可以通过页面右下角的反馈按钮提名想要的语言或具体片段。
页首标注了更新时间。语言大版本发布(如 Go、Python 的新稳定版)后会校订受影响的条目,平时发现更好的写法也会即时更新。