Sunday, August 14, 2016

The Bash "Shopt builtin"

The shopt builtin

  • This builtin allows you to change additional shell optional behavior.

  • Syntax:
             shopt [-pqsu] [-o] [optname ...]

-s
Enable (set) each optname.

-u
Disable (unset) each optname.

-q
Suppresses normal output; the return status indicates whether the optname is set or unset. If multiple optname arguments are given
with -q, the return status is zero if all optnames are enabled; nonzero otherwise.

-o
Restricts the values of optname to be those defined for the -o option to the set builtin. This means we can change the options provided by set builtin, but using shopt also.

-p
With no options, or with the -p option, a list of all settable options is displayed, with an indication of whether or not each is set.
The -p option causes output to be displayed in a form that may be reused as input.

  • If either -s or -u is used with no optname arguments, shopt shows only those options which are set or unset, respectively.

  • The return status when listing options is zero if all optnames are enabled, nonzero otherwise. When setting or unsetting options, the return status is zero unless an optname is not a valid shell option

Example 1 :
The shopt builtin without any other options displays all the options set or unset.
[sukul1@lldd010 ~]$ shopt
autocd          off
cdable_vars     off
cdspell         off
checkhash       off
checkjobs       off
checkwinsize    on
cmdhist         on
compat31        off
compat32        off
compat40        off
dirspell        off
dotglob         off

Example 2:
The shopt builtin with a option name gives us whether its on or off.
[sukul1@lldd010 ~]$ shopt expand_aliases
expand_aliases  on
[sukul1@lldd010 ~]$

Example 3:
The shopt with a -p option gives in a way that can be reused.
[sukul1@lldd010 ~]$ shopt -p
shopt -u autocd
shopt -u cdable_vars
shopt -u cdspell
shopt -u checkhash
shopt -u checkjobs
shopt -s checkwinsize
shopt -s cmdhist
shopt -u compat31
shopt -u compat32
shopt -u compat40

[sukul1@lldd010 ~]$ shopt -p expand_aliases
shopt -s expand_aliases

Example 4:
With either -s or -u option, without the option name shows the set and unset options.
[sukul1@lldd010 ~]$ shopt -s
checkwinsize    on
cmdhist         on
expand_aliases  on
extquote        on
force_fignore   on
hostcomplete    on
interactive_comments    on
login_shell     on
progcomp        on
promptvars      on
sourcepath      on

  • Following are the important options:

autocd
If set, a command name that is the name of a directory is executed as if it were the argument to the cd command. This option is only used by interactive shells.

[sukul1@lldd011 ~]$ shopt autocd
autocd          off
[sukul1@lldd011 ~]$ shopt -p autocd
shopt -u autocd
[sukul1@lldd011 ~]$ mkdir dir1
[sukul1@lldd011 ~]$ shopt -s autocd
[sukul1@lldd011 ~]$ dir1
cd dir1
[sukul1@lldd011 dir1]$

cdable_vars
If this is set, an argument to the cd builtin command that is not a directory is assumed to be the name of a variable whose value is the directory to change to.

[sukul1@lldd011 ~]$ shopt cdable_vars
cdable_vars     off
[sukul1@lldd011 ~]$ shopt -p cdable_vars
shopt -u cdable_vars
[sukul1@lldd011 ~]$ shopt -s cdable_vars
[sukul1@lldd011 ~]$ VAR1=dir1
[sukul1@lldd011 ~]$ cd VAR1
dir1
[sukul1@lldd011 dir1]$ pwd
/home/sukul1/dir1
[sukul1@lldd011 dir1]$ cd ..
[sukul1@lldd011 ~]$ shopt -u cdable_vars
[sukul1@lldd011 ~]$ cd VAR1
-bash: cd: VAR1: No such file or directory
[sukul1@lldd011 ~]$

cdspell
If set, minor errors in the spelling of a directory component in a cd command will be corrected. The errors checked for are transposed characters, a missing character, and a character too many. If a correction is found, the corrected path is printed, and the command proceeds. This option is only used by interactive shells.

