跳到正文
跟我做
自媒体自动化8 分钟FFmpeg

ffmpeg 不是内部或外部命令:先查 PATH

一个不装依赖的 Node 脚本,检查 ffmpeg 和 ffprobe 是否在 PATH 里、常见安装目录有没有文件,并按 Windows、macOS、Linux 给出下一步。

约 8 分钟
本页目录
  1. 先别急着重装
  2. 报错长什么样
  3. 跑一次检查
  4. 看输出判断
  5. 按系统修 PATH
  6. Windows
  7. macOS
  8. Linux
  9. 已经装在别的位置
  10. 改完 PATH 还是不行
  11. ffprobe 也要检查
  12. 常见坑
  13. 1. 把 ffmpeg.exe 复制到项目目录
  14. 2. 只在当前终端 export
  15. 3. 把 npm 的 ffmpeg-static 当成系统命令
  16. 4. 从不明来源下载
  17. 下一步读什么

30 秒速查

  • 先跑 node check-ffmpeg.mjs,看是「没装」还是「没进 PATH」
  • 没装:Windows 解压到 C:\ffmpeg;macOS brew install ffmpeg
  • 装了找不到:把 bin 目录加进 PATH,重开终端
  • 验证:ffmpeg -version 和 ffprobe -version

先别急着重装

装完 ffmpeg,终端还是回你「不是内部或外部命令」。重装通常没用:问题往往不在 ffmpeg,而在 PATH。

终端不搜整个硬盘,只按 PATH 里的目录逐个找。ffmpeg 装了,但 bin 目录不在 PATH 里,终端就当作没装。

先分清三种情况,再动手:

现象 原因 先做什么
从没装过 系统里没有 ffmpeg 安装
装过,但提示找不到 bin 目录没进 PATH 加 PATH
加过 PATH,还是找不到 终端没重开、改错了用户变量,或只改了当前会话 重开终端再查
  1. 从没装过 → 安装

    系统里没有 ffmpeg,按自己的系统装一次完整版。

  2. 装过但找不到 → 加 PATH

    bin 目录没进 PATH,终端就当作没装。

  3. 加过还找不到 → 重开终端

    终端没重开、改错用户变量,或只改了当前会话。

图 1:先分清三种情况,再决定是装、是加 PATH,还是重开终端。

报错长什么样

不同终端的说法不一样,意思相同:在 PATH 里没找到这个命令。

Windows 的 cmd:

'ffmpeg' 不是内部或外部命令,也不是可运行的程序或批处理文件。

Windows 的 PowerShell 会啰嗦一些,开头是:

ffmpeg : 无法将“ffmpeg”项识别为 cmdlet、函数、脚本文件或可运行程序的名称。

macOS 的 zsh:

zsh: command not found: ffmpeg

Linux 的 bash:

bash: ffmpeg: command not found

看到这些,不要先怀疑安装包坏了。先确认文件到底在不在,以及它在不在 PATH 里。

跑一次检查

新建 check-ffmpeg.mjs,把下面整段复制进去。它只用 Node 自带模块,不装依赖:

import { existsSync } from "node:fs";
import path from "node:path";
import { spawnSync } from "node:child_process";
import { pathToFileURL } from "node:url";

const HINTS = {
  darwin: "macOS:brew install ffmpeg",
  win32: "Windows:从 ffmpeg.org 下载构建包,解压后把 bin 目录加进用户 PATH",
  linux: "Linux:sudo apt install ffmpeg(Debian/Ubuntu)或 sudo dnf install ffmpeg(Fedora)",
};

export function hintFor(platform = process.platform) {
  return HINTS[platform] ?? HINTS.linux;
}

