Run a bash command from a string - bash

I have this function named st that let's me change the name of the terminal so that I understand what the terminal session is doing.
function st() {
if [[ -z "$ORIG" ]]; then
ORIG=$PS1
fi
TITLE="\[\e]2;$*\a\]"
PS1=${ORIG}${TITLE}
}
So when I run st yarn transportation start the title of my terminal changes to yarn transportation start. So far so good.
I don't know much about bash.
But it pains me that I have to now run yarn transportation start to actually run that command. Is there any way that I can run anything after st after the title is changed?
I want to run a single command that changes the title of the terminal and runs the command that I gave in the title

After setting title (setting PS1), you need to make sure that right TITLE is being used so that proper command can be run.
As pointed in How to execute a bash command stored as a string with quotes and asterisk there is eval function that runs the command from a string.
$ type st
type st
st is a function
st ()
{
if [[ -z "$ORIG" ]]; then
ORIG=$PS1;
fi;
TITLE="\[\e]2;$*\a\]";
PS1=${ORIG}${TITLE};
TITLE="$*"; # Take all the arguments
eval $TITLE
}
You can even at a -t flag to provide a separate title and command like below
function st() {
TITLE=$*
COMMAND=$*
if [[ -z "$ORIG" ]]; then
ORIG=$PS1
fi
if [ "$1" == "-t" ]; then
TITLE=$2;
COMMAND=${#:3}
fi
TITLE="\[\e]2;$TITLE\a\]"
PS1=${ORIG}${TITLE}
eval $COMMAND
}

Related

Equal and not equal operators not working in bash script

I have this function inside a .sh script :
prepare_for_test(){
stopresources;
initresources;
if [ "$1" = "--gf" ]; then
startglassfish;
fi
docker ps;
notify-send "Done!" "You can now test" -t 10000;
};
The script's name's preparefortests.sh. When I run it on bash, passing --gf or "--gf":
preparefortests.sh --gf
it does not run alias startglassfish, as if that if statement was false.
I even tried to add a check on the parameter:
if [ "$1" ] && [ "$1" != "--gf" ]; then
echo "uknown parameter $1"
fi
but it's not working neither, when e.g. I try to run it like:
preparefortests.sh hello
I'd expect "unknown parameter hello".
What am I doing wrong?
The comparison statement is correct:
if [ "$1" = "--gf" ]; then
startglassfish;
fi
There can be other issue like:
Make sure you pass $1 argument, while calling function:
Write prepare_for_test $1
The problem might be the alias used. For almost every purpose, aliases are superseded by shell functions. So either you need to make alias as function and export it or instead use special variable BASH_ALIASES. In your case:
if [ "$1" = "--gf" ];then
${BASH_ALIASES[startglassfish]};
fi

How can I make the colors of the command prompt dynamic?

My attempt to change the colors of the prompt depending on the state of an environment variable:
tw_prompt_color () {
if [[ ! -z $TASKRC ]]; then
echo '\033[34m\'
else
echo '\033[32m\'
fi
}
export -f tw_prompt_color
PS1="\[$(tw_prompt_color)]iMac5K# \[\033[33;1m\]\w:\[\033[m\]\[\033[33m\]\$(parse_git_branch)\[\033[00m\] "
This doesn't work. The prompt remains the same color regardless of the state of $TASKRC unless I manually run exec bash. But oddly, adding exec bash into the tw_prompt_color function doesn't help.
This works for me. Maybe use this approach as a starting point to create your own solution.
1. Edit your ~/.bashrc
fancy_git_prompt(){
git_info=`git branch 2>/dev/null | sed -e '/^[^*]/d' -e 's/* \(.*\)/\1/'`
if [ "${git_info}" ]; then
if [ "$(git status -s)" ]; then
git_color='\033[1;31m'
else
git_color='\033[1;36m'
fi
echo -e "${git_color}git:${git_info}"
fi
}
PS1='\n\[\033[1;32m\][\w] $(fancy_git_prompt)\[\033[0m\]\n\$ '
2. Reload ~/.bashrc
source ~/.bashrc
There are more advanced versions out there, e.g. git-prompt.sh
OK, this works:
tw_prompt_color () {
if [[ ! -z $TASKRC ]]; then
echo -e '\033[34m'
else
echo -e '\033[32m'
fi
}
export -f tw_prompt_color
PS1='$(tw_prompt_color)iMac5K# \[\033[33;1m\]\w:\[\033[m\]\[\033[33m\]$(parse_git_branch)\[\033[00m\] '
Fix #1: Change to single quotes as suggested. I still don't understand why this worked when the $(parse_get_branch) bit worked fine with double quotes.
Fix #2: Add -e to the echo command.
Fix #3: Remove some of the now extraneous brackets and escaping. Totally don't get why I need to do that now but it works.
If anyone who can shed light on my bash cluelessness, I'd appreciate it.

How can I intercept commands that contain a certain string?

Sometimes, it happens to me that I'm executing certain commands, and only afterwards I realize that I sent the wrong parameter to a command ( like a restart of a Heroku application ). I'd like to modify bash in such a way that if it sees a command containing a certain string, it will prompt me whether I'm sure or not. For example ( imagine the string is tempus ):
$ heroku restart --app tempus
Now, I'd like bash to prompt me with a Y/N prompt, and if I type y, only then I'd like it to execute the command. If I type N, the command will not be executed. How could I handle this problem?
I don't know of any way to intercept all bash commands, but you can intercept predetermined commands using the following trick.
Create a directory (say ~/interception) and set it as the first entry in $PATH
Create the following script in that directory with a list of commands you wish to intercept and the full path to the actual command
[bash]$ cat intercept.sh
#!/bin/bash
# map commands to full path
declare -A COMMANDS
COMMANDS[heroku]=/usr/bin/heroku
COMMANDS[grep]=/bin/grep
# ... more ...
CMD=$(basename $0) # command used to call this script
if [[ ! -z "${COMMANDS[$CMD]+x}" ]]; then # mapping found
# Do what you wish here. You can even modify/inspect the params.
echo "intercepted $CMD command... "
${COMMANDS[$CMD]} $# # run actual command with all params
else
echo "Unknown command $CMD"
fi
In the same directory, create symlinks to that script using the name of the commands you wish to intercept
[bash]$ ln -s intercept.sh grep
[bash]$ ln -s intercept.sh heroku
Now, each time you call the command, that script is invoked via the symlink and it can then do your bidding before calling the actual command.
You can extend this further by sourcing the $COMMANDS from a config file and create helper commands to augment the config file and create/remove the sym links. You would then be able to manage the who setup using commands such as:
intercept_add `which heroku`
intercept_remove heroku
intercept_list
Because bash itself doesn't support command line filter, it's not possible to intercept commands.
Here is a dirty solution:
Find all executables in PATH and create wrapper functions for each of them.
The wrapper function then call prefilter() function if it's declared.
If prefilter() function failed, the command is canceled.
SOURCE: cmd-wrap.sh
#!/bin/bash # The shebang is only useful for debug. Don't execute this script.
function create_wrapper() {
local exe="$1"
local name="${exe##*/}"
# Only create wrappers for non-builtin commands
[ `type -t "$name"` = 'file' ] || return
# echo "Create command wrapper for $exe"
eval "
function $name() {\
if [ \"\$(type -t prefilter)\" = 'function' ]; then \
prefilter \"$name\" \"\$#\" || return; \
fi; \
$exe \"\$#\";
}"
}
# It's also possible to add pre/post hookers by install
# [ `type -t \"$name-pre\"` = 'function' ] && \"$name-pre\" \"\$#\"
# into the dynamic generated function body.
function _create_wrappers() {
local paths="$PATH"
local path
local f n
while [ -n "$paths" ]; do
path="${paths%%:*}"
if [ "$path" = "$paths" ]; then
paths=
else
paths="${paths#*:}"
fi
# For each path element:
for f in "$path"/*; do
if [ -x "$f" ]; then
# Don't create wrapper for strange command names.
n="${f##*/}"
[ -n "${n//[a-zA-Z_-]/}" ] || create_wrapper "$f"
fi
done
done
unset _create_wrappers # Remove the installer.
unset create_wrapper # Remove the helper fn, which isn't used anymore.
}
_create_wrappers
To utilize it for your problem:
source it in bash:
. ./cmd-wrap.sh
Create your version of prefilter() to check if any argument contains the string:
function prefilter() {
local a y
for a in "$#"; do
if [ "$a" != "${a/tempus}" ]; then
echo -n "WARNING: The command contains tempus. Continue?"
read y
[ "$y" = 'Y' ] || [ "$y" = 'y' ]
return $?
fi
done
return 0
}
Run
heroku restart --app tempus
but not
/usr/bin/heroku restart --app tempus
to make use of the wrapper function.
The easiest way is to use aliases. This simple example should work for you:
This protects from executing the heroku command with tempus in the arguments
function protect_heroku {
# use grep to determine if the bad string is in arguments
echo "$*" | grep tempus > /dev/null
# if string is not in arguments
if [ $? != 0 ]; then
# run the protected command using its full path, so as not to trigger alias
/path/to/heroku "$#"
else
# get user confirmation
echo -n Are you sure \(y/n\)?' '
read CONFIRM
if [ "$CONFIRM" = y ]; then
# run the protected command using its full path
/path/to/heroku "$#"
fi
fi
}
# This is the key:
# This alias command means that 'heroku' from now refers
# to the function protect_heroku, rather than /bin/heroku
alias heroku=protect_heroku
Put this code into your bash profile ~/.profile and then log out and log back in. From now on, bash will protect you from accidentally running heroku with tempus.
Simplest way is to replace heroku with a script that does the checking before executing the real heroku. Another way would be to add a bash alias for heroku.

ZSH which/whence alias for type -p not identifying node

We have code to check for a node install:
which="type -p"
if [ $SHELL = "/bin/zsh" ]; then
which="whence"
fi
# make sure that node exists
node=`$which node 2>&1`
ret=$?
if [ $ret -ne 0 ] || ! [ -x "$node" ]; then
<"This error code is returned">
But when I run this with ZSH (OhMyZsh) it returns a 127 (does not exist). Commenting out the which="whence" lets it run fine.
Without removing the whole aliasing bit is there any way to have ZSH play along with this? Ideally I'd like to make a change on my end to make this work rather than modifying this code at all.
You mean, you run $node and it appears that you’ve tried to run command whose name is node --alias-args which does not exist?
If this is true, change the third line to use whence -p: it has the same output as type -p in bash. If not, please, explain when this code is returned.
Update: I do not know what was done in ohmyzsh (though I have not a single idea how to make a builtin not found) so just try to rewrite the code in this way:
# At the very top
if [ -n $ZSH_VERSION ] ; then
emulate -L zsh
endif
<...>
which="type -p"
if [ -n $ZSH_VERSION ] ; then
which=( whence -p ) # Changes variable type as well
endif
node=`$which node 2>&1`
if (( ? )) || ! test -x $node ; then
<...>

writing from a function in a Bash script leaking file descriptors

We have a shell script that is called by cron and runs as root.
This script outputs logging and debug info, and has been failing at one certain point. This point varies based on how much output the script creates (it fails sooner if we enable more debugging output, for example).
However, if the script is called directly, as a user, then it works without a problem.
We have since created a simplified test case which demonstrates the problem.
The script is:
#!/bin/bash
function log_so () {
local msg="$1"
if [ -z "${LOG_FILE}" ] ; then warn_so "It's pointless use log_so() if LOG_FILE variable is undefined!" ; return 1 ; fi
echo -e "${msg}"
echo -e "${msg}" >> ${LOG_FILE}
(
/bin/true
)
}
LOG_FILE="/usr/local/bin/log_bla"
linenum=1
while [[ $linenum -lt 2000 ]] ; do
log_so "short text: $linenum"
let linenum++
done
The highest this has reached is 244 before dying (when called via cron).
Some other searches recommended using a no-op subshell from the function and also calling /bin/true but not only did this not work, the subshell option is not feasible in the main script.
We have also tried changing the file descriptor limit for root, but that did not help, and have tried using both #!/bin/sh and #!/bin/bash for the script.
We are using bash 4.1.5(1)-release on Ubuntu 10.04 LTS.
Any ideas or recommendations for a workaround would be appreciated.
What about opening a fd by hand and cleaning it up afterwards? I don't have a bash 4.1 to test with, but it might help.
LOG_FILE="/usr/local/bin/log_bla"
exec 9<> "$LOG_FILE"
function log_so () {
local msg="$1"
if [ -z "${LOG_FILE}" ] ; then warn_so "It's pointless use log_so() if LOG_FILE variable is undefined!" ; return 1 ; fi
echo -e "${msg}"
echo -e "${msg}" >&9
return 0
}
linenum=1
while [[ $linenum -lt 2000 ]] ; do
log_so "short text: $linenum"
let linenum++
done
exec 9>&-

Resources