This is happening because cut
is outputting NULL characters in the output. You can't pass a program arguments which contain a null character (see this).
In bash this works because bash can't handle NULL characters in strings, and it strips them out. Zsh is a bit more powerful, and it can handle NULL characters. However when it comes time to pass the string to the program, it still contains the null, which signals the end of the argument.
Let's look at this in detail.
$ echo 'English/Flavors/AU/stuff1/filename.txt' | cut -d'/' --output-delimiter '' -f1,3 | xxd
0000000: 456e 676c 6973 6800 4155 0a English.AU.
Here we simulated one of your files, passing the path through cut
. Notice the xxd
output which has a NULL character between English
and AU
.
Now lets run through and simulate the rest of the script.
$ l=$(echo 'English/Flavors/AU/stuff1/filename.txt' | cut -d'/' --output-delimiter '' -f1,3)
$ dest=/stuff2/${l}.utf8.txt
$ echo "$dest" | xxd
0000000: 2f73 7475 6666 322f 456e 676c 6973 6800 /stuff2/English.
0000010: 4155 2e75 7466 382e 7478 740a AU.utf8.txt.
Notice the NULL after the English
. The echo
displays it properly because echo
is a shell built-in. If we use an external echo
, it also exhibits the issue.
$ /bin/echo "$dest" | xxd
0000000: 2f73 7475 6666 322f 456e 676c 6973 680a /stuff2/English.
P.S. You really should be quoting too :-)
The solution is to not use cut
, use awk
instead.
$ echo 'English/Flavors/AU/stuff1/filename.txt' | awk -F/ '{ print $1$3 }' | xxd
0000000: 456e 676c 6973 6841 550a EnglishAU.
In string:
rtsp://user:pass@my.webserver.org:5554/my-media/media.amp?videocodec=h264
you have ?
in that string, so the shell will perform pathname expansion on that string, using pattern matching rules.
In bash
, if failglob
options was not set, which is default, then failed pattern will be left as-is:
$ echo does-not-exist?
does-not-exist?
While zsh
will report no pattern match error with nomatch
option set, which is default:
$ echo does-not-exist?
zsh: no matches found: does-not-exist?
You can make zsh
suppress the error and print the pattern:
$ setopt nonomatch
$ echo does-not-exist?
does-not-exist?
You can make bash
behave like zsh
with nomatch
option set, by turning on failglob
:
$ shopt -s failglob
$ echo does-not-exist?
bash: no match: does-not-exist?
More general, you can disable shell filename generation:
$ set -f
$ : "The command"
$ set +f
(or set -o noglob
, set +o noglob
)
or using one of shell quoting methods to make the shell treats ?
and other pattern matching special characters literally.
zsh
also provide the noglob
builtin, which disable filename generation in any words for the following simple command:
$ noglob echo *
*
Best Answer
Try doing
unsetopt equals
and try again. Or put quotes on the first equals character.If you use
[
for conditions, all arguments are evaluated normally and = at the start of a word is a useful zsh shorthand to get the location of a command. e.g.=ls
expands to/bin/ls
. Given==
zsh looks for an=
command and doesn't find one.Zsh in posix mode (sh emulation) will be a lot more compatible with bash. Many things are compatible and most learnt knowledge of bash is still useful but there are certain things to be aware of if you want to write portable scripts.
Unlike
[
which is a builtin,[[
is a reserved word understood by the parser and will give you fewer issues. However in this case you're using arithmetic mode so you simplify it:In fact the comparison to zero is even superfluous because, the default return status reflects whether the value is zero, though the sense is reversed: