This is a general shell programming issue (not specific to Git at all, and generic across many POSIX-style shells including sh, bash, zsh, and more). The syntax for running a command is straightforward:
cmd arg1 arg2 ... argN
The output from this command goes to the standard output. Wrapping the command in dollar-sign-prefixed parentheses and assigning this to a variable with:
var=$(cmd arg1 arg2 ... argN)
tells the shell to capture the output (by redirecting the command's standard output somewhere, then reading this output). The command's standard error stream is not affected, but using:
var=$(cmd ... 2>&1)
will cause the parenthesized sub-command to send its own standard error output to the same place its own standard output is already going, which is to say, the outer shell that is collecting it up. So if you have a command that prints to both stdout and stderr, you can collect both outputs.
This is not what you wanted to do. You wanted to save the exit status of the command. To do that, as eftshift0 notes, you need to use the $? pseudo-variable:
cmd arg1 arg2 ... argN
status=$?
Having captured the exit status in an ordinary shell variable, you can now test it repeatedly, or—if your shell supports arithmetic—do arithmetic with the status, or whatever you like.
As phd notes in a comment, though, if you just want to inspect this status once, for zero-vs-non-zero, the way to do that is with the shell's if ...; then ...; else ...; fi construct:
if cmd arg1 arg2 ... argN
then
# stuff to do if the exit status was zero
else
# stuff to do if the exit status was nonzero
fi
Note that the then keyword must occur in the spot where a command would normally be found: that's why it's on a separate line. You can use an entire pipeline in an if construct, but only the last command's exit status matters:1
if cmd1 | cmd2 | cmd3; then
echo cmd3 exited zero
else
echo cmd3 exited nonzero
fi
Here then is on the same line, but we use a semicolon to make sure it appears where a standalone command would. The semicolon terminates the three-command pipeline.
Since you wrote:
wt_del=$(git worktree remove -f $DIR)
you captured the standard output of git worktree remove here, which is not really useful. You then have:
if [ $wt_del -eq 0]
which has a syntactic glitch: the ] should be separated from the zero by white-space. The [ program demands that its last argument be ]. The variant built into bash thus complains:
bash$ [ $wt_del -eq 0]
bash: [: missing `]'
This suggests you re-typed your actual script when convert it for posting purposes, since you didn't mention any such complaint. That's a recipe for getting useless answers. Note that since $wt_del might be empty, it should be quoted: fixing the above produces:
bash$ [ $wt_del -eq 0 ]
bash: [: -eq: unary operator expected
```none
Fixing *that* in turn produces the "correct" error:
```none
bash$ [ "$wt_del" -eq 0 ]
bash: [: : integer expression expected
but in general here you want if git ... constructs. Most Git commands endeavor to produce a useful exit status (which brings us back to git, finally)—but these details are sometimes not documented, so you have to test and then hope that the results you get for your version are general to all versions.
If Git calls something a plumbing command, it's meant for use in scripts, and should have a fully reliable way to be used from scripts, including both predictable, machine-parse-ready output and a predictable and useful exit status. Unfortunately git worktree is not a plumbing command, but it does have a --porcelain option for its list sub-command; --porcelain indicates that it's being used in "plumbing mode". So it should have a reliable exit status.
1Bash has a setting (pipefail) to adjust this behavior. Using them can be powerful, but decreases the portability of your shell script. Bash's PIPESTATUS array gives the most control of all but is annoying to use.
In the original Bourne shell, this last-command-status-matters trick was arranged by having the if ... then construct run the entire ... section in a sub-shell. If the sub-shell had just one command to run, it ran it with an execve system call. If it had a pipeline, it would spawn a sub-sub-shell to run each of the additional parts of the pipeline, then run the last command directly with an execve system call. This meant the last command in a pipeline was the parent process of an earlier command! Some badly-written commands were tripped up by this, sometimes. Bash can still do this, via the lastpipe setting.