It’s normal behaviour of the language. Quoting the perlsyn manpage:
The number
0, the strings'0'and"", the empty list(), andundef
are all false in a boolean context. All other values are true. Negation
of a true value by!ornotreturns a special false value.
When evaluated as a string it is treated as"", but as a number, it is
treated as0.
Because of this, there needs to be a way to return 0 from a system call that expects to return 0 as a (successful) return value, and leave a way to signal a failure case by actually returning a false value. "0 but true" serves that purpose.