The following:
expr "/foo" : "/"
Results in a syntax error. I don't understand why?
These variations do not cause a syntax error:
expr "/foo" : "/*"
expr "/foo" : "/\/"
expr "foo" : "f"
At first I thought it might be an escaping issue, but forward slash doesn't seem to be documented as a special character for BRE's. And then I noticed that if either arg to expr string matching is a single forward slash, it causes the syntax error.
This also causes a syntax error:
expr "/" : "f"
(I'm running on a Mac - Darwin Kernel Version 24.6.0)
1 Answer 1
The forward slash isn't special in regular expression, nor is it special in the shell, so it doesn't need any quoting.
But these are also errors with the expr
on Mac:
% expr : : .
expr: syntax error
% expr % : .
expr: syntax error
The man page also has this note on one example:
Since [the variable] might represent the path
/
, it is necessary to prevent it from being interpreted as the division operator. The//
characters resolve this ambiguity.
So, it looks this version of expr
identifies anything that looks like an operator as an operator regardless of the position within in the expression, and then you get the syntax error in the above cases since an expression can't start with a binary operator. Even if interpreting those as strings instead would form a valid expression.
You could prepend something like an x
to both parts of the expression to prevent the strings from looking like operators, i.e. something like expr x/foo : x/
or expr x% : x.
works... at the cost of looking rather silly.
The man page has another note:
The syntax of the expr command in general is historic and inconvenient. New applications are advised to use shell arithmetic rather than expr.
And even if this isn't arithmetic, that's a good suggestion.
Instead of expr
, you can do regex matches directly in Bash1:
bash-3.2$ if [[ /foo =~ ^/ ]]; then echo match. ; fi
match.
or, if you just want to check if the string starts with a slash, a pattern match (like filename globs) would do:
bash-3.2$ if [[ /foo == /* ]]; then echo match. ; fi
match.
or, the same pattern match with a case
statement which works in any POSIX shell:
$ case /foo in /*) echo match. ;; esac
match.
1 with the caveat that bash's =~
uses Extended Regular Expressions while expr
uses Basic Regular Expressions, both having different feature sets: BREs support back reference (as in \(.\)1円
to match two identical characters) while EREs support alternation (as in (foo|bar)
); some ERE implementations do support back references and some BRE implementations support alternation (using \|
) as an extension over the standard though.
$SHELL --version
to your question! Thank you, and welcome here. Same forexpr --version
, please!expr "//foo" : "/"
work? This MacOS man page suggests "The // characters act to eliminate ambiguity with the division operator"/
argument is interpreted as a division operator which is missing operands, so you'd needexpr x/foo : x/
. I can reproduce on FreeBSD FWIW.expr --version
just output--version
on macOS, just like it does on OpenBSD and with busybox. It is less likely to be helpful in any other way than to let you know it's not the GNU coreutils variant, which I think has already been established.