告诉计算机去做事
Telling a Computer to Do Things

原始链接: https://will-keleher.com/posts/telling-your-computer-to-do-things/

在职业生涯初期,作者将命令行视为运行独立程序的简单界面。随着时间的推移,他们意识到掌握 Shell(编写逻辑脚本并将工具串联起来的能力)是一项具有变革意义的技能。 作者认为,仅依赖图形界面(GUI)会限制工程师的潜能,因为这会将他们束缚在预设的工作流程中。尽管 Shell 脚本(Bash/Zsh)以晦涩难懂著称,但其强大的地方在于能够以极低的开销将程序“缝合”在一起,这一点优于 Node.js 等更笨重的语言。 至关重要的是,作者指出 Shell 的精通程度中,语法只占 20%,剩下的 80% 在于了解命令行工具箱(如 `fzf`、`xargs`、`rsync` 和 `gh`)。一旦工程师掌握了这些工具,就能轻松实现复杂任务的自动化、文件处理和环境管理。无论是使用传统的 Shell 脚本还是像 `zx` 这样的现代替代方案,其核心价值是一致的:获得掌控计算机的主动权,而非受限于它。学会驾驭工具而非仅仅通过点击操作,是成为一名高效工程师的基础。

这段 Hacker News 的讨论探讨了 Shell 脚本与高级语言及图形界面(GUI)相比的哲学与实用性。 讨论主要分为三个观点: * **Shell 的力量:** 支持者认为,Shell 脚本真正的力量不在于语言本身,而在于 Unix “一切皆文件”的哲学、通用的输入/输出接口以及标准工具的可用性。他们主张,掌握 Shell 能提供一种稳定的、跨环境的技能组合,这比不断学习演变中的编程生态系统更具优势。 * **实用的替代方案:** 批评者认为,由于 Shell 脚本容易引发“自残式”错误(危险操作)且难以调试,用户在处理复杂任务时可能更适合使用 Python。另一些人则认为,人工智能工具正在降低准入门槛,让用户能够通过简单的英语直接“告诉”计算机该做什么,而无需背诵晦涩的语法。 * **“胶水”论:** 许多人强调,Shell 依然是 Unix 系统中最好的“胶水”。虽然其他语言也能执行类似任务,但在串联进程、管理信号或控制硬件和远程设备时,它们往往缺乏 Shell 那样的工程效率。
相关文章

原文

For the first few years of my career, I didn’t know how to tell my computer to do things. I could kick off a few commands from the terminal – run those tests, install that dependency, start that container, ssh to that machine – but I was limited to running simple commands one at a time. My terminal was the world’s worst GUI, and I thought that the shell was the way to start programs that didn’t have application wrappers.

At a fundamental level, I wasn’t able to tell my computer to accomplish anything that involved logic or stitching together multiple programs like:

  • Do one thing and then do another
  • If a command fails, log out an error message
  • Kick off a program when the computer starts
  • Run two commands at the same time
  • Loop through all of the files in a directory and take an action on each one
  • Use the output of one command as the input for another

In theory, I could have used NodeJS to write those sorts of programs. In practice, I never did. This was partially mindset: I wasn’t used to thinking about the programs I used on the command line as things that I could control. And the rest of it was a lack of skill: I didn’t know the programs that I was using well enough to integrate them into a script that I’d written.

I kept trying to learn the shell though, and I slowly got to the point where I could muddle my way through scripts like this one that had basic logic:

set +e
npm install
status_code=$?
if [[ "$status_code" != "0" ]]; then
    echo "Something went wrong with your npm install. Check your ~/.npmrc to make sure it's authed to our registry."
    exit 1
fi
set -e

Depending on your familiarity with shell scripting, You might be gibbering right now. Sorry. (If you’re not bleeding from the eyes yet, here’s why you should be: )

Even though the commands and scripts I wrote had problems, it was transformational for me; I had the sudden ability to tell my computer to stitch together existing programs to accomplish my goals. It felt a little bit like the change that came from learning to program in the first place.

Over the course of years, I slowly started to learn more shell tools and figure out shell syntax. I’d often learn a new tool or pattern and then have a moment of pain when I realized how much easier a past problem would have been to solve if I hadn’t used a hammer to solve a problem that needed a drill.

