This should be very simple.
I recently noticed that when I type 'bash' into Terminal on Mac it shows this:
Jays-MacBook-Pro: ~ $ bash
bash: parse_git_branch: command not found
When before it didn't. Can someone explain why and how to resolve.
It is likely that you configured BASH to run parse_git_branch and print the result as part of PS1 (or alike). You can check this by: "echo $PS1" and "echo $PROMPT_COMMAND".
However, parse_git_branch is not a builtin function of bash. Below is how I configured my PS1. You may want to copy my git_branch_4_ps1 as your parse_git_branch
PS1='\n' # begin with a newline
PS1=$PS1'\[\e[38;5;101m\]\! \t ' # time and command history number
PS1=$PS1'\[\e[38;5;106m\]\u#\h ' # user#host
PS1=$PS1'\[\e[7;35m\]${MY_WARN}\[\e[0m\] ' # warning message if there is any
PS1=$PS1'\[\e[38;5;10m\]${MY_EXTRA} ' # extra info if there is any
PS1=$PS1'\[\e[0;36m\]$(git_branch_4_ps1) ' # git_branch_4_ps1 defined below
PS1=$PS1'\[\e[38;5;33m\]\w' # working directory
PS1=$PS1'\n\[\e[32m\]\$ ' # "$"/"#" sign on a new line
PS1=$PS1'\[\e[0m\]' # restore to default color
function git_branch_4_ps1 { # get git branch of pwd
local branch="$(git branch 2>/dev/null | grep "\*" | colrm 1 2)"
if [ -n "$branch" ]; then
echo "(git: $branch)"
fi
}
If your parse_git_branch is defined in ~/.bash_profile, it will not be loaded when you open a non-login shell (e.g. by running bash).
The differences between login and non-login shells are described here: Difference between Login Shell and Non-Login Shell? For our purposes, the main difference is that login shells (e.g. that when you first open Terminal) automatically source ~/.bash_profile upon startup, whereas non-login shells (e.g. that when you run bash from within Terminal) do not.
To fix this error, simply source your ~/.bash_profile after running bash:
user#host:~ $ bash
bash: parse_git_branch: command not found
user#host:~ $ source .bash_profile
Alternatively, place the function in ~/.bashrc instead, which will be automatically sourced by non-login shells (as covered in the earlier link).
Instead of having
parse_git_branch
call in PS1 definition alone you may use
parse_git_branch 2>/dev/null
to send stderr to /dev/null. This will silence the error you don't want to see.
have you export your $PS1 ?
You can check by run command:
printenv
else you should export it by run:
export -n PS1
after you will can run sudo or sudo su without problem
The key to this is to NOT export PS1. If it's exported, then any non-login shell also takes PS1. Since .bash_profile is automatically source'd by the login shell, the PS1 variable only affects the login shell.
Related
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 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 trying to run a command that has been aliased in my ~/.bashrc from Perl using the system command. It works well running the command only once, but when I run it twice the second invocation is run as a background job and then suspended (the same as pressing <CTRL-Z>) and I have to type fg to complete the command. For example
use strict;
use warnings;
system ('bash -ic "my_cmd"');
system ('bash -ic "my_cmd"');
The second call never completes. The output is [1]+ Stopped a.pl.
Note:
The same result is obtained when replacing my_cmd with any other command, for example ls.
It seems not to depend of the contents of my ~/.bashrc file. I tried to remove everything from it, and the problem still persisted.
I am using Ubuntu 14.04 and Perl version 5.18.2.
Update
For debugging I reduced my ~/.bashrc to
echo "Entering ~/.bashrc .."
alias my_cmd="ls"
alias
and my ~/.bash_profile to
if [ -f ~/.bashrc ]; then
echo "Entering ~/.bash_profile .."
. ~/.bashrc
fi
Now running:
system ('bash -lc "my_cmd"');
system ('bash -lc "my_cmd"');
gives
Entering ~/.bash_profile ..
Entering ~/.bashrc ..
alias my_cmd='ls'
bash: my_cmd: command not found
Entering ~/.bash_profile ..
Entering ~/.bashrc ..
alias my_cmd='ls'
bash: my_cmd: command not found
and running
system ('bash -ic "my_cmd"');
system ('bash -ic "my_cmd"');
gives
Entering ~/.bashrc ..
alias my_cmd='ls'
a.pl p.sh
[1]+ Stopped a.pl
Rather than using the -i switch for an interactive shell, I think you should use the -l (or --login) switch, which causes bash to act as if it had been invoked as a login shell.
Using the -l switch doesn't load ~/.bashrc by default. According to man bash, in a login shell, /etc/profile/ is loaded, followed by the first file found from ~/.bash_profile/, ~/.bash_login or ~/.profile/. On my system, I have the following in ~/.bash_profile, so ~/.bashrc is loaded:
# Source .bashrc
if [ -f ~/.bashrc ]; then
. ~/.bashrc
fi
Now that your ~/.bashrc is being loaded, you need to enable the expansion of aliases, which is off in a non-interactive shell. To do this, you can add the following line before setting your aliases:
shopt -s expand_aliases
A process randomly stopping - aside from ctrl-z is usually when it needs STDIN, but doesn't have it attached.
Try it with - for example passwd &. It'll background and go straight into 'stopped' state. This may well be what's happening with your bash command. -i means interactive shell, explicitly, and you're trying to do something noninteractive with it.
That's almost certainly not the best approach, you probably want to do something different. bash --login might be closer to what you're after.
Tom Fenech's answer worked for me in Ubuntu 16.04.1 LTS with a small addition. At the top of my ~/.bashrc file, I commented out the following section so that if the shell is not interactive (e.g., a login shell), ~/.bashrc is still read. On some other versions of Linux I don't see this section.
# If not running interactively, don't do anything
case $- in
*i*) ;;
*) return;;
esac
I can't seem to find the difference between a script run two different ways.
Here's the script (named test.sh):
#! /bin/bash
printf "%b\n" "\u5A"
When the script is sourced:
. test.sh
> Z ## Result I want ##
When the script is run:
./test.sh
> \u5A ## Result I get ##
I want the run script to give the results of the sourced script... what setting do I need to set/change?
You are probably getting different versions of printf; the script you are sourcing from is probably a /bin/sh script, not a Bash script proper?
Shouldn't you be using \x instead of \u? printf "%b\n" "\x5A" works fine in both cases for me.
(Totally different idea here, so I'm posting it as another answer.)
Try running these at the command line:
builtin printf "%b\n" "\u5A"
/usr/bin/env printf "%b\n" "\u5A"
printf is both a shell builtin and an executable, and you may be getting different ones depending on whether you source or run the script. To find out, insert this in the script and run it each way:
type printf
While you're at it, you may as well insert this line too:
echo $SHELL
That will reveal if you're getting different shells, per tripleee.
HAHA!!! I finally traced down the problem! Read ahead if interested (leave the page if not).
These are the only command that will translate \u properly:
. ./test.sh ## Sourcing the script, hash-bang = #! /bin/sh
. ./test.bash ## Sourcing the script, hash-bang = #! /bin/bash
./test ## Running the script with no hash-bang
All of the following produce identical results in that they do NOT translate \u:
./test.sh ## Script is run from an interactive shell but in a non-interactive shell
## test.sh has first line: #! /bin/sh
/bin/sh -c "./test.sh" ## Running the script in a non-interactive sh shell
/bin/sh -lc "./test.sh" ## Running the script in a non-interactive, login sh shell
/bin/sh -c ". ./test.sh" ## Sourcing the file in a non-interactive sh shell
/bin/sh -lc ". ./test.sh" ## Sourcing the file in a non-interactive, login sh shell
## test.bash has first line: #! /bin/bash
/bin/bash -c "./test.bash" ## Running the script in a non-interactive bash shell
/bin/bash -lc "./test.bash" ## Running the script in a non-interactive, login bash shell
/bin/bash -c ". ./test.bash" ## Sourcing the file in a non-interactive bash shell
/bin/bash -lc ". ./test.bash" ## Sourcing the file in a non-interactive, login bash shell
## And from ***tripleee*** (thanks btw):
/bin/sh --norc; . ./test.sh ## Sourcing from an interactive sh shell without the ~/.bashrc file read
/bin/bash --norc; . ./test.bash ## Sourcing from an interactive bash shell without the ~/.bashrc file read
The only way to get proper translation is to run the script without a hash-bang... and I finally figured out why! Without a hash-bang my system chooses the default shell, which btw is NOT /bin/bash... it turns out to be /opt/local/bin/bash... two different versions of bash!
Finally, I removed the OSX /bin/bash [v3.2.48(1)] and replaced it with the MacPorts /opt/local/bin/bash [v4.2.10(2)] and now running the script works! It actually solved about 10-15 other problems I've had (like ${var,,}, read sN1 char, complete -EC "echo ' '", and a host of other commands I have scattered throughout my scripts, ~/.bashrc amd ~/.profile). Honestly, I really should have noticed when my scripts using associative arrays suddenly crapped out on me... how stupid can I get!?
I've been using bash v4 for a looong time now, and my Lion upgrade went and down-graded bash back to v3 (get with the program Apple!)... ugh, I feel so ashamed! Everyone still using bash v3, upgrade!! bash v4 is has many, many beautiful upgrades over version 3. Type bash --version to see what version you are running. One advantage is now bash can translate \uHEX into Unicode!
Try removing the space in the first line, I seem to recall that can cause problems. Offhand I'd guess that because of that space, you're not getting bash, but sh.
Glad you solved it. Still, you might be looking for a portable solution.
Assuming you are always using the same formatting string, we can just discard it, and use something like this;
printf () {
# Discard format string
shift
perl -CSD -le '
print map { s/^\\u//; chr(hex($_)) } #ARGV' "$#"
}
Edit to add: You would simply add this function definition at the beginning of your existing script, overriding the builtin printf. Obviously, if you also use printf for other stuff, this special-purpose replacement isn't good enough.
You could rename the function to uprintf or something, still. It merely translates a sequence of hex codes to the corresponding Unicode characters, discarding any \u prefix.
I'm trying to put a large set of bash commands into a matlab script and manage my variables (like file paths, parameters etc) from there. It is also needed because this workflow requires manual intervention at certain steps and I would like to use the step debugger for this.
The problem is, I don't understand how matlab interfaces with bash shell.
I can't do system('source .bash_profile') to define my bash variables. Similarly I can't define them by hand and read them either, e.g. system('export var=somepath') and then system('echo $var') returns nothing.
What is the correct way of defining variables in bash inside matlab's command window? How can I construct a workflow of commands which will use the variables I defined as well as those in my .bash_profile?
If all you need to do is set environment variables, do this in MATLAB:
>> setenv('var','somepath')
>> system('echo $var')
Invoke Bash as a login shell to get your ~/.bash_profile sourced and use the -c option to execute a group of shell commands in one go.
# in Terminal.app
man bash | less -p 'the --login option'
man bash | less -p '-c string'
echo 'export profilevar=myProfileVar' >> ~/.bash_profile
# test in Terminal.app
/bin/bash --login -c '
echo "$0"
echo "$3"
echo "$#"
export var=somepath
echo "$var"
echo "$profilevar"
ps
export | nl
' zero 1 2 3 4 5
# in Matlab
cmd=sprintf('/bin/bash --login -c ''echo "$profilevar"; ps''');
[r,s]=system(cmd);
disp(s);