换了一台新电脑,要把一个项目跑起来:比如一个 Python 写的网站,数据存在 PostgreSQL 里。装 Python、配镜像、拉代码、起数据库,每一步敲什么命令,现在问一下 AI 就有,而且比任何配置手册都新。这篇文章原来就是这样一份手册,所以改写了。
难的是出问题的时候:明明装了却提示「command not found」,配了镜像 Docker 还是拉不下来,同事电脑上能跑的代码到你这里就报错。这时候要判断 AI 给的方向对不对,靠的是知道每个工具是怎么工作的。
所以这里不列安装步骤,每个工具只讲三件事:解决什么问题、核心原理、最常用的几个用法。读这篇只需要用过命令行,知道「敲一行命令、回车执行」就够了。
WSL:在 Windows 上用 Linux
解决什么问题:服务器几乎都跑 Linux,大部分开发工具也是先为 Linux 设计的。WSL(适用于 Linux 的 Windows 子系统)让你不装双系统、不手动管虚拟机,就能在 Windows 里用上一个完整的 Linux。用 Mac 的话不需要它:macOS 本身就是类 Unix 系统,自带终端。
原理:WSL 2 在一个轻量的虚拟机里运行真正的 Linux 内核,由 Windows 自动管理它的启动和资源。Linux 的文件存放在一个虚拟磁盘文件(ext4.vhdx)里;Linux 这一边通过 /mnt/c 访问 Windows 的 C 盘。
明白了这两点,三个常见的坑就都能解释:
代码放在
/mnt/c下面,git status、npm install慢得出奇:跨系统访问文件要经过一层转换,读写大量小文件时尤其明显。代码应该放在 Linux 自己的文件系统里,比如~/projects。C 盘被占满:虚拟磁盘默认放在 C 盘,用得越多越大。要搬到别的盘,可以先
wsl --export导出成一个压缩包,再wsl --import导入到新位置。WSL 里用不了 Windows 上的代理:WSL 默认是 NAT 网络,有自己的 IP,访问 Windows 上的
127.0.0.1并不是同一个地方。较新的版本可以在.wslconfig里设成镜像网络模式(networkingMode=mirrored),和 Windows 共用网络与代理设置。
终端与 Shell
解决什么问题:用文字命令操作电脑,可以组合、可以重复、可以写成脚本,这是图形界面做不到的。终端是那个显示文字的窗口;Shell 是窗口里读你的命令、再去执行它的程序。bash 和 zsh 是两种常见的 Shell,Oh My Zsh 是 zsh 的一套配置和插件集合。
原理:你敲下一个命令时,Shell 做的事情大致是:
两个最关键的概念:
PATH:一个目录列表。Shell 从前往后找,用第一个找到的。「装了却说 command not found」,十有八九是程序装进了一个不在 PATH 里的目录;「装了新版本,用的却还是旧版本」,是旧版本所在的目录排在前面。
环境变量会被继承,但只往下传:子进程拿到的是父进程环境变量的一份拷贝。所以写在
~/.zshrc里的export只对之后新开的终端生效,已经打开的终端要source ~/.zshrc重新读一遍;已经在运行的程序则完全感知不到。
最常用的几个用法:
写法 | 作用 |
|---|---|
| 实际用的是哪一个 python |
| 去哪些目录找命令,按什么顺序 |
| 管道:前一个命令的输出,作为后一个命令的输入 |
| 把输出写进文件(覆盖、追加) |
| 前一个成功了才执行后一个 |
| 在历史命令里搜索 |
我在 Oh My Zsh 里常开的插件有四个:zsh-autosuggestions 根据历史命令提示你接下来可能要敲什么;zsh-syntax-highlighting 给命令上色,敲错了马上能看出来;z 记住常去的目录,z 目录名的一部分 就能跳过去;extract 让 x 文件名 解压任意格式的压缩包。
包管理器
解决什么问题:一个软件往往依赖很多别的软件,依赖又有依赖。手动下载安装既麻烦,又容易装出互相冲突的版本。不同层面有各自的包管理器:
包管理器 | 管什么 | 软件从哪来 |
|---|---|---|
apt | Ubuntu 的系统软件 | Ubuntu 的软件仓库 |
Homebrew | macOS(和 Linux)上的命令行工具,版本通常比 apt 新 | Homebrew 的仓库 |
pip、uv | Python 包 | PyPI |
Maven | Java 依赖 | Maven 中央仓库 |
npm | JavaScript 包 | npm registry |
原理:所有包管理器的工作方式都差不多,分四步:
理解了这四步,很多操作就不神秘了:
为什么要先 update:
apt update只更新本地的索引,apt upgrade才真正升级软件。索引旧了,就会找不到新版本。为什么会顺带装上别的东西:这是「解析依赖」那一步的结果。比如用 Homebrew 装 Maven,会自动带上一个 OpenJDK;已经有自己的 JDK 的话,可以加
--ignore-dependencies跳过。为什么会有锁文件:
package-lock.json、uv.lock这类文件记下了解析出来的确切版本,换一台机器也能装出一模一样的环境。「同事那里能跑,我这里报错」,先看双方装的版本是不是同一份锁文件解析出来的。安装说到底只是把文件放进一个目录:在公司的服务器上没有 root 权限、不能用 apt 时,可以下载源码,用
./configure --prefix=$HOME编译安装到自己的家目录,再确保~/bin在 PATH 里。我就这样在没有权限的服务器上装过 zsh。
apt 源里的 main、restricted、universe、multiverse 是什么
配置 Ubuntu 软件源时,每一行末尾那几个词是软件仓库的四个分区,按「是否自由软件」和「是否官方支持」划分:
自由软件 | 非自由软件 | |
|---|---|---|
官方支持 | main | restricted |
社区维护 | universe | multiverse |
镜像与代理
解决什么问题:国内访问很多国外的软件仓库很慢甚至不通,第一次装依赖时就会遇到。解决办法有两类,改变的东西不一样:
办法 | 改变的是什么 | 适合 |
|---|---|---|
镜像 | 下载的地址:从国内的一份副本下载 | apt、pip、Maven、npm、Docker 镜像这类有国内副本的东西 |
代理 | 网络的路径:流量绕道境外的服务器 | 没有国内副本的网站和服务,比如 GitHub |
原理:
镜像只替换下载地址。镜像是仓库的一份副本,配镜像就是告诉包管理器「去这里下载」,索引、解析依赖、安装都不变。副本可能不全:Maven 的镜像设成
mirrorOf为*时,会接管所有仓库的请求,镜像里没有的包就找不到了。所以我的 Maven 配置没有用全局镜像,而是列了几个仓库:先查阿里云,找不到再依次查中央仓库和 Confluent 仓库(Kafka 相关的依赖在那里)。代理靠环境变量告诉命令行程序。curl、git、pip 等很多工具会读取
http_proxy、https_proxy、all_proxy这几个环境变量,决定走不走代理(各工具对大小写写法的支持不完全一样,常见做法是大小写都设上)。结合 Shell 一节「环境变量只往下传」的原理就能理解:在一个终端里设置了代理,只对这个终端里之后启动的程序生效;Docker 拉镜像的是后台的 Docker 服务,它读不到你终端里的变量,要单独配置。
代理的原理、系统代理和 TUN 模式的区别,见 科学上网。
语言环境:Python 与 Java
Python:每个项目一个虚拟环境
解决什么问题:项目 A 要某个库的 1.x,项目 B 要 2.x,都装进系统的 Python 里必然冲突。venv、conda、uv 都能给每个项目建一个独立的环境。
原理:一个虚拟环境就是一个独立的目录,里面有自己的 Python 解释器(或指向它的链接)和自己的第三方包目录。所谓「激活」,不过是把这个目录下的 bin 放到 PATH 的最前面,于是 python 和 pip 都指向它。又是 PATH。
工具 | 特点 |
|---|---|
venv | Python 自带,最轻量 |
conda | 除了 Python 包,还能装 CUDA、编译器这类非 Python 的依赖,数据科学领域常用 |
uv | 用 Rust 写的新一代工具,安装很快,同时管理 Python 版本、虚拟环境和依赖锁定 |
用法要点:which python 确认当前用的是哪个环境;依赖写进文件(requirements.txt 或 pyproject.toml),不要只装在自己的机器上。
Java:JDK 与 Maven
解决什么问题:JDK 是 Java 开发工具包,包含编译器 javac 和运行 Java 程序的虚拟机(JVM);Maven 负责 Java 项目的构建和依赖。
原理:
Java 代码先编译成字节码(
.class文件),再由 JVM 执行,所以同一份编译结果可以在不同的系统上运行。一台机器上常常装着好几个 JDK,JAVA_HOME告诉各种工具用哪一个。Maven 用
pom.xml描述项目,每个依赖由「组织 : 名称 : 版本」三个坐标唯一确定。下载过的依赖缓存在本地的~/.m2/repository,下次直接用。构建是一条固定的生命周期:编译、测试、打包、安装、部署。执行后面的阶段,会自动先执行前面的。
最常用的几个用法:mvn package(编译、测试、打包);mvn dependency:tree(看每个依赖是从哪里引进来的,排查版本冲突时最有用)。
Git:版本管理
解决什么问题:记录代码的每一次修改,可以随时回退;多人同时修改同一份代码,也不会互相覆盖。拿到项目的第一步,通常就是 git clone。
原理:Git 的每一次提交,保存的是整个项目在那一刻的快照,再加上指向上一次提交的指针,提交连起来就是历史。分支只是一个指向某次提交的指针,所以创建分支几乎没有成本。代码在四个地方之间流动:
最常用的几个用法:
命令 | 作用 |
|---|---|
| 现在有哪些改动,分别在哪个区 |
| 具体改了什么 |
| 看提交历史和分支 |
| 新建并切换到一个分支 |
| 两种合并分支的方式:前者生成一个合并提交,后者把提交逐个接到另一条线的后面 |
SSH:安全地连上远程机器
解决什么问题:登录远程服务器、从 GitHub 拉取私有仓库、让 VS Code 在远程机器上开发,走的都是 SSH 这条加密通道。
原理:推荐用密钥对登录,而不是密码:
私钥始终留在你的电脑上,网络上传的只是签名,所以比密码安全得多。反过来,第一次连接某台服务器时,客户端会记下它的指纹(存在 ~/.ssh/known_hosts 里),以后指纹变了就会警告,防止连上冒充的服务器。
最常用的几个用法:
命令 | 作用 |
|---|---|
| 生成一对密钥 |
| 把公钥加进服务器的 |
| 端口转发:在本机打开 8080,就像访问服务器上的 80 端口 |
| 给常连的机器起别名,记住用户名、端口和密钥 |
自己搭服务器时 SSH 该怎么加固,见 从零搭建网站。
VS Code:编辑器
解决什么问题:一个编辑器,靠插件支持各种语言,既能编辑本地的代码,也能编辑远程机器、WSL、容器里的代码。
原理:VS Code 本身只是一个编辑器外壳,两项设计让它几乎什么都能做:
语言服务器协议(LSP):代码补全、跳转到定义、报错提示,都由每种语言各自的「语言服务器」提供,编辑器和语言服务器之间用统一的协议通信。所以同一个语言服务器,可以给不同的编辑器用。
远程开发:用 Remote-SSH 连接远程机器(或者连 WSL、容器)时,VS Code 会在那一边装一个 VS Code Server。界面留在你的电脑上,读写文件、终端、语言服务器和大部分插件都在远程那一边运行,所以用起来和本地开发一样。
用法要点:命令面板(Ctrl/Cmd + Shift + P)可以找到所有功能。远程开发时,插件分为装在本地的界面类插件和装在远程的工作区插件,同一个插件可能要在两边各装一次。我常用的插件是远程开发、Python、Extension Pack for Java、Git Graph(提交历史可视化)和 Code Spell Checker(拼写检查)。
AI 编程助手
解决什么问题:Claude Code、Codex 这一类编程 Agent 能读代码、改代码、运行命令;前面那些安装和配置,也可以直接交给它。
原理:大模型本身只会根据上下文生成文字(它是怎么做到的,见 从线性回归到大语言模型)。编程 Agent 给它加上了工具,再放进一个循环里:
能力的上限取决于模型,日常的表现很大程度上取决于两件事:给它的上下文,和它能用来验证的工具。
用法要点:
写一份项目说明:在仓库根目录放一份
AGENTS.md(Claude Code 读的是CLAUDE.md),写清怎么启动、怎么测试、有哪些不能破坏的约定。它每次开工都会先读,相当于新同事的入职文档。让它能自己验证:让它跑测试、看输出,比只看它写的代码可靠得多。
小步改,逐步看:每一步改动都看一眼
git diff,Git 是你的安全网。原理还是要懂:它会配环境,但环境出了问题,它也可能在错误的方向上越走越远。你懂 PATH、懂镜像和代理的区别,才能一眼看出它是不是在兜圈子。
Docker:把应用装进容器
解决什么问题:「在我电脑上明明能跑」。项目要用 PostgreSQL,与其在每台电脑上手动安装、配置一遍,不如把数据库连同它的环境打包好,一条命令启动。容器把环境本身变成了可以分发的东西。
原理:
镜像是分层的:Dockerfile 的每一步生成一层只读的文件系统。没变的层可以缓存、复用,所以改一行代码重新构建时,只需要重做后面几层。
容器就是一个被隔离的进程:它和普通进程一样运行在宿主机的内核上,只是用命名空间(namespace)隔离了它能看到的文件、网络和进程,用控制组(cgroup)限制了它能用的 CPU 和内存。所以容器启动只要一瞬间,不像虚拟机要启动整个操作系统。
在 Mac 上:容器需要 Linux 内核,所以 Docker Desktop、OrbStack 这些工具实际上先运行了一个轻量的 Linux 虚拟机,容器跑在它里面。和 WSL 是同一个思路。
最常用的几个用法:
命令 | 作用 |
|---|---|
| 启动容器,并把容器的 5432 端口映射到本机 |
| 看有哪些容器在运行,看它的输出 |
| 进到正在运行的容器里看看 |
| 按 |
容器删掉后,可写层里的数据也就没了。数据库文件这类要长期保存的数据,要挂载到卷(volume)上。
Kubernetes:把容器编排成集群
解决什么问题:一台机器用 Docker 就够了。机器一多,就要有人决定容器放在哪台机器上、挂了谁来重启、流量怎么分给多个副本、新版本怎么平滑上线。Kubernetes(简称 k8s)就是做这件事的容器编排系统。
原理:Kubernetes 的核心是声明式的:你只描述「想要的状态」,比如「这个应用要 3 个副本」;控制器不断比较想要的和实际的,发现不一致就动手调整,这个过程叫调和(reconcile)。
几个基本对象:Pod(一个或几个容器组成的最小调度单位)、Deployment(管理一组相同的 Pod,负责副本数和滚动更新)、Service(给一组 Pod 一个稳定的访问入口)。本地学习可以用 minikube 搭一个单机的集群。
最常用的几个用法:kubectl apply -f 文件(提交期望状态)、kubectl get pods、kubectl describe pod 名字(看它为什么起不来)、kubectl logs 名字。
小结:遇到问题,想哪条原理
开头说,出问题时要靠原理判断方向。下面这张表把常见的症状和对应的原理放在一起:
症状 | 先想到的原理 | 先做什么 |
|---|---|---|
装了却提示 command not found | Shell 按 PATH 找命令 |
|
装了新版本,用的还是旧的 | PATH 里先找到的先用 | 看 PATH 里目录的顺序 |
改了 | 环境变量只往下传 |
|
下载很慢或失败 | 镜像换地址,代理换路径 | 有国内副本用镜像,没有用代理 |
终端里有代理,Docker 还是拉不下来 | 后台服务读不到终端的环境变量 | 单独给 Docker 配置 |
同事那里能跑,我这里报错 | 依赖解析出的版本不同 | 对比锁文件,或者用容器 |
两个项目的依赖互相冲突 | 每个虚拟环境是一个独立目录 | 每个项目一个环境 |
WSL 里 | 跨系统访问文件要经过转换 | 代码挪到 |
容器删了,数据也没了 | 可写层随容器一起删除 | 数据挂载到卷上 |
Pod 一直起不来 | 控制器在不断调和 |
|
命令记不住没关系,随时可以问;这张表里的原理,值得自己记住。环境搭好之后,可以接着看 从零搭建网站,把自己的网站搭起来;想知道一次请求在网络上经过了什么,见 Life of a Request。
参考资料
MIT,The Missing Semester of Your CS Education:Shell、Git、编辑器等工具的系统课程。
Scott Chacon、Ben Straub,Pro Git(中文版):Git 快照和分支的原理见第 1、3 章。
Maven 文档:Introduction to the Build Lifecycle、Using Mirrors for Repositories
OpenSSH 手册:ssh(1):
-A、-L的说明和安全提示。Anthropic,Building effective agents(2024):Agent 是「模型根据环境的反馈,在循环里使用工具」。
AGENTS.md:给编程 Agent 看的项目说明的约定格式。
全文评论
0正在加载评论…