Over that time, I’ve worked with a ton of (incredibly strong!) engineers who didn’t spend as much time learning the shell, and instead relied on GUIs to do things like run tests, manage git, talk to databases, and do day-to-day tasks. Relying on a GUI works well until you want to accomplish something that the GUI wasn’t set up to handle, and I think it’s easy to slip into a mindset where you’re limited to what the GUI is capable of. I’ve seen skilled engineers spend a ton of effort because they didn’t know how to do things like use while to keep running a command or write a for loop to do the same operation on every file in a directory.

I think that same GUI-focus can be a problem when it comes to editing and maintaining shell scripts. I’d wager most companies have a decent amount of essential logic to build, deploy, validate code, and test in languages like Bash or Zsh. If you’re not comfortable with the language your tooling is built in, then you won’t be able to easily read or improve it. You might be able to tell the remote servers that your code runs on how to behave but not be able to tell the computer that you work on how to do things like run linting and tests in parallel – that’s a bummer!

Let’s pause to take a quick detour: Why the heck are so many of these scripts end up written in languages that aren’t the main ones the team uses? I think it’s often more ergonomic to write a script that stitches together commands in a language that’s been designed to be easy to stitch together commands. Let’s take a super simple example of running a test until it fails: while pnpm exec mocha ./pathToFile.test.ts; do true; done. There are obvious things to critique about this syntax, but let’s take a look at what it looks like in NodeJS:

const { execSync } = require("child_process");
while (true) {
    try {
        execSync(`pnpm exec mocha ./pathToFile.test.ts`,  { stdio: "inherit" });
    } catch (err) {
        console.error("failed", err);
        break;
    }
}

There are a lot of rough edges and gotchas here, and I personally think the shell is easier! I don’t need to worry about creating a file, requiring child_process, or setting { stdio: "inherit" } to see output. And this is a pretty simple example that doesn’t even stitch together multiple programs with a pipe, capture any output, or use a temporary file!

💡

This doesn’t mean that you need to resign yourself to writing in Bash or Zsh or any similar language! For teams that know JavaScript well, one tool I’ve enjoyed is zx. I think it can make these scripts pretty ergonomic to write and maintain. Ruby and Python are both easier than NodeJS is, but I think there are plenty of languages out there that require even more ceremony to write a quick little script like this.

I’m certainly not arguing that shell scripts will always be easier for build scripts! When problems are simple enough that you’re just stitching two programs together, a tool like Bash or Zsh feels pretty ergonomic. As soon as you want more sophisticated logic and data types, you’ll want to choose a language that makes it easy to represent (and test!) more sophisticated logic and data types.

I suspect that many engineers who gripe about build scripts being written in a shell language haven’t actually tried converting them to a different language. Aside from the syntax (potentially) being more complicated, a huge part of "learning to write shell scripts" isn’t actually syntactical. If you convert a script that stitches together commands but don’t actually know how the commands you’re stitching together behave, the resulting script is likely to be similarly impenetrable.

Knowing the shell – being able to tell a computer to do things – depends a ton on knowledge of the programs that do the things that you want to accomplish! I’d argue that knowing the shell is 20% syntax and 80% having a good toolbox:

  • If you know fzf, you can build quick utilities with interactive fuzzy-searching. (Example: git checkout $(git branch --sort=-committerdate | fzf) will let you fuzzy-choose a branch.)
  • If you know tldr or eg, you can pull up usage examples for any other command in this list
  • If you know rsync, you can copy changed files on to a faster remote server to run something heavy and slow
  • If you know xargs, you can build up commands incrementally and parallelize work
  • If you know sed -i or ast-grep, you can quickly rewrite complicated patterns across a bunch of files
  • If you know direnv, you can make sure the right environment variables are set for everyone who runs commands in a codebase.
  • If you know duckdb, you can write SQL to query CSV and JSON files locally as part of a larger script.
  • If you know gh, you can build scripts to check on your PRs and open up new PRs from the cli.
  • If you know ngrok, you can quickly serve a local port to test something out on a different machine.

Each additional program you learn expands your capabilities more because each new tool can be used with every other tool you already know.

I can’t stress enough that I’m the furthest thing in the world from a shell scripting expert, and I’m terrible compared to people who know it deeply, but I’ve still gotten a lot of value out of knowing enough shell syntax to stitch together programs and knowing enough programs that I actually want to stick together.

Telling your computer to do things is great!

联系我们 contact @ memedata.com