export function commonPaths(platform = process.platform, env = process.env) {
  if (platform === "win32") {
    const home = env.USERPROFILE ?? "C:\\Users\\你";
    const local = env.LOCALAPPDATA ?? path.win32.join(home, "AppData", "Local");
    return [
      "C:\\ffmpeg\\bin\\ffmpeg.exe",
      "C:\\Program Files\\ffmpeg\\bin\\ffmpeg.exe",
      "C:\\ProgramData\\chocolatey\\bin\\ffmpeg.exe",
      path.win32.join(home, "scoop", "shims", "ffmpeg.exe"),
      path.win32.join(local, "Microsoft", "WinGet", "Links", "ffmpeg.exe"),
    ];
  }

  if (platform === "darwin") {
    return ["/opt/homebrew/bin/ffmpeg", "/usr/local/bin/ffmpeg", "/opt/local/bin/ffmpeg"];
  }

  return ["/usr/bin/ffmpeg", "/usr/local/bin/ffmpeg", "/snap/bin/ffmpeg"];
}

export function pathEntries(env = process.env, platform = process.platform) {
  const raw = env.PATH ?? env.Path ?? "";
  return raw.split(platform === "win32" ? ";" : ":").filter(Boolean);
}

export function findInPath(command, { platform = process.platform, env = process.env, exists = existsSync } = {}) {
  const names = platform === "win32" ? [`${command}.exe`, command] : [command];

  for (const dir of pathEntries(env, platform)) {
    for (const name of names) {
      const file = platform === "win32" ? path.win32.join(dir, name) : path.join(dir, name);
      if (exists(file)) return file;
    }
  }

  return null;
}

export function parseVersion(output, tool = "ffmpeg") {
  const match = new RegExp(`^${tool} version (\\S+)`, "m").exec(output);
  return match ? match[1] : null;
}

function probeCommand(tool) {
  const result = spawnSync(tool, ["-version"], { encoding: "utf8" });
  if (result.error || result.status !== 0) return { found: false };

  const output = `${result.stdout ?? ""}\n${result.stderr ?? ""}`;
  return { found: true, version: parseVersion(output, tool) };
}

export function diagnose(options = {}) {
  const {
    platform = process.platform,
    env = process.env,
    exists = existsSync,
    probe = probeCommand,
  } = options;

  return {
    platform,
    ffmpeg: probe("ffmpeg"),
    ffprobe: probe("ffprobe"),
    candidates: commonPaths(platform, env).map((file) => ({ file, exists: exists(file) })),
  };
}

function printReport({ platform, ffmpeg, ffprobe, candidates }) {
  const status = (item) => (item.found ? (item.version ?? "已安装(版本未知)") : "未找到");

  console.log(`ffmpeg:${status(ffmpeg)}`);
  console.log(`ffprobe:${status(ffprobe)}`);

  if (ffmpeg.found) {
    const file = findInPath("ffmpeg", { platform });
    if (file) console.log(`路径:${file}`);
  }

  if (ffmpeg.found && ffprobe.found) {
    console.log("\n状态:可用,视频脚本可以继续。");
    return;
  }

  if (ffmpeg.found && !ffprobe.found) {
    console.log("\nffprobe 缺失。它通常和 ffmpeg 在同一个安装包里,重装完整版本即可。");
    console.log("装完重开终端,再运行 ffprobe -version 确认。");
    return;
  }

  console.log("\n常见安装位置:");
  for (const item of candidates) {
    console.log(`  ${item.exists ? "存在  " : "不存在"}  ${item.file}`);
  }

  const installed = candidates.filter((item) => item.exists);
  console.log("\n怎么处理:");

  if (installed.length > 0) {
    console.log(`  已经找到了文件,但它不在 PATH 里:${installed[0].file}`);
    console.log("  把它的 bin 目录加进 PATH,重开终端后再运行一次这个脚本。");
  } else {
    console.log(`  ${hintFor(platform)}`);
  }

  console.log("  装完先关掉终端再重开,然后运行 ffmpeg -version 确认。");
}

const isDirectRun =
  process.argv[1] &&
  pathToFileURL(path.resolve(process.argv[1])).href === import.meta.url;

if (isDirectRun) {
  printReport(diagnose());
}

