I've been using "strict mode" in my bash scripts for a few weeks now and it's generally been positive. However, I'm confused about a specific issue that arises when I use `cat`. Here's a snippet of my script:
```bash
#!/bin/bash
trap 'echo "ERROR: A command has failed. Exiting the script. Line was ($0:$LINENO): $(sed -n "${LINENO}p" "$0")"; exit 3' ERR
set -Eeuo pipefail
set -x
du -a /etc > /tmp/etc-files 2>/dev/null || true
ls -lh /tmp/etc-files
# works (without cat)
head -n 10 > /tmp/disk-usage-top-10.txt /tmp/disk-usage-top-10.txt
echo "done"
```
Can someone help me understand why this happens?
4 Answers
Just a heads-up, strict mode can definitely introduce these types of problems. It's a common issue that people run into, and while it helps catch errors, it can also complicate things unnecessarily. You’re not alone in finding it a bit tricky!
Just a tip on script formatting: it looks like there are issues in how you displayed your shell script. You might want to format it properly with four spaces before each line to make it easier to read, especially when you share it in forums.
For example:
```
#!/bin/bash
echo "This is code!"
```
The issue stems from how `cat` and `head` interact through a pipe. When `head` finishes reading its 10 lines, it exits, which triggers a SIGPIPE signal to `cat`. This causes `cat` to terminate as well, since it thinks its reader is gone and treats it as an error due to strict mode. It's possible it didn't break before because `cat` managed to finish writing before `head` completed, but with larger outputs, it can fail. This could be one of those quirkier cases of strict mode throwing up flags where it usually wouldn’t. Just be aware that with `pipefail`, the termination is treated as significant, unlike default behavior where it generally goes unnoticed.
Have you thought about simplifying your command? Instead of relying on `cat`, you could do something like this:
`du -a /etc | head -n 10 > /tmp/disk-usage-top-10.txt`
If you need that intermediate file for any reason, you could also use:
`du -a /etc | tee -a /tmp/etc-files >(head -n 10 > /tmp/disk-usage-top-10.txt)`
Yes, I see your point! The sorting is missing, though—this was just a minimal example I provided.
That’s really interesting! I get how piped commands could cause synchronization issues, but why is it that `head` isn't able to read everything from `cat` before it exits? Seems like a common usage.