My favourite zsh/bash shortcuts (functions and aliases)

My favourite zsh/bash shortcuts (functions and aliases)

我最喜欢的 zsh/bash 快捷方式(函数与别名)

Introduction

引言

My zsh profile is over 1000 lines at this point. A lot of that is functions I asked AI to generate for me, since it’s fast, portable, and saves me a ton of typing. Here’s the thing though: the shortcuts that save me the most time aren’t the clever ones. They’re the dumb ones. Things like clone instead of git clone && cd, or dir instead of mkdir -p && cd. Each one only saves a second or two, but I run them so often that it adds up fast. These are in no particular order, just the ones I reach for constantly.

目前我的 zsh 配置文件已经超过了 1000 行。其中很大一部分是我让 AI 为我生成的函数,因为这种方式既快速又便携,还帮我省去了大量的输入工作。但关键在于:真正为我节省最多时间的快捷方式并非那些“聪明”的技巧,而是那些“笨”方法。比如用 clone 代替 git clone && cd,或者用 dir 代替 mkdir -p && cd。每一个快捷方式可能只节省一两秒钟,但我使用它们的频率极高,累积起来就非常可观了。以下内容不分先后,仅列出我经常使用的那些。


Git aliases for common commands

常用 Git 命令的别名

A few one-liners I have set up as plain aliases: 我设置了一些简单的单行别名:

alias gcp="git cherry-pick"
alias git-append="git commit --amend --no-edit -a"

gcp is self-explanatory. git-append amends the last commit with your currently staged (and unstaged, thanks to -a) changes without touching the commit message. Great for fixing up a commit you just made before you push.

gcp 的含义显而易见。git-append 可以将当前暂存区(以及通过 -a 参数包含的未暂存区)的更改合并到上一次提交中,且无需修改提交信息。这对于在推送前修复刚刚完成的提交非常有用。


Create a branch or switch to it if it already exists

创建分支,若已存在则直接切换

One of my most-used functions. Normally you have to remember whether a branch exists before deciding between git checkout <branch> and git checkout -b <branch>. This just does the right thing either way:

这是我最常用的函数之一。通常,在执行 git checkout <branch>git checkout -b <branch> 之前,你必须先记住分支是否存在。而这个函数无论哪种情况都能自动处理:

gb() {
  if git rev-parse --verify --quiet "$1" >/dev/null; then
    git checkout "$1"
  else
    git checkout -b "$1"
  fi
}

Nuke all local changes to reset the working tree

清除所有本地更改以重置工作区

When an experiment goes sideways or I just want to throw everything away and start clean, I run nah:

当实验搞砸了,或者我只想丢弃一切从头开始时,我会运行 nah

nah() {
  git reset --hard
  git clean -df
  if [ -d ".git/rebase-apply" ] || [ -d ".git/rebase-merge" ]; then
    git rebase --abort
  fi
}

This resets tracked changes, removes untracked files and directories. No confirmation prompt, so use it carefully.

这会重置已跟踪的更改,并删除未跟踪的文件和目录。由于没有确认提示,请谨慎使用。


将最近的提交打印为可直接粘贴的 cherry-pick 命令

Useful when you need to cherry-pick a batch of commits from one branch onto another in order:

当你需要按顺序将一批提交从一个分支 cherry-pick 到另一个分支时,这非常有用:

logs() {
  if [[ -z "$1" || "$1" =~ [^0-9] ]]; then
    echo "Usage: logs <number_of_commits>"
    return 1
  fi
  git log -n "$1" --reverse --pretty=format:"gcp %h"
}

Run logs 5 and you get the last 5 commits printed oldest to newest, each one already formatted as gcp <hash> (using the alias from above). Copy, paste, done.

运行 logs 5,你就会得到按从旧到新排列的最后 5 次提交,每一行都已格式化为 gcp <hash>(使用了上面的别名)。复制、粘贴,搞定。


Create a directory and cd into it in one step

一步创建目录并进入

This is the one I mentioned that barely saves any time per use, but I run it constantly and the time save compounds:

这就是我之前提到的那种单次使用几乎不省时间,但因为我频繁使用,节省的时间会累积起来的命令:

dir() { mkdir -p "$1" && cd "$1"; }

Make mkdir always create parent directories

让 mkdir 始终创建父目录

I got tired of hitting “No such file or directory” errors from mkdir when the parent folder didn’t exist yet, so I just overrode the default behavior globally:

我厌倦了在父文件夹不存在时 mkdir 报错“No such file or directory”,所以我直接全局覆盖了默认行为:

mkdir() { command mkdir -p "$@" }

Now mkdir always behaves like mkdir -p.

现在 mkdir 的行为始终等同于 mkdir -p


Open nano and auto-create missing parent directories

打开 nano 并自动创建缺失的父目录

Same idea as the mkdir override, but for opening a file in nano when the folder it lives in doesn’t exist yet:

思路与 mkdir 的覆盖相同,但适用于当文件所在的文件夹尚未创建时,直接用 nano 打开文件:

nano() {
  if [ $# -eq 0 ]; then
    command nano
  else
    dir=$(dirname "$1")
    if [ ! -d "$dir" ]; then
      mkdir -p "$dir"
    fi
    command nano "$@"
  fi
}

Safely replace a file’s contents by moving the original to trash first

安全替换文件内容:先将原文件移至回收站

Instead of overwriting a file and losing the original, replace moves it to ~/.Trash first and then opens a fresh file with the same name in nano:

为了避免覆盖文件导致丢失原件,replace 会先将其移至 ~/.Trash,然后在 nano 中打开一个同名的新文件:

replace() {
  if [[ $# -ne 1 ]]; then
    echo "Usage: replace <file>" >&2
    return 1
  fi
  local file="$1"
  if [[ ! -f "$file" ]]; then
    echo "Error: '$file' not found or not a file" >&2
    return 1
  fi
  mkdir -p ~/.Trash
  mv "$file" ~/.Trash/
  nano "$file"
}

But mainly this saves me from running rm && nano which I often do when replacing a file from an AI chat or similar. If you need the original back, it’s sitting in ~/.Trash.

这主要是为了省去我经常在处理 AI 对话等内容时,为了替换文件而执行 rm && nano 的麻烦。如果你需要找回原文件,它就在 ~/.Trash 里。


Convert HEIC images to JPG or PNG

将 HEIC 图像转换为 JPG 或 PNG

iPhone photos default to HEIC, which isn’t great for sharing or uploading. These two functions batch-convert them using ImageMagick:

iPhone 照片默认格式为 HEIC,这不利于分享或上传。这两个函数使用 ImageMagick 进行批量转换:

heic2jpg() {
  if [ $# -eq 0 ]; then echo "Usage: heic2jpg file1.heic [file2.heic ...]"; return 1; fi
  for f in "$@"; do
    if [ ! -f "$f" ]; then echo "Not found: $f"; continue; fi
    magick "$f" "${f%.*}.jpg"
  done
}

heic2png() {
  if [ $# -eq 0 ]; then echo "Usage: heic2png file1.heic [file2.heic ...]"; return 1; fi
  for f in "$@"; do
    if [ ! -f "$f" ]; then echo "Not found: $f"; continue; fi
    magick "$f" "${f%.*}.png"
  done
}

Both take any number of files, so heic2jpg *.heic works fine.

两者都支持任意数量的文件,所以 heic2jpg *.heic 可以正常工作。


Batch convert images to WebP

批量将图像转换为 WebP

For getting images web-ready, this wraps img2webp with sensible defaults and skips anything that isn’t actually a file:

为了让图像适配网页,这个函数封装了 img2webp 并设置了合理的默认值,同时会跳过非文件项:

img2webp() {
  if [[ $# -eq 0 ]]; then
    echo "Usage: img2webp <file.png> [file2.png ...] or img2webp *.png"
    return 1
  fi
  for input in "$@"; do
    if [[ ! -f "$input" ]]; then
      echo "Skipping: '$input' is not a file"
      continue
    fi
    local output="${input%.*}.webp"
    echo "Converting: $input$output"
    command img2webp -lossy -q 80 "$input" -o "$output"
  done
}

Runs at quality 80, lossy, which is a good default for most web use cases. Saves a TON of storage space and bandwidth.

以 80 的质量进行有损压缩,这对大多数网页使用场景来说是一个很好的默认值。能节省大量的存储空间和带宽。


A few misc aliases

一些杂项别名

Small ones I use daily without thinking about them: 我每天都在用,甚至不需要思考的小别名:

alias pest="./vendor/bin/pest"
alias python="python3" # For AI commands expecting it
alias py="python3"     # For me when I'm lazy

The pest alias saves typing out the full vendor binary path every time I want to run tests. The python/py aliases exist because plenty of AI-generated commands assume python points to Python 3, and half the time I’m too lazy to type the 3 myself anyway.

pest 别名省去了我每次运行测试时都要输入完整 vendor 二进制路径的麻烦。python/py 别名是因为很多 AI 生成的命令都假设 python 指向 Python 3,而且我有一半时间懒得自己输入那个“3”。


Conclusion

结语

None of these are groundbreaking on their own. But that’s kind of the point: the small, boring shortcuts you run 50 times a day save you more time overall than the clever ones you run once a week. If you’re not already keeping a running file of these for yourself, start one. Every time you catch yourself typing the same thing twice, that’s a candidate.

这些技巧单独来看都不算什么开创性的东西。但这正是重点所在:那些你每天运行 50 次的微小、枯燥的快捷方式,比你每周运行一次的“聪明”技巧更能节省时间。如果你还没有开始整理自己的快捷方式文件,现在就开始吧。每当你发现自己在重复输入同样的内容时,那它就是一个候选对象。