代码只做三件事:

  • 分别运行 ffmpeg -version 和 ffprobe -version。找不到时 spawnSync 返回 ENOENT,不会把终端搞崩。
  • 如果找不到,检查各系统最常见的安装位置,区分「文件存在」和「文件不存在」。
  • 只有在 PATH 里找不到时,才检查安装目录;这一步能直接指出「装了但没加 PATH」。

看输出判断

在一台没装 ffmpeg 的 Linux 上,输出是这样:

ffmpeg:未找到
ffprobe:未找到

常见安装位置:
  不存在  /usr/bin/ffmpeg
  不存在  /usr/local/bin/ffmpeg
  不存在  /snap/bin/ffmpeg

怎么处理:
  Linux:sudo apt install ffmpeg(Debian/Ubuntu)或 sudo dnf install ffmpeg(Fedora)
  装完先关掉终端再重开,然后运行 ffmpeg -version 确认。

装好之后,大概是:

ffmpeg:7.1.1
ffprobe:7.1.1
路径:/opt/homebrew/bin/ffmpeg

状态:可用,视频脚本可以继续。

版本号和路径会随安装方式不同而变,不用逐字对上。

终端里 ffmpeg -version 输出版本号、编译信息和 configuration 行
① 版本号出来了,说明 ffmpeg 能跑(本机真实输出)

如果中间出现「存在 /usr/local/bin/ffmpeg」,但上面的 ffmpeg 仍是「未找到」,就是第二种情况:文件装了,但它所在的目录不在 PATH 里。按下一节把 bin 目录加进去。

按系统修 PATH

Windows

  1. 打开 ffmpeg.org,进入 Windows builds(gyan.dev 或 BtbN 的构建),下载 ffmpeg-release-essentials.zip。
  2. 解压到 C:\ffmpeg,确认这个文件存在:C:\ffmpeg\bin\ffmpeg.exe。
  3. 在开始菜单搜「环境变量」→「编辑系统环境变量」→「环境变量」。
  4. 在「用户变量」里选中 Path →「编辑」→「新建」→ 填 C:\ffmpeg\bin → 一路确定。加完回头看一眼,列表里出现 C:\ffmpeg\bin 就对了。
  5. 关掉所有终端窗口,重新打开,运行:
where ffmpeg

看到 C:\ffmpeg\bin\ffmpeg.exe 就成了。

注意加的是 bin 目录,不是 C:\ffmpeg。加错一层,终端还是找不到。

macOS

用 Homebrew:

brew install ffmpeg
which ffmpeg

Apple Silicon 的 Homebrew 在 /opt/homebrew,命令应该在 /opt/homebrew/bin/ffmpeg;Intel Mac 通常在 /usr/local/bin。如果 which 还是空,先确认 Homebrew 的 bin 在 PATH 里(~/.zshrc 是开终端时自动读的配置文件,source 让它当场生效):

brew --prefix
echo 'export PATH="/opt/homebrew/bin:$PATH"' >> ~/.zshrc
source ~/.zshrc

再重开终端,跑一次 which ffmpeg。

Linux

Debian / Ubuntu:

sudo apt install ffmpeg

Fedora:

sudo dnf install ffmpeg

装完用 which ffmpeg 确认,路径通常是 /usr/bin/ffmpeg。

已经装在别的位置

如果文件不在上面这些常见目录,找到 ffmpeg(Windows 是 ffmpeg.exe)实际所在的 bin 目录,把它加进 PATH 就行。规则只有一条:加到 bin,不是加到 exe 本身,也不是加到它的上一级目录。

改完 PATH 还是不行

