0

I met an interesting issue while working with this code from Stack Overflow: tripleee's answer on "How to check if a file contains only zeros in a Linux shell?"

Why does the same bash code produce different result depending on interactive shell or subshell?

Make all-zeros file with name your_file.

$ truncate -s 1K your_file

Interactive shell example

$ tr -d '0円' <your_file | grep -am 1 ^ ; echo $?
1

The same code but using subshell

$ bash -c 'tr -d '0円' <your_file | grep -am 1 ^ ; echo $?'
0

And also interesting fact. I changed original example by adding -a option ("equivalent to --binary-files=text") because without this option interactive shell works but subshell:

$ bash -c 'tr -d '0円' <your_file | grep -m 1 ^ ; echo $?'
grep: (standard input): binary file matches
0

P.S. I use bash 5.2.37(1)-release from Ubuntu 25.04

wjandrea
7059 silver badges22 bronze badges
asked Jul 21 at 13:34
4
  • 10
    You can't embed single-quotes inside single-quotes in shell. in place of '0円', try '\''0円'\''. Or just use double-quotes around the entire sub-shell command (but you'll need to escape the $ in $? as \$? so that the shell you're running it from doesn't interpolate it) Commented Jul 21 at 13:48
  • 4
    BTW, see Why does my shell script choke on whitespace or other special characters?, $VAR vs ${VAR} and to quote or not to quote and When is double-quoting necessary? for more info about shell quoting Commented Jul 21 at 13:53
  • 2
    that's not a subshell Commented Jul 22 at 13:10
  • 1
    Use ShellCheck to find problems like this. It flags this as "This 0円 will be a regular '0' in this context." and "This word is outside of quotes." Commented Jul 23 at 13:50

1 Answer 1

19

This is just a quoting issue. When you run this:

bash -c 'tr -d '0円' <your_file | grep -am 1 ^ ; echo $?'

It becomes:

  1. bash -c 'tr -d '
  2. 0円 (which is just 0)
  3. ' <your_file | grep -am 1 ^ ; echo $?'

You are using single quotes within the single quotes, so your '0円' actually becomes 0円 which is just 0. You can see it happen with set -x:

$ bash -c 'tr -d '0円' <your_file | grep -am 1 ^ ; echo $?'
+ bash -c 'tr -d 0 <your_file | grep -am 1 ^ ; echo $?'
0

The presence of NUL (0円) in a file will mark it as binary data. So the fact that grep complained about that is also a hint. As you can see above, you are not actually removing 0円, you are removing 0.

Next, the exit status of grep, which is what you have in your $? variable, is 0 if at least one line matches and 1 if no line matches. In your first command, tr -d '0円' <your_file | grep -am 1 ^ ; echo $?, you are deleting all 0円 from a file that consists of nothing but 0円 so you end up with an empty file. Therefore, nothing is matched and you get an exit status of 1.

With your second command, bash -c 'tr -d '0円' <your_file | grep -am 1 ^ ; echo $?', because of the quoting issue, you are removing 0. The file has no 0, so nothing is removed and there is one "line" to be found and so the output is 0.

The second shell is completely irrelevant, you can get the same behavior without it:

$ tr -d '0円' <your_file | grep -am 1 ^ ; echo $?
1
$ tr -d 0 <your_file | grep -am 1 ^ ; echo $?
0

To get the expected output in the second shell, use:

$ bash -c 'tr -d "0円" <your_file | grep -am 1 ^ ; echo $?'
1
answered Jul 21 at 13:50

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.