[sukul1@lldd011 ~]$ shopt -p cdspell
shopt -u cdspell
[sukul1@lldd011 ~]$ shopt -s cdspell
[sukul1@lldd011 ~]$ cd di1r
dir1
[sukul1@lldd011 dir1]$ pwd
/home/sukul1/dir1
[sukul1@lldd011 dir1]$

checkhash
If this is set, Bash checks that a command found in the hash table exists before trying to execute it.
 If a hashed command no longer exists, a normal path search is performed.

This option makes sure that command found in hash actually exists.

checkjobs
If set, Bash lists the status of any stopped and running jobs before exiting an interactive shell.
If any jobs are running, this causes the exit to be deferred until a second exit is attempted without an intervening command
The shell always postpones exiting if any jobs are stopped.

To put in simple words normally if jobs are running in background and we exit the main shell we wont get  any warnings.
We would get warnings the 1st time we exit if
- jobs are in stopped state
- or jobs are running and checkjobs option is enabled.
If there are any Background stopped jobs, shell will always ask twice befor exiting.
if we exit the 2nd time and jobs are in stopped state, they get cancelled.

cmdhist
If set, Bash attempts to save all lines of a multiple-line command in the same history entry. This allows easy re-editing of multi-line commands

[sukul1@lldd011 dir1]$ shopt cmdhist
cmdhist         on
[sukul1@lldd011 dir1]$
[sukul1@lldd011 dir1]$ if [[ $LOG_NAME == "sukul1" ]];then
> echo "Hola its sukul"
> fi

history:
  330  08/12/16 03:37 if [[ $LOG_NAME == "sukul1" ]];then echo "Hola its sukul"; fi
dotglob
If set, Bash includes filenames beginning with a ‘.’ in the results of filename expansion.

By Default ? and * dont match the preceding . of hidden files. (Very Imp: File globbing does not work on hidden files by default)
We need to explicitly specify .
ex: ls .hiddenfile.
But if we use dotglob option, then ? and * would match preceding period also.

[sukul1@lldd010 ~]$ shopt dotglob
dotglob         off
[sukul1@lldd010 ~]$ shopt -p dotglob
shopt -u dotglob
[sukul1@lldd010 ~]$ touch .hiddenfile
[sukul1@lldd010 ~]$ ls *hiddenfile
ls: cannot access *hiddenfile: No such file or directory
[sukul1@lldd010 ~]$ shopt -s dotglob
[sukul1@lldd010 ~]$ shopt -p dotglob
shopt -s dotglob
[sukul1@lldd010 ~]$ ls *hiddenfile
.hiddenfile
[sukul1@lldd010 ~]$

Note that after enabling dotglob, * and . Matches hidden files too.
execfail
If this is set, a non-interactive shell will not exit if it cannot execute the file specified as an argument to the exec builtin command. An interactive shell does not exit if exec fails.

expand_aliases
If set, aliases are expanded as described below under Aliases.
This option is enabled by default for interactive shells and disabled for noninteractive shells.

[sukul1@lldd010 ~]$ echo $BASHOPTS
checkwinsize:cmdhist:dotglob:expand_aliases:extquote:force_fignore:hostcomplete:interactive_comments:login_shell:progcomp:promptvars:sourcepath
[sukul1@lldd010 ~]$ shopt expand_aliases
expand_aliases  on
[sukul1@lldd010 ~]$

extglob
If set, the extended pattern matching features described above are enabled.
failglob
If set, patterns which fail to match filenames during filename expansion result in an expansion error.

Normally if a pattern does not match any filename, the command using the pattern will still be executed.
Ex: ls *xyz*
if no file matches *xyz* the command  ls will still be executed and ls will give us an error message.
But if we want globbing to fail and command ls not to be executed, then we can use failglob.
This could be useful if we provide  multiple patterns to a command and we want atleast one file to match each of the patterns.
If any of the patterns does not match a file, then the command should not be executed.


globstar
If set, the pattern ‘**’ used in a filename expansion context will match all files and zero or more directories and subdirectories. If
the pattern is followed by a ‘/’, only directories and subdirectories match.

