Unable to save shopt globstar output to variable - bash

I'm trying to save the current status of shopt globstar to a variable so I can change it if needed then set it back as to not leave the user's environment altered.
I have tried storing the command output multiple ways such as var=$(command) and var=`command` but when I test with
echo $var it always reads the state as "off" even though it's not.
gstar=$( shopt globstar )
echo "$gstar"
I'm hoping to use echo to test the current state against manually running shopt globstar but they do not match.
This basic variable store is working fine with whoami command.

Are you running your script directly (./script.sh) or sourcing it (. script.sh or source script.sh)?
If you're running it directly it'll have its own environment and you don't have to worry about preserving the user's settings. Scripts get a copy of the user's environment and changes only affect the copy, not the original. Just set the option however you like at the top of your script.
#!/bin/bash
shopt -s globstar
foo **/bar
If it's being sourced it's a lot easier to just wrap the relevant parts of the script in a subshell so they run in an isolated environment.
(
shopt -s globstar
foo **/bar
)
baz
I suspect it's case 1 since you say it always starts out off.

To save the shopt value:
if shopt -q globstar
then
# the option is enabled
saved_globstar=-s
else
# the option is disabled
saved_globstar=-u
fi
Now you can change your globstar value. If you later want to restore it to the previous state, do a
shopt $saved_globstar globstar

Related

bash script yields a different result when sourced

Could you help me, why this script works when sourced (or even directly on console) and does not work on a script?
I have checked and in any case I'm using the same bash in /bin/ and always 4.4.19(1)-release (checked with $BASH_VERSION).
Moreover I tried removing shebang but nothing changes.
#!/bin/bash
fname=c8_m81l_55.fit
bname=${fname%%+(_)+([0-9]).fit}
echo $bname
GIving these results:
test:~$ ./test.sh
c8_m81l_55.fit
test:~$ . ./test.sh
c8_m81l
Bash does not recognize +(pattern) syntax unless extglobs are enabled, and they are disabled by default. Apparently your bash setup enables them in interactive sessions; that's why your script works only when sourced in an interactive shell.
To fix that, either enable extglobs within the script by this command:
shopt -s extglob
Or use an alternative that works irrespective of shell's interactiveness:
bname=$(sed 's/__*[0-9][0-9]*\.fit$//' <<< $fname)
# with GNU sed it'd look like:
bname=$(sed -E 's/_+[0-9]+\.fit$//' <<< $fname)

How to `command file.txt OR another_file.txt` in CLI (bash/zsh)

Is it possible to run a command with either file.txt as an argument or, if file.txt doesn't exist, with another_file.txt?
To make my request more realistic, I'd like to run VSCode with workspace file by default or, if workspace file doesn't exist, with . (current folder), something like:
code *.code-workspace OR .
If *.code-workspace exists, then the command should be equivalent to code *.code-workspace, otherwise the commend should be equivalent to code .
Is it possible in bash or zsh? (I'm using zsh + oh-my-zsh)
In bash you can use nullglob to expand wildcards (like *) to nothing if there is no match. Then put everything into an array and retrieve the first entry. Note that $array is the same as the first array entry ${array[0]}.
#! /bin/bash
shopt -s nullglob
files=(*.code-workspace .)
code "$files"
Above code starts code firstMatchOf.code-workspace if there is a file ending with .code-workspace and . if there is no such file.
For zsh you can do the same by replacing shopt -s nullglob with setopt null_glob.
Note that above approach only works with wildcards. files=(a b); code "$files" will call code a even if a does not exist. Here you could use the following function instead, which should work in every case:
#! /bin/sh
firstExisting() {
set -- "$#" . # ensure termination
while ! [ -e "$1" ]; do
shift
done
printf %s\\n "$1"
}
Usage example:
code "$(firstExisting *.code-workspace .)"
or just
code "$(firstExisting *.code-workspace)"
… as . is the default in case none of the arguments existed. shopt -s nullglob is not needed here.

Shell globbing negation doesn't work in bash

I'm currently confused why shell globbing in terminal works with negation but shows an error when running in bash.
Take the commands executed in the terminal below, which shows all js files within the ./HTML directory except for js files that ends with .bundle.js.
$ shopt -s globstar
$ ls ./HTML/**/!(*.bundle).js
The command above works perfectly, now let's put it in a bash file
list-js.sh
#!/usr/bin/env bash
shopt -s globstar
ls ./HTML/**/!(*.bundle).js
Executing it in a terminal:
$ bash list-js.sh
list-js.sh: line 4: syntax error near unexpected token `('
list-js.sh: line 4: `ls ./HTML/**/!(*.bundle).js'
As you can see, it shows a syntax error.
globstar only enables the ** pattern. The extglob option allows !(...). Somewhere in your interactive shell, that has already been enabled (perhaps in your .bashrc, perhaps you typed shopt -s extglob earlier). However, it needs to be enabled explicitly in your script, since such settings are not inherited from the shell that starts the script.
#!/usr/bin/env bash
shopt -s globstar extglob
ls ./HTML/**/!(*.bundle).js
(As an aside, ** without globstar does not cause a syntax error because it is treated simply as two adjacent *s, the second one being redundant.)

Unable to enable globstar in Bash 4

I put the following unsuccessfully to my .bashrc
shopt -s globstar
I am trying to test the command in action by
ls **/*.c
and by comparing it to
ls */*/*.c
How can you enable globstar in Bash 4?
Hmm. shopt -s globstar should work.
To debug, make sure you are running Bash 4:
$SHELL --version
Then check the setting of globstar:
shopt globstar
If it is unset, try setting it manually:
shopt -s globstar
Now see if that works. If it does, you might want to look into why your .bashrc isn't working. Did you remember to restart you shell after editing your .bashrc, or load it with . .bashrc?

Unable to use Checkjobs and Autocd in Bash 4

There are new options in Bash 4: checkjobs and autocd.
However, I did not find documentation for them at
man bash
I run unsuccessfully
{checkjobs,autocd}
I found the following in release notes
There is a new `checkjobs` option that causes the shell to check for and
report any running or stopped jobs at exit
and
There is a new `autocd` option that, when enabled, causes bash to attempt
to `cd` to a directory name that is supplied as the first word of a
simple command.
How can you use autocd and checkjobs?
autocd and checkjobs are not commands, but rather, they are options.
They can be set by using the shopt built-in.
Example:
shopt -s autocd
and
shopt -s checkjobs
or
shopt -s autocd checkjobs
to set both.

Resources