DEV Community

Cover image for My PC Screamed! How a `grep -r` Trap Led to 100% CPU and How I Prevented It with a JavaScript Git Hook
oji - building AI in public
oji - building AI in public

Posted on

My PC Screamed! How a `grep -r` Trap Led to 100% CPU and How I Prevented It with a JavaScript Git Hook

Hey there, it's your resident "old man" developer, 38 years old and tinkering with AI trading bots in my spare time.

One evening, as I was routinely checking my bot's logs, disaster struck. I wanted to search the entire repository for a specific log output, so I mindlessly typed this into my terminal:

grep -r "some_keyword" .
Enter fullscreen mode Exit fullscreen mode

The next second, my PC's fans roared like a Formula 1 car, and my mouse cursor started lagging severely. I opened Task Manager, and sure enough, CPU usage was pegged at 100%. I genuinely thought my machine was on its last legs. The culprits? grep.exe and, consequently, Windows Defender's real-time scan, which went haywire.

Ultimately, I had to force-restart my PC. Who knew a simple grep could cause such a catastrophe? Today, I'm sharing the story of this "grep scream incident" and the mechanism I built to prevent it from ever happening again.

The Problem: .gitignore Is Just a Text File

Why did grep go rogue? The reason became clear quickly.

My repository contained several massive log files generated by the bot. These were temporarily placed there for testing, with the largest one clocking in at 23GB. Naturally, I had these files properly listed in .gitignore to exclude them from Git's tracking.

But here's where my huge misconception lay: the shell's grep command knows absolutely nothing about .gitignore. To grep, .gitignore is just ignore.txt—nothing more, nothing less.

grep -r recursively, and naively, reads all files under the current directory. So, it plunged headfirst into that 23GB log file. No wonder the CPU screamed.

This same issue can occur with find . | xargs grep "..." or PowerShell's Select-String -Path . -Pattern "..." -Recurse. Essentially, any command that recursively scans the file system is susceptible to this trap.

Especially when working with automated trading bots, gigabyte-sized logs and data files are a daily occurrence. It seems personal development environments are particularly prone to these kinds of accidents.

The Solution: "Being Careful" Isn't Enough, So I Automated It

Even if I vow to use rg (ripgrep) or ag—which respect .gitignore—I can already foresee myself accidentally typing grep out of habit. Humans forget, and humans make mistakes.

So, instead of relying on "being careful," I opted for an approach that would "physically prevent execution."

Specifically, I leveraged a "hook" mechanism that kicks off a specific script right before a command is executed in the terminal. If a dangerous grep command is about to run, the hook detects and blocks it.

I wrote this hook script in JavaScript. What it does is simple: it checks the command string using a regular expression.

// Check the command line about to be executed with a regular expression

// Roughly detect grep-like command call patterns
const GREP_CALL = /(?:^|[\s(])(?:[^\s|;&]*[\\/])?(r|e|f)?grep(?:\.exe)?(?:\s+(.*))?$/s;

// Detect short recursive flags like -r or -R
const SHORT_RECURSIVE_FLAG = /(?:^|\s)-(?!-)[A-Za-z]*[rR][A-Za-z]*(?=\s|$)/;

// findstr /s on Windows is equally dangerous, so detect it too
const FINDSTR_SUBDIRS = /\bfindstr(?:\.exe)?\b[^|;\n]*\s\/s\b/i;

function shouldBlock(toolName, command) {
  // Only target Bash or PowerShell execution
  if (toolName !== 'Bash' && toolName !== 'PowerShell') return false;

  // findstr /s is an immediate block
  if (FINDSTR_SUBDIRS.test(command)) return true;

  // Split the command by pipes or semicolons and check if any segment
  // includes a recursive grep call
  return command.split(/\|\||&&|[|;\n]/).some(isRecursiveGrepSegment);
}

function isRecursiveGrepSegment(segment) {
  const match = segment.match(GREP_CALL);
  if (!match) return false;

  const args = match[2] || '';
  // Check for long flags like --recursive or --directories=recurse
  const hasLongRecursiveFlag = /\s--(recursive|directories=recurse)\b/.test(args);
  // Check for short flags like -r, -R
  const hasShortRecursiveFlag = SHORT_RECURSIVE_FLAG.test(args);

  return hasLongRecursiveFlag || hasShortRecursiveFlag;
}
Enter fullscreen mode Exit fullscreen mode

With this script integrated into a hook, if I accidentally type grep -r ., the command will be blocked before execution with a message like: "Dangerous command! Please use rg instead." Peace of mind achieved.

The regular expression is a bit messy because I wanted to detect not just grep -r but also -R, --recursive, and grep commands connected via pipes. findstr /s causes equally nasty accidents, so I included it in the block list as well.

Summary and Lessons Learned

What I learned from this incident is, while obvious, crucial: you'll get burned if you don't properly understand what your tools are doing implicitly.

  • .gitignore is Git's rulebook, not the shell's.
  • Mechanical prevention is more robust and reliable than relying on self-discipline.
  • In personal development, especially with automation and bot development, huge files can be generated unintentionally. Safety measures in the development environment, though seemingly trivial, are incredibly important.

Just grep, but grep can be a beast. To keep my PC from screaming, I need to properly understand the characteristics of my tools. With this, I've made my development environment safer for those late-night coding sessions. 👍


I build and run small Python systems — trading bots, RAG APIs, scheduled automation — and write up whatever breaks along the way.

If a provider-agnostic RAG Q&A API is useful to you, mine is MIT-licensed on GitHub: rag-faq-api. It runs and passes its full test suite **with no API key* (offline stub LLM + hashing embedder), swaps to Claude / Gemini / OpenAI via one env var, and ships a retrieval-quality harness (Hit@k / MRR / Recall@k) with a chunking sweep.*

Top comments (1)

Collapse
 
systemcraftdev profile image
SystemCraftDev

Rough experience, glad the hook caught it going forward. Worth knowing for next time: git grep (built into every Git install, no extra tool needed) sidesteps this exact trap by default. It only searches tracked files, so anything covered by .gitignore — including that 23GB log — is automatically excluded, no config or hook required. git grep --untracked extends that to untracked-but-not-ignored files if you want a broader search, still skipping ignored ones. The custom hook is still a nice safety net for anyone who might type plain grep -r out of habit, but if the search always happens inside a repo, git grep solves the root problem (grep not knowing what .gitignore means) rather than blocking the symptom.