histappend
If set, the history list is appended to the file named by the value of the HISTFILE variable when the shell exits, rather than overwriting the file.
Normally HISTFILE is overwritten everytime. With this option enabled, history is appended to the file.
huponexit
    If set, Bash will send SIGHUP to all jobs when an interactive login shell exits. Default is off.


    1. All child processes, backgrounded or not of a shell opened over an ssh connection are killed with SIGHUP when the ssh connection is (gracefully) closed only if the huponexit option is set: run shopt huponexit to see if this is true.
    2. If huponexit is true, then you can use nohup or disown to dissociate the process from the shell so it does not get killed when you exit. Or, run things with screen.
    3. If huponexit is false, which is the default on at least some linuxes these days, then backgrounded jobs will not be killed on normal logout.
    4. But even if huponexit is false, then if the ssh connection gets killed, or drops (different than normal logout), then backgrounded processes will still get killed. This can be avoided by disownor nohup as in (2).

    [sukul1@cldi016 ~]$ shopt | grep hup
    huponexit       off
    [sukul1@cldi016 ~]$ cat sukul_sleep
    sleep 3000
    [sukul1@cldi016 ~]$ bash sukul_sleep &
    [1] 21348
    [sukul1@cldi016 ~]$ jobs
    [1]+  Running                 bash sukul_sleep &
    [sukul1@cldi016 ~]$ ps -ef | grep sleep
    sukul1   21348 26657  0 08:41 pts/4    00:00:00 bash sukul_sleep
    sukul1   21349 21348  0 08:41 pts/4    00:00:00 sleep 3000
    sukul1   21931 26657  0 08:42 pts/4    00:00:00 grep sleep
    [sukul1@cldi016 ~]$

    Now we exit and logon again.
    After exit and logon:
    [sukul1@cldi016 ~]$ jobs
    [sukul1@cldi016 ~]$ ps -ef | grep sleep
    sukul1   21348     1  0 08:41 ?        00:00:00 bash sukul_sleep
    sukul1   21349 21348  0 08:41 ?        00:00:00 sleep 3000
    sukul1   22765 22671  0 08:42 pts/3    00:00:00 grep sleep
    [sukul1@cldi016 ~]$

    Note that jobs can no more be seen using jobs.
    But the job is still running and parent process id has changed to 1(init), but its still running.
    So we can see that background processes were not killed upon normal exit, just their ownership changes.
    This behaviour can change if huponexit in turned on. This will cause shell to send SIGHUP even on normal exit.
    In case of abnormal exits, the SIGHUP will be sent to background processes even if huponexit  is not set.
    This is where nohup and disown come into picture. They actually protext from abnormal exits.

interactive_comments
Allow a word beginning with ‘#’ to cause that word and all remaining characters on that line to be ignored in an interactive shell.
This option is enabled by default.

[sukul1@lldd010 ~]$ echo $BASHOPTS
checkwinsize:cmdhist:dotglob:expand_aliases:extquote:force_fignore:hostcomplete:interactive_comments:login_shell:progcomp:promptvars:sourcepath

lithist
If enabled, and the cmdhist option is enabled, multi-line commands are saved to the history with embedded newlines rather than using semicolon separators where possible.
login_shell
The shell sets this option if it is started as a login shell.
The value may not be changed.

[sukul1@lldd010 ~]$ echo $BASHOPTS
checkwinsize:cmdhist:dotglob:expand_aliases:extquote:force_fignore:hostcomplete:interactive_comments:login_shell:progcomp:promptvars:sourcepath

nocaseglob
If set, Bash matches filenames in a case-insensitive fashion when performing filename expansion.

By default pattern matching is  case sensitive. By using nocaseglob we can make pattern matching
case insensitive.
Ex: when enabled ls [F]* indicates files start with both F and f.

nocaseglob does not impact ? or * because they match any character. So it is useful in character classes.Case sensitivity is only restricted to pattern and not any other text.

[sukul1@lldd010 ~]$ touch Script1.bash
[sukul1@lldd010 ~]$ ls *cri*
script1.bash  Script1.bash
[sukul1@lldd010 ~]$ shopt nocaseglob
nocaseglob      off
[sukul1@lldd010 ~]$ shopt -s nocaseglob
[sukul1@lldd010 ~]$ ls scri*
script1.bash  Script1.bash
[sukul1@lldd010 ~]$ shopt -u nocaseglob
[sukul1@lldd010 ~]$ ls scri*
script1.bash
[sukul1@lldd010 ~]$

nocasematch
If set, Bash matches patterns in a case-insensitive fashion when performing matching while executing case or [[ conditional commands.

[sukul1@lldd010 ~]$ shopt -p nocasematch
shopt -u nocasematch
[sukul1@lldd010 ~]$ VAR1=SUKUL
[sukul1@lldd010 ~]$ if [[ $VAR1 == "sukul" ]];then
> echo "thats it"
> fi
[sukul1@lldd010 ~]$ shopt -s nocasematch
[sukul1@lldd010 ~]$ if [[ $VAR1 == "sukul" ]];then
> echo "thats it"
> fi
thats it
[sukul1@lldd010 ~]$

After enabling nocasematch "sukul" and "SUKUL" are both the same

nullglob
If set, Bash allows filename patterns which match no files to expand to a null string, rather than themselves.

If nullglob is disabled(default) and the globbing pattern (ex: xyz*) does not match any filename, then the pattern as is (xyz*) is passed to the command(one  that is using this pattern). However if nullglob is set, the shell will remove the pattern completely and run the command.

[sukul1@lldd016 ~]$ ls *abc
ls: cannot access *abc: No such file or directory
[sukul1@lldd016 ~]$ shopt -p nullglob
shopt -u nullglob
[sukul1@lldd016 ~]$ shopt -s nullglob
[sukul1@lldd016 ~]$ ls *abc
17:27:02  bin           code_onetime      ctl  f1.bash        Jul        runtime     

Here in the 1st example *abc didnt match anything. So the pattern *1bc itself was sent to ls and thats why ls output said "no such file of directory). After we enable nullglob *abc gets replaced by null string and only ls runs without any argument, making it show the contents of existing directory.

This could beuseful when matching multiple patterns and we dont want to see an error message if any of those dont match
[sukul1@lldd016 ~]$ ls *abc f1.bash
f1.bash
[sukul1@lldd016 ~]$

Note that this works fine when we are globbing (.i.e using * or .). If you give a filename and if the file does not exist we will get an error of No such file or directory.

[sukul1@lldd016 ~]$ ls abc f1.bash
ls: cannot access abc: No such file or directory
f1.bash

promptvars
If set, prompt strings undergo parameter expansion, command substitution, arithmetic expansion, and quote removal after being expanded.
This option is enabled by default.

shift_verbose
If this is set, the shift builtin prints an error message when the shift count exceeds the number of positional parameters

[sukul1@lldd016 ~]$ shopt -u shift_verbose
[sukul1@lldd016 ~]$ shift 4
[sukul1@lldd016 ~]$


[sukul1@lldd016 ~]$ shopt -s shift_verbose
[sukul1@lldd016 ~]$ echo $BASHOPTS
checkwinsize:cmdhist:expand_aliases:extquote:force_fignore:hostcomplete:interactive_comments:login_shell:nullglob:progcomp:promptvars:shift_verbose:sourcepath
[sukul1@lldd016 ~]$ shift 4
-bash: shift: 4: shift count out of range

sourcepath
If set, the source builtin uses the value of PATH to find the directory containing the file supplied as an argument.
This option is enabled by default.
xpg_echo
If set, the echo builtin expands backslash-escape sequences by default.

[sukul1@lldd016 ~]$ echo "Sukul\tmahadik"
Sukul\tmahadik
[sukul1@lldd016 ~]$ shopt -s xpg_echo
[sukul1@lldd016 ~]$ echo "Sukul\tmahadik"
Sukul   mahadik
[sukul1@lldd016 ~]$

Note that without xpg_echo, the backslash escape sequences are not interpreted.
With this we wont need to use the -e option with echo to make it recognize escape sequences.

  • Note that these options can also be used upon invocation of the shell.
            Example: bash -O sourcepath script1.bash

  • We can see what all options are set by using variable $BASHOPTS

  • We can set also use shopt to set the options available with set.
             Ex: shopt -s -o noclobber

No comments:

Post a Comment