-
Notifications
You must be signed in to change notification settings - Fork 2.2k
not docs: mention that not does not affect $pipestatus
#11768
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
Makes the information easier to find. It was already mentioned in https://fishshell.com/docs/current/language.html#the-status-variable
|
|
||
| ``not`` negates the exit status of another command. If the exit status is zero, ``not`` returns 1. Otherwise, ``not`` returns 0. | ||
|
|
||
| In order to make it possible for the original status of the command to be recovered, ``not`` does not affect the ``$pipestatus`` environment variable. It only affects ``$status``. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure if "make it possible" is true.
One can always do
foo | bar | baz
set -l s $pipestatus
if ! [ $s[-1] -eq 0 ]
...
end
so the other place where it's documented is probably misleading,
we should fix that.
I think we can state the behavior without justification
the way it currently works is reasonably consistent --
by definition, pipestatus is the list of exit codes of each process in a job,
and builtin not negates the status of the whole job,
and doesn't care for processes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
so the other place where it's documented is probably misleading,
we should fix that.
For reference, it says
One difference is that not applies to
$status, but not$pipestatus, because it loses information.
the way it currently works is reasonably consistent --
by definition, pipestatus is the list of exit codes of each process in a job,
and builtin not negates the status of the whole job,
and doesn't care for processes.
I find the $pipestatus behavior extremely confusing when doing
foo | not bar | baz
Since (Update: actually, it binds weaker, so some of this comment is wrong),not binds stronger than pipe
not foo | bar | baz
is also confusing.
Perhaps this is never useful; if so it might make sense to prohibit it. If not was only allowed at the end of a pipe (or only in the beginning, but with not foo | bar meaning not { foo | bar }), I think I could see how that makes some sense.
Your workaround disproving the "loses information" argument is interesting and valid, but seemingly not obvious. I didn't think of it myself, and I don't think the person who implemented this was thinking along those lines did either (or not would bind weaker than pipe. Or perhaps this is difficult; not sure it would work sanely with the precedence of && and || behaving intuitively around pipes).
I don't really have a conclusion; the current situation is confusing enough that I'd prefer providing the explanation of "loses information" to not saying anything unless it's fixed. But I'm not sure; I could delete the justification.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, I was confused by a buggy prompt I made. I was wrong in my previous comment; { not true | false } && echo blah does print blah. So, not does binds weaker than |.
The default prompt shows it correctly.
(Posted after #11768 (comment))
|
On Sat, Sep 13, 2025 at 09:54:30PM -0700, Ilya Grigoriev wrote:
For reference, it says
> > One difference is that
> > [not](https://fishshell.com/docs/current/cmds/not.html)
> > applies to `$status`, but not `$pipestatus`, because it loses
> > information.
rather than the vague "because it loses information"
maybe we should emphasize that "not" applies to the whole pipeline,
and the rest follows.
diff --git a/doc_src/language.rst b/doc_src/language.rst
index 1acff6e065..a3792330b3 100644
--- a/doc_src/language.rst
+++ b/doc_src/language.rst
@@ -1778,9 +1778,12 @@
If a process exits through a signal, the exit status will be 128 plus the number of the signal.
-The status can be negated with :doc:`not <cmds/not>` (or ``!``), which is useful in a :ref:`condition <syntax-conditional>`. This turns a status of 0 into 1 and any non-zero status into 0.
+The status can be negated with :doc:`not <cmds/not>` (or ``!``), which is useful in a :ref:`condition <syntax-conditional>`.
+This turns a status of 0 into 1 and any non-zero status into 0.
-There is also ``$pipestatus``, which is a list of all ``status`` values of processes in a pipe. One difference is that :doc:`not <cmds/not>` applies to ``$status``, but not ``$pipestatus``, because it loses information.
+The :doc:`not <cmds/not>` command can be applied to a pipeline by prepending it to any command within the pipeline.
+As implied above, it affects the pipeline's `:envvar:`$status`` value.
+There is also ``$pipestatus``, which is a list of all exit status values of processes in a pipe without the :doc:`not <cmds/not>` commands.
For example::
One difference is that
[not](https://fishshell.com/docs/current/cmds/not.html)
applies to `$status`, but not `$pipestatus`, because it loses
information.
The "loses information" argument also not very practical
because even if you find a use case for $pipestatus,
the chances are very high that you're not gonna use "not" on the same command.
-----
> the way it currently works is reasonably consistent --
by definition, pipestatus is the list of exit codes of each process in a job,
and builtin not negates the status of the whole job,
and doesn't care for processes.
I find the `$pipestatus` behavior extremely confusing when doing
```
foo | not bar | baz
```
right, the source of confusion is that we can add "not" in the middle
of the pipeline even though it applies to the whole pipeline.
This is not allowed in other shells.
$ true | not true
~ [0|0] 1>
$pipestatus[2] is "unexpected" here.
we probably don't want to touch "not" at the beginning (also at the
end) of the pipeline, but I don't see why anyone would put it in the
middle like "foo | not bar | baz". So we can make that a syntax error.
Since `not` binds stronger than pipe,
```
not foo | bar | baz
```
is also confusing.
Perhaps this is never useful;
We do use "! foo | grep" in POSIX shell scripts:
! git ls-files --others --exclude-standard | grep .
if so it might make sense to prohibit
it. If `not` was only allowed at the end of a pipe (or only in the
beginning, but with `not foo | bar` meaning `not { foo | bar }`),
It's hard to improve things; hypothetically we could
1. allow the weird constructs that are already in POSIX (e.g. "! foo | bar", probably the same for "not")
2. reject other weird constructs like "foo | not bar"
3. recommend a more obvious notation ("not { foo | bar }")
but we likely can't do 2, due to compatibility
I guess the existing confusion is no big deal since I can't think of good reasons to ever use $pipestatus, except for our only use, in the prompt.
|
|
`{ not true | false } && echo blah`
that command is cursed!
(I'm wondering if we should ban this)
|
|
The next things I tried were and also :) In my mind, it's The fact that |
|
```
{ not not true | not false } && echo blah # prints blah
{ not true | not false | not true } && echo blah # does not print blah
```
:)
In my mind, it's `a | not b` that's the most cursed.
yeah, though that one is probably used in the wild (which doesn't help correct this misunderstanding at all..)
For a moment, I thought `a | not b` is the same as `not { a | b }`,
it is (only less cursed)
but the last example above seems to disprove it.
it's not because of how it's implemented.
Each "not" applies to the whole job, and two nots cancel each other out.
So all of these are fully equivalent.
not true | not false | not true
not true | false | true
true | not false | true
true | false | not true
I don't know why more than one `not` is allowed.
so we should at least try to
1. ban cursed usage of `not` in the middle of a pipeline
2. ban multiple `not` commands on the same pipeline at least if they are on different processes
|
|
The fact that `not a | b` inverts the whole command I could reason about, and document.
Sounds good.
If we don't change any behavior, we could also mention the current
differences to other shells:
diff --git a/doc_src/cmds/not.rst b/doc_src/cmds/not.rst
index f609d708c5..ec07cc6bd7 100644
--- a/doc_src/cmds/not.rst
+++ b/doc_src/cmds/not.rst
@@ -17,7 +17,7 @@
``not`` negates the exit status of another command. If the exit status is zero, ``not`` returns 1. Otherwise, ``not`` returns 0.
…-Some other shells only support the ``!`` alias.
+Some other shells only support the ``!`` alias, and only at the beginning of a pipeline.
The **-h** or **--help** option displays help about using this command.
|
Makes the information easier to find. It was already mentioned in https://fishshell.com/docs/current/language.html#the-status-variable