Four hundred lines of build output, and the failure is on line 212. Scrolling for the word "error" gets you there eventually. There is a faster way to ask.

The idea

The is a small language for joining programs together. Each command reads some text, writes some text, and finishes with a number that says whether it worked. Learn to send one command's output into the next, stop a sequence when a step fails, and search a stream of text, and you can ask the exact question you want answered instead of reading whatever the computer printed.

code
  pnpm build            |          grep  "error"          →  exit status
  ────────────          ▼          ─────────────              ──────────
  writes 400 lines   the pipe   reads them, prints        this is GREP's
  of output          carries    only matching lines       status, not the
                     the text                             build's. 0 here
                                                          means grep found
                                                          a match.

How it works

  • The pipe, |. git log --oneline | head -20 hands git log's output to head, so you see the last twenty commits instead of all nine hundred. Nothing is saved to disk. The text moves left to right.
  • Chaining with &&. pnpm typecheck && pnpm test runs the tests only if the typecheck succeeded. Compare ;, which runs the second command regardless. If you have ever watched a test suite pass on code that does not compile, you wanted &&.
  • . Type echo $? right after a command to see it. Zero meaning success is backwards from most people's guess, and it is why && behaves as it does.
  • grep. It prints the lines matching a pattern. grep -rn "DATABASE_URL" src/ searches every file under src/, recursively, printing the file name and line number of each hit. That answers "where is this used" in two seconds.
  • --help. Read it before pasting a you do not recognise. Some commands answer with a short list, some open the manual in a pager, and q leaves a pager.
  • Backgrounding. A trailing &, as in pnpm dev &, starts the command and gives you your prompt back. jobs lists what is running, fg brings one to the front, and Ctrl-C then stops it. The job still prints into the same terminal, so a chatty dev server scribbles over what you type. For anything long-running, a second terminal tab is better.

On Windows. These are bash and zsh moves, and PowerShell is a different language. && is a parse error in Windows PowerShell 5.1, the version built into Windows, and works only in 7 and later. $? is a true/false value there, so the exit code you want is $LASTEXITCODE. There is no grep (Select-String) and no head (Select-Object -First 20). Trailing & has worked since PowerShell 6, but starts a background job in a separate process rather than a command you can pull to the front: Get-Job lists them, Receive-Job -Wait hands you the output, and there is no fg. WSL and Git Bash give you a real bash on Windows, the shortest path to these examples working as printed.

What to do

  1. Next time a build fails, capture the whole run and search the capture.

    code
    pnpm build > build.log 2>&1; echo "exit status: $?"
    grep -n -i -E "error|failed|cannot" -A 3 build.log

    > build.log sends the output to a file instead of the screen and 2>&1 sends the error output to the same file. The ; is deliberate: the echo has to run whether the build passed or failed. In the search, -i ignores case, -E lets | mean "or" inside the pattern, and -A 3 prints the three lines after each hit, where the filename and the explanation live. Trust the exit status over the grep output. A pipeline reports the status of its last command, so pnpm build | grep error answers a question about grep. Zero matches print nothing and look exactly like success, and compilers disagree about the word: Next.js says Failed to compile., pnpm says ELIFECYCLE.

  2. Replace the next two commands you run in sequence with one && chain. pnpm typecheck && pnpm test is a safe one to start on. Keep git add -A && git commit out of your habits: -A stages every untracked file in the repository, including the .env your .gitignore happens to miss, and a pushed commit is hard to unpublish. Run git status, then stage the files you meant, by name.

  3. Pick one command you use blind and read its help. Expect two shapes. git log --help prints nothing and opens the full manual in a pager, so press q to get out; git log -h gives the short flag list. curl --help shows a dozen or so common options, depending on your version, and says at the bottom that it has hidden the rest, which need curl --help all.

Where it breaks

grep matches text, so it has no idea what your code means. Searching for user hits comments, variable names, and the word "user" inside a string. It gives you a short list of places to look, and you still have to look.

Chaining is also a way to build something dangerous quickly. A command that would have made you pause alone gets less attention as the fourth link in a chain, and an AI assistant will happily compose a long one. Before running a chain, find the step that touches something real, a file, a database, a server, and read that one first. Anything with rm, --force, or a production hostname in it deserves the pause you would have given it on its own.