On mac OSX, I have this script:
#!/usr/local/bin/bash
echo -e "\e[41mError: Some error.\e[0m"
When I just run the echo -e ... in a console, it prints the colored text "Error: Some error."
When executed as a script sh myscript.sh, it litterally prints the flag and the escape characters: -e "\e[41mError: Some error.\e[0m".
When I add the script location to ~/.bash_profile and execute it as myscript.sh, it does work. But I need to be able execute it without adding it to my bash profile.
Edit: using printf works: printf "\e[41mError: Some error.\e[0m\n".
when you run the shell with sh it runs in posix compatibility mode (i.e. as the bourne shell does)
bash is a successor to this shell, one of the features it adds is the -e switch to echo
in posix shell you don't need the -e, the escapes will be evaluated anyway
in bash you do, so if you want to run bash do so explicitly
Related
I use zsh as the default shell, I run echo $0 and get -zsh on terminal, but the following code can't detect default interactive shell
#!/usr/bin/env bash
if [ -n "$ZSH_VERSION" ]; then
echo "zsh"
elif [ -n "$BASH_VERSION" ]; then
echo "bash"
else
echo "others"
fi
The detect result is always bash, why? Thank you.
Your code works for detecting the current shell. But scripts run in their own shell, independently from the interactive shell. Your script file always runs in bash due to its shebang. Without the shebang, the calling shell decides how to run the script (if at all).
Detecting the parent shell
To detect the shell that called your script, try
#!/usr/bin/env bash
ps -p $PPID -o comm=
When you run an interactive zsh and execute this file you should get zsh as output.
Detecting the default shell
Your question's title is about detecting the default interactive shell. To do so, you cannot check any processes, because even if your default shell is X you can always use Y. Instead, look at the file where the default is stored:
grep "^$USER:" /etc/passwd | cut -d: -f7
The standard way to do this is with the SHELL environment variable:
echo "Your default shell is $SHELL"
This is defined in the POSIX standard, section 8.3, "Other Environment Variables":
SHELL
This variable shall represent a pathname of the user's preferred command language interpreter. If this interpreter does not conform to the Shell Command Language in XCU Shell Command Language, utilities may behave differently from those described in POSIX.1-2017.
How can I definitively determine if the currently running shell is bash or zsh?
(being able to disambiguate between additional shells is a bonus, but only bash & zsh are 100% necessary)
I've seen a few ways to supposedly do this, but they all have problems (see below).
The best I can think of is to run some syntax that will work on one and not the other, and to then check the errors / outputs to see which shell is running. If this is the best solution, what command would be best for this test?
The simplest solution would be if every shell included a read-only parameter of the same name that identified the shell. If this exists, however, I haven't heard of it.
Non-definitive ways to determine the currently running shell:
# default shell, not current shell
basename "${SHELL}"
# current script rather than current shell
basename "${0}"
# BASH_VERSINFO could be defined in any shell, including zsh
if [ -z "${BASH_VERSINFO+x}" ]; then
echo 'zsh'
else
echo 'bash'
fi
# executable could have been renamed; ps isn't a builtin
shell_name="$(ps -o comm= -p $$)"
echo "${shell_name##*[[:cntrl:][:punct:][:space:]]}"
# scripts can be sourced / run by any shell regardless of shebang
# shebang parsing
On $ prompt, run:
echo $0
but you can't use $0 within a script, as $0 will become the script's name itself.
To find the current shell (let's say BASH) if shebang / magic number executable was #!/bin/bash within a script:
#!/bin/bash
echo "Script is: $0 running using $$ PID"
echo "Current shell used within the script is: `readlink /proc/$$/exe`"
script_shell="$(readlink /proc/$$/exe | sed "s/.*\///")"
echo -e "\nSHELL is = ${script_shell}\n"
if [[ "${script_shell}" == "bash" ]]
then
echo -e "\nI'm BASH\n"
fi
Outputs:
Script is: /tmp/2.sh running using 9808 PID
Current shell used within the script is: /usr/bin/bash
SHELL is = bash
I'm BASH
This will work, if shebang was: #!/bin/zsh (as well).
Then, you'll get the output for SHELL:
SHELL is = zsh
While there is no 100% foolproof way to achieve it, it might help to do a
echo $BASH_VERSION
echo $ZSH_VERSION
Both are shell variables (not environment variables), which are set by the respective shell. In the respective other shell, they are empty.
Of course, if someone on purpose creates a variable of this name, or exports such a variable and then creates a subshell of the different kind, i.e.
# We are in bash here
export BASH_VERSION
zsh # the subshell will see BASH_VERSION even though it is zsh
this approach will fail; but I think if someone is really doing such a thing, he wants to sabotage your code on purpose.
This should work for most Linux systems:
cat /proc/$$/comm
Quick and easy.
Working from comments by #ruakh & #oguzismail, I think I have a solution.
\shopt -u lastpipe 2> /dev/null
shell_name='bash'; : | shell_name='zsh'
I have a bash script that opens up a shell called salome shell and it should execute a command called as_run in that shell. The thing is that after entering the salome shell it doesn't execute the command until I exit the salome shell. This is the code that i got:
#!/bin/bash
cd /opt/salome/appli_V2018.0.1_public
./salome shell
eval "as_run /home/students/gbroilo/Desktop/Script/Template_1_2/exportSalome"
What should I do in order to execute the command in the salome shell?
Might be this is what you want:
# call salome shell with commands in a specified script file
cd /opt/salome/appli_V2018.0.1_public
./salome shell <"/home/students/gbroilo/Desktop/Script/Template_1_2/exportSalome"
Or might be this is what you want:
# pipe a command as_run... to salome shell
cd /opt/salome/appli_V2018.0.1_public
echo "as_run /home/students/gbroilo/Desktop/Script/Template_1_2/exportSalome" | ./salome shell
Anyway, you have to read the salome guide about how salome shell call it's script.
Most shells implement a way to pass the commands as parameters, e.g.
dash -c 'x=1 ; echo $x'
You'll need to consult your shell's manual to see if it's possible.
You can also try sending the commands to the standard input of the shell:
echo 'set x = 1 ; echo $x' | tcsh
Using a HERE doc might be a bit more readable in case of complex commands:
tcsh << 'TCSH'
set x = 1
echo $x
TCSH
I am attempting to store the result of an echo command as a variable to be used in a shell script. Debian 4.19.0-6-amd64
The command works in terminal: echo $HOSTNAME returns debian-base, the correct hostname.
I attempt to run it in a shell script, such as:
#!/usr/bin/bash
CURRENT_HOSTNAME=`echo $HOSTNAME`
echo $CURRENT_HOSTNAME
I have tried expansion:
CURRENT_HOSTNAME=$(echo $HOSTNAME)
And just to cover some more bases, I tried things like:
CURRENT_HOSTNAME=$HOSTNAME
# or
CURRENT_HOSTNAME="$HOSTNAME"
# also, in case a problem with reserved names:
test=$HOSTNAME
test="$HOSTNAME"
Works great in the terminal! Output is as follows:
root#debian-base:/scripts# echo $HOSTNAME
debian-base
root#debian-base:/scripts# TEST_HOSTNAME=$HOSTNAME
root#debian-base:/scripts# echo $TEST_HOSTNAME
debian-base
root#debian-base:/scripts# TEST_TWO_HOSTNAME=$(echo $HOSTNAME)
root#debian-base:/scripts# echo $TEST_TWO_HOSTNAME
debian-base
As soon as I run the script (as above):
root#debian-base:/scripts# sh test.sh
root#debian-base:/scripts#
What am I doing wrong?
You are using bash as your terminal. Bash has the variable $HOSTNAME set. You run your script with sh. sh does not have a $HOSTNAME.
Options:
bash test.sh
Or run it as a program:
chmod +x test.sh
./test.sh
But I think you need to change your first line to:
#!/bin/bash
As I don't think bash is installed in /usr/bin in most cases. But you need to try. To figure out where bash is installed use which bash
Another option is to use the hostname binary:
CURRENT_HOSTNAME=$(hostname)
echo $CURRENT_HOSTNAME
Which works in both bash and sh.
You can start sh by just running sh. You will see it has a bash-like terminal. You can try to do echo $HOSTNAME. It will not show, because it's not there. You can use set to see all the variables that are there (as sh does not have tab completion it's harder to figure out).
I am using this:
$ uname -a
CYGWIN_NT-6.1 bassoon 1.7.15(0.260/5/3) 2012-05-09 10:25 i686 Cygwin
$ bash --version
GNU bash, version 4.1.10(4)-release (i686-pc-cygwin)
$ cat myexpr.sh
#!/bin/sh
echo "In myexpr, Before expr"
ac_optarg=`expr x--with-gnu-as : 'x[^=]*=\(.*\)'`
echo "ac_optarg=$ac_optarg"
echo "In myexpr, After expr"
$ cat myexpr2.sh
#!/bin/sh
set -e
echo "In myexpr, Before expr"
ac_optarg=`expr x--with-gnu-as : 'x[^=]*=\(.*\)'`
echo "ac_optarg=$ac_optarg"
echo "In myexpr, After expr"
The only difference between the two scripts is that myexpr2.sh uses "set -e"
$ echo $$
2880
$ ./myexpr.sh
In myexpr, Before expr
ac_optarg=
In myexpr, After expr
$ ./myexpr2.sh
In myexpr, Before expr
Expected behavior, so far.
If I do this in the parent shell (PID 2880, above):
$ set -e
$ ./myexpr.sh
The parent shell exits! That is pID 2880 above where I did the "set -e"
This is not the behavior on Linux or cygwin 1.5.12. Is this a bug in cygwin or BASH on cygwin?
This is not a bug, it's a feature of the Bash environment. This happens when you don't have the Bash shell environment variable execfail set, and/or the Shell environment variable errexit.
execfail - (is a BASHOPTS)
If 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.
errexit - (is a SHELLOPTS)
Exit immediately if a pipeline (see Pipelines), which may consist of a
single simple command (see Simple Commands), a subshell command enclosed
in parentheses (see Command Grouping), or one of the commands executed as
part of a command list enclosed by braces (see Command Grouping) returns a
non-zero status. The shell does not exit if the command that fails is part
of the command list immediately following a while or until keyword, part
of the test in an if statement, part of any command executed in a && or ||
list except the command following the final && or ||, any command in a
pipeline but the last, or if the command’s return status is being inverted
with !. A trap on ERR, if set, is executed before the shell exits.
This option applies to the shell environment and each subshell environment
separately (see Command Execution Environment), and may cause subshells to
exit before executing all the commands in the subshell.
Different Linux versions have different defaults for these.
You can check which are enabled with:
echo "SHELLOPTS=$SHELLOPTS"
echo "BASHOPTS=$BASHOPTS"
and you can see all of them using:
set -o && echo -e "\n" && shopt -p
So, you need to enable yours with:
shopt -s execfail
If that doesn't work, you may also have to unset (off) the errexit of $SHELLOPTS with:
set -o errexit
For further info, see: The GNU Bash Manual!
PS. "set" is using reverse logic so if you wanna use the 'e' flag you have to use a "+": set +e