From 52c805be411a1b4be77690446250052ea1a1a748 Mon Sep 17 00:00:00 2001 From: Ryan Caloras Date: Sat, 22 Aug 2020 13:02:36 -0400 Subject: [PATCH 1/5] get started on 2.0.2-dev --- bashhub/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bashhub/version.py b/bashhub/version.py index 9f503ce..095ad5f 100644 --- a/bashhub/version.py +++ b/bashhub/version.py @@ -1,5 +1,5 @@ import platform -__version__ = '2.0.1' +__version__ = '2.0.2-dev' version_str = 'Bashhub {0} (python {1})'.format(__version__, platform.python_version()) From 53bb3e2001da39d467e2c2fed668b7993cb025cc Mon Sep 17 00:00:00 2001 From: Ryan Caloras Date: Sat, 22 Aug 2020 13:04:07 -0400 Subject: [PATCH 2/5] Update bash-preexec to 0.4.0 --- bashhub/shell/deps/bash-preexec.sh | 142 ++++++++++++++++++----------- 1 file changed, 90 insertions(+), 52 deletions(-) diff --git a/bashhub/shell/deps/bash-preexec.sh b/bashhub/shell/deps/bash-preexec.sh index a29c6c5..60d7c65 100644 --- a/bashhub/shell/deps/bash-preexec.sh +++ b/bashhub/shell/deps/bash-preexec.sh @@ -1,5 +1,3 @@ -#!/bin/bash -# # bash-preexec.sh -- Bash support for ZSH-like 'preexec' and 'precmd' functions. # https://github.com/rcaloras/bash-preexec # @@ -11,7 +9,7 @@ # Author: Ryan Caloras (ryan@bashhub.com) # Forked from Original Author: Glyph Lefkowitz # -# V0.3.6 +# V0.4.0 # # General Usage: @@ -35,19 +33,38 @@ # either of these after bash-preexec has been installed it will most likely break. # Avoid duplicate inclusion -if [[ "$__bp_imported" == "defined" ]]; then +if [[ "${__bp_imported:-}" == "defined" ]]; then return 0 fi __bp_imported="defined" # Should be available to each precmd and preexec -# functions, should they want it. +# functions, should they want it. $? and $_ are available as $? and $_, but +# $PIPESTATUS is available only in a copy, $BP_PIPESTATUS. +# TODO: Figure out how to restore PIPESTATUS before each precmd or preexec +# function. __bp_last_ret_value="$?" +BP_PIPESTATUS=("${PIPESTATUS[@]}") __bp_last_argument_prev_command="$_" __bp_inside_precmd=0 __bp_inside_preexec=0 +# Initial PROMPT_COMMAND string that is removed from PROMPT_COMMAND post __bp_install +__bp_install_string=$'__bp_trap_string="$(trap -p DEBUG)"\ntrap - DEBUG\n__bp_install' + +# Fails if any of the given variables are readonly +# Reference https://stackoverflow.com/a/4441178 +__bp_require_not_readonly() { + local var + for var; do + if ! ( unset "$var" 2> /dev/null ); then + echo "bash-preexec requires write access to ${var}" >&2 + return 1 + fi + done +} + # Remove ignorespace and or replace ignoreboth from HISTCONTROL # so we can accurately invoke preexec with a command from our # history even if it starts with a space. @@ -75,6 +92,19 @@ __bp_trim_whitespace() { echo -n "$var" } + +# Returns a copy of the passed in string trimmed of whitespace +# and removes any leading or trailing semi colons. +# Used for manipulating substrings in PROMPT_COMMAND +__bp_sanitize_string() { + local sanitized_string + sanitized_string=$(__bp_trim_whitespace "${1:-}") + sanitized_string=${sanitized_string%;} + sanitized_string=${sanitized_string#;} + sanitized_string=$(__bp_trim_whitespace "$sanitized_string") + echo -n "$sanitized_string" +} + # This function is installed as part of the PROMPT_COMMAND; # It sets a variable to indicate that the prompt was just displayed, # to allow the DEBUG trap to know that the next command is likely interactive. @@ -86,9 +116,9 @@ __bp_interactive_mode() { # This function is installed as part of the PROMPT_COMMAND. # It will invoke any functions defined in the precmd_functions array. __bp_precmd_invoke_cmd() { - # Save the returned value from our last command. Note: this MUST be the - # first thing done in this function. - __bp_last_ret_value="$?" + # Save the returned value from our last command, and from each process in + # its pipeline. Note: this MUST be the first thing done in this function. + __bp_last_ret_value="$?" BP_PIPESTATUS=("${PIPESTATUS[@]}") # Don't invoke precmds if we are inside an execution of an "original # prompt command" by another precmd execution loop. This avoids infinite @@ -106,7 +136,8 @@ __bp_precmd_invoke_cmd() { # Test existence of functions with: declare -[Ff] if type -t "$precmd_function" 1>/dev/null; then __bp_set_ret_value "$__bp_last_ret_value" "$__bp_last_argument_prev_command" - $precmd_function + # Quote our function invocation to prevent issues with IFS + "$precmd_function" fi done } @@ -115,19 +146,19 @@ __bp_precmd_invoke_cmd() { # precmd functions. This is available for instance in zsh. We can simulate it in bash # by setting the value here. __bp_set_ret_value() { - return $1 + return ${1:-} } __bp_in_prompt_command() { local prompt_command_array - IFS=';' read -ra prompt_command_array <<< "$PROMPT_COMMAND" + IFS=$'\n;' read -rd '' -a prompt_command_array <<< "$PROMPT_COMMAND" local trimmed_arg - trimmed_arg=$(__bp_trim_whitespace "$1") + trimmed_arg=$(__bp_trim_whitespace "${1:-}") local command - for command in "${prompt_command_array[@]}"; do + for command in "${prompt_command_array[@]:-}"; do local trimmed_command trimmed_command=$(__bp_trim_whitespace "$command") # Only execute each function if it actually exists. @@ -144,10 +175,10 @@ __bp_in_prompt_command() { # environment to attempt to detect if the current command is being invoked # interactively, and invoke 'preexec' if so. __bp_preexec_invoke_exec() { + # Save the contents of $_ so that it can be restored later on. # https://stackoverflow.com/questions/40944532/bash-preserve-in-a-debug-trap#40944702 - __bp_last_argument_prev_command="$1" - + __bp_last_argument_prev_command="${1:-}" # Don't invoke preexecs if we are inside of another preexec. if (( __bp_inside_preexec > 0 )); then return @@ -157,16 +188,16 @@ __bp_preexec_invoke_exec() { # Checks if the file descriptor is not standard out (i.e. '1') # __bp_delay_install checks if we're in test. Needed for bats to run. # Prevents preexec from being invoked for functions in PS1 - if [[ ! -t 1 && -z "$__bp_delay_install" ]]; then + if [[ ! -t 1 && -z "${__bp_delay_install:-}" ]]; then return fi - if [[ -n "$COMP_LINE" ]]; then + if [[ -n "${COMP_LINE:-}" ]]; then # We're in the middle of a completer. This obviously can't be # an interactively issued command. return fi - if [[ -z "$__bp_preexec_interactive_mode" ]]; then + if [[ -z "${__bp_preexec_interactive_mode:-}" ]]; then # We're doing something related to displaying the prompt. Let the # prompt set the title instead of me. return @@ -176,12 +207,12 @@ __bp_preexec_invoke_exec() { # In other words, if you have a subshell like # (sleep 1; sleep 2) # You want to see the 'sleep 2' as a set_command_title as well. - if [[ 0 -eq "$BASH_SUBSHELL" ]]; then + if [[ 0 -eq "${BASH_SUBSHELL:-}" ]]; then __bp_preexec_interactive_mode="" fi fi - if __bp_in_prompt_command "$BASH_COMMAND"; then + if __bp_in_prompt_command "${BASH_COMMAND:-}"; then # If we're executing something inside our prompt_command then we don't # want to call preexec. Bash prior to 3.1 can't detect this at all :/ __bp_preexec_interactive_mode="" @@ -189,7 +220,10 @@ __bp_preexec_invoke_exec() { fi local this_command - this_command=$(HISTTIMEFORMAT= builtin history 1 | { read -r _ this_command; echo "$this_command"; }) + this_command=$( + export LC_ALL=C + HISTTIMEFORMAT= builtin history 1 | sed '1 s/^ *[0-9][0-9]*[* ] //' + ) # Sanity check to make sure we have something to invoke our function with. if [[ -z "$this_command" ]]; then @@ -204,13 +238,14 @@ __bp_preexec_invoke_exec() { local preexec_function local preexec_function_ret_value local preexec_ret_value=0 - for preexec_function in "${preexec_functions[@]}"; do + for preexec_function in "${preexec_functions[@]:-}"; do # Only execute each function if it actually exists. # Test existence of function with: declare -[fF] if type -t "$preexec_function" 1>/dev/null; then - __bp_set_ret_value $__bp_last_ret_value - $preexec_function "$this_command" + __bp_set_ret_value ${__bp_last_ret_value:-} + # Quote our function invocation to prevent issues with IFS + "$preexec_function" "$this_command" preexec_function_ret_value="$?" if [[ "$preexec_function_ret_value" != 0 ]]; then preexec_ret_value="$preexec_function_ret_value" @@ -229,14 +264,14 @@ __bp_preexec_invoke_exec() { __bp_install() { # Exit if we already have this installed. - if [[ "$PROMPT_COMMAND" == *"__bp_precmd_invoke_cmd"* ]]; then + if [[ "${PROMPT_COMMAND:-}" == *"__bp_precmd_invoke_cmd"* ]]; then return 1; fi trap '__bp_preexec_invoke_exec "$_"' DEBUG # Preserve any prior DEBUG trap as a preexec function - local prior_trap=$(sed "s/[^']*'\(.*\)'[^']*/\1/" <<<"$__bp_trap_string") + local prior_trap=$(sed "s/[^']*'\(.*\)'[^']*/\1/" <<<"${__bp_trap_string:-}") unset __bp_trap_string if [[ -n "$prior_trap" ]]; then eval '__bp_original_debug_trap() { @@ -248,61 +283,64 @@ __bp_install() { # Adjust our HISTCONTROL Variable if needed. __bp_adjust_histcontrol - # Issue #25. Setting debug trap for subshells causes sessions to exit for # backgrounded subshell commands (e.g. (pwd)& ). Believe this is a bug in Bash. # # Disabling this by default. It can be enabled by setting this variable. - if [[ -n "$__bp_enable_subshells" ]]; then + if [[ -n "${__bp_enable_subshells:-}" ]]; then # Set so debug trap will work be invoked in subshells. set -o functrace > /dev/null 2>&1 shopt -s extdebug > /dev/null 2>&1 fi; + local __bp_existing_prompt_command + # Remove setting our trap install string and sanitize the existing prompt command string + __bp_existing_prompt_command="${PROMPT_COMMAND//$__bp_install_string[;$'\n']}" # Edge case of appending to PROMPT_COMMAND + __bp_existing_prompt_command="${__bp_existing_prompt_command//$__bp_install_string}" + __bp_existing_prompt_command=$(__bp_sanitize_string "$__bp_existing_prompt_command") + # Install our hooks in PROMPT_COMMAND to allow our trap to know when we've # actually entered something. - PROMPT_COMMAND="__bp_precmd_invoke_cmd; __bp_interactive_mode" + PROMPT_COMMAND=$'__bp_precmd_invoke_cmd\n' + if [[ -n "$__bp_existing_prompt_command" ]]; then + PROMPT_COMMAND+=${__bp_existing_prompt_command}$'\n' + fi; + PROMPT_COMMAND+='__bp_interactive_mode' # Add two functions to our arrays for convenience # of definition. precmd_functions+=(precmd) preexec_functions+=(preexec) - # Since this function is invoked via PROMPT_COMMAND, re-execute PC now that it's properly set - eval "$PROMPT_COMMAND" + # Invoke our two functions manually that were added to $PROMPT_COMMAND + __bp_precmd_invoke_cmd + __bp_interactive_mode } -# Sets our trap and __bp_install as part of our PROMPT_COMMAND to install -# after our session has started. This allows bash-preexec to be inlucded -# at any point in our bash profile. Ideally we could set our trap inside -# __bp_install, but if a trap already exists it'll only set locally to -# the function. +# Sets an installation string as part of our PROMPT_COMMAND to install +# after our session has started. This allows bash-preexec to be included +# at any point in our bash profile. __bp_install_after_session_init() { # Make sure this is bash that's running this and return otherwise. - if [[ -z "$BASH_VERSION" ]]; then + if [[ -z "${BASH_VERSION:-}" ]]; then return 1; fi - # If there's an existing PROMPT_COMMAND capture it and convert it into a function - # So it is preserved and invoked during precmd. - if [[ -n "$PROMPT_COMMAND" ]]; then - eval '__bp_original_prompt_command() { - '"$PROMPT_COMMAND"' - }' - precmd_functions+=(__bp_original_prompt_command) - fi + # bash-preexec needs to modify these variables in order to work correctly + # if it can't, just stop the installation + __bp_require_not_readonly PROMPT_COMMAND HISTCONTROL HISTTIMEFORMAT || return - # Installation is finalized in PROMPT_COMMAND, which allows us to override the DEBUG - # trap. __bp_install sets PROMPT_COMMAND to its final value, so these are only - # invoked once. - # It's necessary to clear any existing DEBUG trap in order to set it from the install function. - # Using \n as it's the most universal delimiter of bash commands - PROMPT_COMMAND=$'\n__bp_trap_string="$(trap -p DEBUG)"\ntrap DEBUG\n__bp_install\n' + local sanitized_prompt_command + sanitized_prompt_command=$(__bp_sanitize_string "$PROMPT_COMMAND") + if [[ -n "$sanitized_prompt_command" ]]; then + PROMPT_COMMAND=${sanitized_prompt_command}$'\n' + fi; + PROMPT_COMMAND+=${__bp_install_string} } # Run our install so long as we're not delaying it. -if [[ -z "$__bp_delay_install" ]]; then +if [[ -z "${__bp_delay_install:-}" ]]; then __bp_install_after_session_init fi; From 737fddf776fac104d0beee639e65bea219c3e4c4 Mon Sep 17 00:00:00 2001 From: Ryan Caloras Date: Mon, 24 Aug 2020 15:23:11 -0400 Subject: [PATCH 3/5] Removing dated unnecessary uninstall file - Simply rm -r ~/.bashhub - Remove install hook from bash profile --- uninstall-bashhub.sh | 37 ------------------------------------- 1 file changed, 37 deletions(-) delete mode 100755 uninstall-bashhub.sh diff --git a/uninstall-bashhub.sh b/uninstall-bashhub.sh deleted file mode 100755 index 8c4dfac..0000000 --- a/uninstall-bashhub.sh +++ /dev/null @@ -1,37 +0,0 @@ -#!/bin/bash -# -# Bashhub.com Installation shell script -# -# Ryan Caloras (ryan@bashhub.com) -# -# It must work everywhere, including on systems that lack -# a /bin/bash, map 'sh' to ksh, ksh97, bash, ash, or zsh, -# and potentially have either a posix shell or bourne -# shell living at /bin/sh. -# -# See this helpful document on writing portable shell scripts: -# - -find_users_bash_file () { - - # possible bash files to use, order matters - bash_file_array=( ~/.bashrc ~/.bash_profile ~/.profile) - - for file in "${bash_file_array[@]}" - do - if [ -e $file ]; then - echo $file - return 0 - fi - done - - die "No bashfile (e.g. .profile, .bashrc, ect) could be found" 1 -} - - -rm -r ~/.bashhub -bashprofile=`find_users_bash_file` -cp $bashprofile "$bashprofile.backup" - -grep -v "source ~/.bashhub/bashhub.sh" $bashprofile > temp -mv temp $bashprofile From 321aa655dd8eb1bab65cb13748a8507d1997768e Mon Sep 17 00:00:00 2001 From: Ryan Caloras Date: Tue, 1 Sep 2020 14:00:53 -0400 Subject: [PATCH 4/5] cp bash-preexec master --- bashhub/shell/deps/bash-preexec.sh | 5 ----- 1 file changed, 5 deletions(-) diff --git a/bashhub/shell/deps/bash-preexec.sh b/bashhub/shell/deps/bash-preexec.sh index 60d7c65..5b96f66 100644 --- a/bashhub/shell/deps/bash-preexec.sh +++ b/bashhub/shell/deps/bash-preexec.sh @@ -161,7 +161,6 @@ __bp_in_prompt_command() { for command in "${prompt_command_array[@]:-}"; do local trimmed_command trimmed_command=$(__bp_trim_whitespace "$command") - # Only execute each function if it actually exists. if [[ "$trimmed_command" == "$trimmed_arg" ]]; then return 0 fi @@ -230,10 +229,6 @@ __bp_preexec_invoke_exec() { return fi - # If none of the previous checks have returned out of this function, then - # the command is in fact interactive and we should invoke the user's - # preexec functions. - # Invoke every function defined in our function array. local preexec_function local preexec_function_ret_value From 60c1342b8cdbf61ed8e93a2b4dc75418d10474cc Mon Sep 17 00:00:00 2001 From: Ryan Caloras Date: Tue, 1 Sep 2020 14:54:57 -0400 Subject: [PATCH 5/5] Prepare for release 2.0.2 --- bashhub/version.py | 2 +- install-bashhub.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bashhub/version.py b/bashhub/version.py index 095ad5f..f009a8f 100644 --- a/bashhub/version.py +++ b/bashhub/version.py @@ -1,5 +1,5 @@ import platform -__version__ = '2.0.2-dev' +__version__ = '2.0.2' version_str = 'Bashhub {0} (python {1})'.format(__version__, platform.python_version()) diff --git a/install-bashhub.sh b/install-bashhub.sh index 4a71642..b7d272c 100755 --- a/install-bashhub.sh +++ b/install-bashhub.sh @@ -45,7 +45,7 @@ zshprofile=~/.zshrc # Optional parameter to specify a github branch # to pull from. -github_branch=${1:-'2.0.1'} +github_branch=${1:-'2.0.2'} install_bashhub() { check_dependencies