按可能性从高到低查:

  1. 终端没重开。 Windows 的 cmd、PowerShell、VS Code 内置终端都要关掉重开;只关标签页不算。
  2. 改错了变量。 Windows 有「用户变量」和「系统变量」,改当前用户就用用户变量;路径拼错也会失效。
  3. bash 缓存了旧位置。 运行 hash -r;zsh 用 rehash。
  4. 装到了另一个用户。 管理员账户装的,当前用户不一定能读到;改成当前用户安装,或换对应用户。
  1. 终端没重开

    cmd、PowerShell、VS Code 内置终端都要关掉重开;只关标签页不算。

  2. 改错了变量

    改当前用户就用「用户变量」;路径拼错同样失效。

  3. bash 缓存了旧位置

    运行 hash -r;zsh 用 rehash。

  4. 装到了另一个用户

    管理员账户装的当前用户读不到,改成当前用户安装。

图 2:改完 PATH 还不行,按这 4 条依次排查。

ffprobe 也要检查

站内处理视频的脚本不只用 ffmpeg,还用 ffprobe 读时长和尺寸。它通常和 ffmpeg 在同一个包里,装完整版就会一起出现。

单独确认:

ffprobe -version

如果 ffmpeg 有、ffprobe 没有,说明装的是裁剪包,重装完整版即可。

常见坑

1. 把 ffmpeg.exe 复制到项目目录

终端不会去当前目录找命令,除非写 ./ffmpeg.exe。站内脚本默认调用 ffmpeg,复制过去没用。

2. 只在当前终端 export

export PATH="/path/to/ffmpeg/bin:$PATH" 只对当前窗口有效,关掉就没了。写进 ~/.zshrc、~/.bashrc,或用 Windows 的环境变量界面。

3. 把 npm 的 ffmpeg-static 当成系统命令

它给的是一个文件路径,不是全局命令。脚本里可以 import 那个路径,但在终端直接敲 ffmpeg 仍然找不到。

4. 从不明来源下载

用 ffmpeg.org 上列的构建。装完先跑 ffmpeg -version,确认能输出。

下一步读什么

ffmpeg 能用之后,就可以做一条视频变三个平台版本了:改尺寸、抽封面帧、生成三版文案,都靠它。

如果 ffmpeg 装上了、npm install 又报错,先看npm install 报错。

这篇也是从 0 开始:第一台自动化工作台里的环境检查步骤;做完可以回到自媒体自动化入门按顺序往下走。

常见问题

我明明装了 ffmpeg,为什么终端还是找不到?

最常见的是 bin 目录没进 PATH,或者 PATH 改了但终端没重开。终端只在启动时读一次 PATH;已经开着的窗口、VS Code 内置终端都还是旧环境。先跑文中的检查脚本,它会告诉你文件在不在、在不在 PATH 里。

Windows 加完 PATH 要重启电脑吗?

不用。关掉所有终端窗口再重开就够了;如果用的是 VS Code 或 Cursor 的内置终端,整个编辑器都要退出重开,只关终端标签页不够。

brew 装了 ffmpeg,which 还是找不到?

Apple Silicon 的 Homebrew 装在 /opt/homebrew,bin 目录是 /opt/homebrew/bin。如果它不在 PATH 里,把 export PATH="/opt/homebrew/bin:$PATH" 写进 ~/.zshrc(开终端时自动读的配置文件),再 source 一次(让它当场生效)并重开终端。也可以用 brew --prefix 确认实际路径。

ffprobe 是什么,必须装吗?

ffprobe 和 ffmpeg 来自同一个项目,用来读视频的时长、尺寸、编码信息。站内处理视频尺寸和抽封面的脚本会用到它。官方安装包和 Homebrew 都会一起装;只装了 ffmpeg 的裁剪包可能缺它,重装完整版即可。

能不能把 ffmpeg.exe 放在脚本旁边直接用?

临时测试可以,用 ./ffmpeg.exe 或写绝对路径;但站内脚本默认调用 ffmpeg 这个命令,不会去脚本目录找。长期用还是把 bin 目录加进 PATH,换项目、换目录都不用改代码。

每周一张复习表

每周分享一张实用复习表,不收钱不打扰,想走随时走

邮箱存在 Cloudflare KV 里,只用来发每周更新;任何时候写信到hello@genwozuo.com说「退订」就删掉。详见隐私政策。

同方向继续

全部教程