There is a difference between:
PARMS+="... --exclude='.git'"
and
... --exclude='.git'
In the first, the single quotes are inside quotes themselves, so they are literally present in the substituted text given to rsync
as arguments. rsync
gets an argument whose value is --exclude='.git'
. In the second, the single quotes are interpreted by the shell at the time they're written, because they aren't inside quotes themselves, and rsync
gets to see --exclude=.git
.
In this case, you don't need the single quotes at all — .git
is a perfectly valid shell word on its own, with no special characters, so you can use it literally in the command.
Better for this kind of thing, though, is an array:
PARMS=(-rvu)
PARMS+=(--delete --exclude='.git')
rsync "${PARMS[@]}"
This builds up your command as separate words, with whatever quoting you want interpreted at the time you write the array line. "${PARMS[@]}"
expands to each entry in the array as a separate argument, even if the argument itself has special characters or spaces in it, so rsync
sees what you wrote as you meant it.
Given that the extglob
shell option is set in your interactive bash
session, the command
ls /bin/!(znew) | grep znew
would first run ls
with all the names in /bin
that is not znew
as arguments. If this list of names includes names of subdirectories, ls
would output the contents of those subdirectories (since -d
was not used with ls
). If one of these subdirectories contain the name znew
, then the grep
would match and output that name.
The contents of the unknown subdirectory of /bin
would be listed by ls
without prepending the names in it with any directory path. Hence, if a subdirectory contains the znew
name, it would be outputted as znew
rather than as /bin/some-dir/znew
.
When ls
is used as in
ls /bin/znew | grep znew
it would output the string /bin/znew
rather than znew
(if that pathname exists). It does this because it is outputting the specific pathname of a file given as a command line argument, not the contents of a directory given as an argument.
A situation like this could possibly have occurred if someone tried to create a symbolic link for /usr/bin
called /bin
while /bin
was already a symbolic link to /usr/bin
, or while /bin
was still a directory.
In an update to the question, it is revealed that /bin/X11
was a symbolic link to .
(the current directory). This means that znew
would have been accessible through the pathname /bin/X11/znew
.
So, in conclusion, what is happening is that the /bin/!(znew)
globbing pattern expanded to a list of pathnames, one of these being /bin/X11
(but not /bin/znew
). The ls
utility would then get all pathnames, including /bin/X11
as arguments. When ls
gets to list /bin/X11
, the contents of the /bin
directory is listed, due to /bin/X11
pointing there. The grep
utility then picks out znew
, which will part of the output of ls
.
As Stéphane Chazelas points out in comments, you would get the same effect if there is a file in /bin
on your Debian system that has a name containing a newline followed by the string znew
(possibly followed by another newline and maybe further characters).
Since the name is not exactly the string znew
, it would match the pattern /bin/!(znew)
and ls
would output this single name as
/bin/something
znew
maybe more
and grep
would extract znew
from that output.
Best Answer
In:
That's a simple command without command part, so the exit status is that of the command substitution instead. In:
or
There is a command part here (even if in some shells,
export
is midway between a command and a keyword), so its exit status is returned. Ifcmd
fails and produces no outputexport a=
will still be run and succeed, soexport
will return a 0 exit status.Here you'd want:
Or even better, get rid of that unreliable
set -e
and do proper error handling by hand:See e.g. BashFAQ 015: "Why doesn't set -e do what I expected?" for further examples of why and how
set -e
can produce unexpected results.