diff --git a/bash-abbr b/bash-abbr deleted file mode 100644 index 3fd1f25..0000000 --- a/bash-abbr +++ /dev/null @@ -1,380 +0,0 @@ -#!/usr/bin/env bash -# Shebang merely for syntax highlighting. - -# This file should be pasted directly into ~/.bashrc or -# put into the bashrc drop-in directory ~/.bashrc.d/ -# and then sourced from ~/.bashrc. - -# Disable plugin by uncommenting the following line -# return - -###### bash-abbr: fish like abbreviations for bash, and more ###### -# -# After sourcing this plugin and in an interactive bash shell -# where --noediting was not supplied during invocation -# (readline must be enabled), regular bash aliases will be -# expanded as you type. This is different from normal alias -# behavior, where they are only expanded internally, invisible -# to the user. This is useful when sharing the screen, for -# making edits to the expanded version of an alias, or for -# personal reassurance that you're executing the correct command. -# -# Regular, internal alias expansion will be disabled for this in order -# to not exhibit unexpected behavior. See the end of this file for an explanation. -# -# Besides the expected behavior, there are a few extra features -# that are usually useful. -# -## Global Aliases (can be combined with other alias types): -# These aliases expand anywhere on the command line. -# Syntax: -# - alias [alias-name]=*[alias-value] -# - alias [alias-name]=$[alias-value] (identical but self-insertion is inhibited, see below) -# Examples: -# - alias G='*| grep' -# Typing `G` and pressing space will expand the string after the asterisk -# and self-insert the space. -# -## Post Evaluating Aliases: -# These aliases expand to the evaluation of the *recursively expanded* alias-value. -# Syntax: -# - alias [alias-name]=[alias-value]! -# Examples: -# - alias g=git gph='printf "g push %q HEAD" "$(git remote | head -1)"!' -# Typing `gph` and pressing enter might evaluate to something like -# `git push origin HEAD`. The `origin` part is dynamic, it will -# expand to whatever the first configured git remote is. Because this -# is the post-evaluating form of the evaluating aliases, the `g` was first -# expanded to `git`, only after which it was evaluated. -# -## Pre Evaluating Aliases: -# These aliases expand to the evaluation of the initial alias-value, after -# which recursive alias expansion continues. -# Syntax: -# - alias [alias-name]=[alias-value]$ -# Examples: -# - alias c='*wl-paste$' -# Typing `c` anywhere on the command line will immediately expand to the -# clipboard contents on wayland based systems. The expansion is subject -# to further recursive alias expansion. Note that -# - alias c='*wl-paste!' -# makes less sense, because copying a command that contains an alias would -# not expand in this case, leading to a "command not found" -# -## File Extension Aliases: -# These aliases expand words on the command line that end with a file extension -# (dot + suffix) to a custom command invocation that opens files of that file type. -# Similar to xdg-open and Freedesktop MIME types, but for the command line. -# Syntax: -# - alias .[file-extension-without-dot]=[program-handler] -# Examples: -# - alias .txt=v .js=v .bashrc=v -# Typing `test.txt`, `~/.bashrc` or `index.js` and pressing enter will expand to -# `v test.txt` (or the others respectively), which might in turn expand to `vim test.txt` -# if there is an alias like `alias v=vim`. -# -## Aliases with inhibited self-insertion: -# Upon expanding these aliases, there won't be any self-insertion at all. -# Syntax: -# - alias [alias-name]=-[alias-value] -# - alias [alias-name]=$[alias-value] (global alias) -# Examples: -# - alias gssh='$git@github.com:' -# Typing `gssh` and pressing space will expand to `git@github.com:` without any trailing space. -# This is useful, because you usually enter a GitHub username directly after the colon without -# any characters in between. The `$` form was chosen because this alias can usually appear -# anywhere on the command line. -# - alias cuhs='-curl https://' -# Typing `cuhs` and pressing space will expand to `curl https://` without any trailing space. -# This is similar to the example above, only that the `-` form was chose because this alias -# usually doesn't make sense anywhere on the command line. -# -## Preventing alias types from being recognized: -# If the alias-value is supposed to start with a $, * or !, then you can prefix it with a single space. -# If the alias-value is supposed to start with a space, prefix it with another space. -# If the alias-value is supposed to end with $ or !, suffix it with a space. -# If the alias-value is supposed to end with a space, suffix it with another space. -# -## Combing alias types: -# Every alias type can be combined with any other alias type, as far as the syntax -# allows it (only one trailing character, only one leading character). -# Therefore, you can create an alias like this: -# - alias c=cat .txt='$c!' -# And then use it on the command line like this: -# - echo a.txt| -# `|` represents the cursor. Assuming the file a.txt contains the text `hey`, -# pressing space now would lead to the following result: -# - echo hey| -# Therefore, a global alias without self-insertion ($) was used on a file extension alias (.txt) -# and the result recursively expanded (c -> cat) and finally evaluated (post-evaluation, !). - -# WARNING: This plugin creates and overwrites various readline bindings. -# Therefore, it might interfere with other plugins/custom bindings. -# Some bindings may be changed (e.g. \e\C-a...) while others should not (e.g. \C-m) - -# Setting IFS to null so we can use "${var[*]}" syntax to make a string out of the meta char array -_bash_base_meta_chars=('|' '&' ';' '(' ')') -_bash_meta_chars_without_left_right_angle_brackets=("${_bash_base_meta_chars[@]}" ' ' $'\t' $'\n') -_bash_meta_chars_without_tab_and_nl=("${_bash_base_meta_chars[@]}" '<' '>' ' ') -_bash_meta_chars=("${_bash_base_meta_chars[@]}" '<' '>' ' ' $'\t' $'\n') - -# Regexes for heuristically determining whether we're doing expansion on the first word. -# Construction put into functions to prevent global namespace pollution -# and to increase readability. -_control_op () { - declare -r _newline=$'\n' \ - _bg_proc_or_and_and='[^\\](\\\\)*&&?' \ - _pipe_or_or_or='[^\\](\\\\)*\|\|?' \ - _semicolon_or_case_operators='[^\\](\\\\)*;;?&?' \ - _opening_paren='(^|[^[:alnum:]\\])(\\\\)*\(' - declare _control_op="^" - _control_op+="|$_newline" - _control_op+="|$_bg_proc_or_and_and" - _control_op+="|$_pipe_or_or_or" - _control_op+="|$_semicolon_or_case_operators" - _control_op+="|$_opening_paren" - printf %s "$_control_op" - unset -f _control_op -} -# shellcheck disable=SC2155 -declare _control_op="$(_control_op)" # TODO use bash 5.3 `${}` command substitution syntax to avoid subshells - -_redir_or_assign_word () { - # shellcheck disable=SC2016 - declare -r _varredir_or_number='\{[[:alpha:]_][[:alnum:]_]*\}|[[:digit:]]*' \ - _redir_in_or_out='>|<' \ - _close_or_move_fd='&[[:digit:]]*-?' \ - _redir_or_append_out_and_err='(&)(>>?)' \ - _heredoc_herestring_in_out_etc='>(>|\|)?|<(>|<(-?|<))?' \ - _identifier_equals='[[:alpha:]_][[:alnum:]]*=' \ - _simple_unquoted_word='[^[:blank:]<>&;()|'\''"`$]+' \ - _double_quoted_string='\$?"([^"]|\\")*"' \ - _single_quoted_string="\\\$?'[^']*'" \ - _backtick_string='`([^`]|\\`)*`' \ - _simple_variable='\$([[:alpha:]_][[:alnum:]]*|[-_$!*#[:digit:]])' \ - _advanced_variable='\$\{([[:alpha:]_][[:alnum:]]*|[-_$!*#[:digit:]])[^}]*\}' \ - _deprecated_arithmetic_eval='\$\[[^][]*\]' \ - _cmd_subst='\$\([^()]*\)' \ - _arithmetic_eval='\$\(\([^()]*\)\)' \ - _process_substition='<\([^()]*\)' \ - _escaped_char='\\.' - declare _part="$_simple_unquoted_word" - _part+="|$_double_quoted_string" - _part+="|$_single_quoted_string" - _part+="|$_backtick_string" - _part+="|$_simple_variable" - _part+="|$_advanced_variable" - _part+="|$_deprecated_arithmetic_eval" - _part+="|$_cmd_subst" - _part+="|$_arithmetic_eval" - _part+="|$_process_substition" - _part+="|$_escaped_char" - declare -r _word="($_part)*" \ - _close_redirection="($_varredir_or_number)($_redir_in_or_out)($_close_or_move_fd)" \ - _generic_redirection="($_redir_or_append_out_and_err|($_varredir_or_number)($_heredoc_herestring_in_out_etc))" - # shellcheck disable=SC1087 - declare -r _word_redirection_or_assignment="($_generic_redirection[[:blank:]]*|$_identifier_equals)($_word)" - printf %s "$_close_redirection|$_word_redirection_or_assignment" - unset -f _redir_or_assign_word -} -# shellcheck disable=SC2155 -declare _redir_or_assign_word="$(_redir_or_assign_word)" # TODO use bash 5.3 `${}` command substitution syntax to avoid subshells - -declare -gr is_first_word_prefix="($_control_op)[[:blank:]]*((($_redir_or_assign_word)[[:blank:]]+)*)" -# If the regex leads to problems/performance issues, you may try this simpler regex -# that does no redirection/assignment word checking: -# declare -r is_first_word_prefix="($_control_op)[[:blank:]]*" - -unset -v _control_op _redir_or_assign_word - -_try_abbrev_internal () { - # Recursively perform interactive alias expansion on the line defined by $1, taking $2 as the point and $3 as the mark. - # Set the vars defined by $4, $5 and $6 to the updated values (line, point and mark). - # $7 is the character/string to self-insert. - declare -r line=$1 mark=$3 new_line=$4 new_point=$5 new_mark=$6 \ - level=${#FUNCNAME[@]} - declare point=$2 - declare self_insertion=$7 - # `level` is needed to keep track of the recursion, because we don't have a return value but rather - # set a variable defined in the calling context. This level is just appended to the identifier to make it unique - # across all the recursive calls. - - # IFS must be null so "${var[*]}" concatenates without space - declare -r IFS= - - # If the self-insertion character is null (i.e. when pressing enter/accepting a line), - # then we also want to expand aliases if the cursor/point is *on* the word instead of just *after* the word. - if [[ -z $self_insertion ]]; then - line_from_point=${line:point} - trailing_part_of_word=${line_from_point%%["${_bash_meta_chars[*]}"]*} - # Move point forward to right after the word. Keep mark where it is. - point=$((point+${#trailing_part_of_word})) - fi - declare -r point line_until_point=${line::point} - - if - # Check if the self-insertion character is a shell metacharacter, except tab and newline (we don't want - # interactive alias expansion on tab and newline is represented as the empty self-insertion character) - case "$self_insertion" in - (["${_bash_meta_chars_without_tab_and_nl[*]}"]|'') ;; - (*) false - esac && - - # Check if the character at point is a shell metacharacter or EOL - # [[ $point -eq ${#line} || "${line:point:1}" = ["${_bash_meta_chars[*]}"] ]] && - # NOTE: Above line commented out, it's actually not that useful. - - # Determine word to complete by stripping leading shell metacharacters - # Uses the same word separators as bash (| & ; ( ) space tab newline) (aka "shell metacharacters") - # But < and > are ignored, since they always expect a file/delimiter/string afterwards and not a command - declare -r word_at_point=${line_until_point##*["${_bash_meta_chars_without_left_right_angle_brackets[*]}"]} && - - # Ensure that word does not contain either < or > - [[ ! $word_at_point =~ [\<\>] ]] && - - # Select replacement (if any) - declare replacement=${BASH_ALIASES["${word_at_point:-__nonexistent}"]-} && - - # Account for file extension aliases. Normal aliases take precedence - declare file_extension_replacement=${BASH_ALIASES[".${word_at_point##*.}"]-} && - [[ $replacement ]] || - { # The logic below preserved trailing exclamation points (`!`) or dollar signs (`$`) at the end, - # which are special characters used for evaluating aliases. - replacement=${file_extension_replacement:+${file_extension_replacement%[$\!]} $word_at_point${file_extension_replacement: -1}} && - replacement=${replacement%[^$\!]} - } && - - [[ $replacement ]] && - - # Determine if it's the first word of a simple command, unless we're expanding a global alias (prefix: "*" or "$"). - # Uses the same command separators as bash (|| & && ; ;; ;& ;;& ( ) | |& newline) (aka "control operators"). - # Due to complexity and performance reasons, we cannot reliably know - # whether the cursor position (aka "point" in readline terms) is in a quoted/escaped - # shell context or not (we would have to implement a full-blown bash parser) or - # whether there are any redirections/assignment words before the first word. - # The only heuristic we try is to check whether we're at the beginning of the line or if - # there's a control operator immediately to the left of the word (ignoring whitespace) - # and that there's no character/metacharacter prefixing the control operator - # s.t. there's a syntax error. We also do some rudimentary redirection and assignment word - # checking (including quoting) after the control operator. If the syntax is invalid (as far as - # the regex can tell), then we bail and no alias expansion will happen. - [[ ${replacement::1} =~ [*$] || ${line_until_point%"$word_at_point"} =~ $is_first_word_prefix$ ]]; then - - # Prevent infinite recursion - _expanded_aliases[$word_at_point]=1 - - # Account for inhibited self-insertion - [[ ${replacement::1} =~ [-$] ]] && self_insertion= - - # Strip special flags (or space) from the beginning of the replacement - replacement=${replacement#[*$ -]} - - # Account for Post Evaluating Aliases - # shellcheck disable=SC2015 - [[ ${replacement: -1} = "!" ]] && declare -r eval_expansion=true || declare -r eval_expansion=false - - # Account for and potentially perform Pre Evaluating Alias expansion - [[ ${replacement: -1} = "$" ]] && replacement=$(eval "${replacement%$}") # TODO use bash 5.3 `${}` command substitution syntax to avoid subshells - - # Recursively expand replacement if we haven't expanded it already - declare tmp=" $replacement" # Prepend space, makes the logic easier - [[ $tmp =~ ^([[:blank:]]*$is_first_word_prefix) ]] && tmp=${tmp#"${BASH_REMATCH[1]}"} # Strip matching prefix - declare -r word_to_expand=${tmp%%["${_bash_meta_chars[*]}"]*} - if [[ -z ${_expanded_aliases[$word_to_expand]-} ]]; then - declare -r point_for_replacement=$((${#BASH_REMATCH[1]}+${#word_to_expand})) - declare -g "ret_$level" - declare -n recursively_expanded_replacement="ret_$level" - _try_abbrev_internal " $replacement" "$point_for_replacement" "" "ret_$level" "_" "_" "" - replacement=${recursively_expanded_replacement# } # Strip the prepended space again - fi - # Perform Post Evaluating Alias expansion - "$eval_expansion" && replacement=$(eval "${replacement%!}") # TODO use bash 5.3 `${}` command substitution syntax to avoid subshells - - # Strip trailing space that might've been added to prevent evaluating aliases from being recognized - replacement=${replacement% } - - # Calculate point and mark offset. - declare -ir offset=${#replacement}-${#word_at_point}+${#self_insertion} - - # Update line. - eval "$new_line"'=${line_until_point%"$word_at_point"}$replacement${self_insertion}${line:point}' - else - # shellcheck disable=SC2034 - declare -ir offset=${#self_insertion} - - # Self-insert by default - eval "$new_line"'=${line_until_point}${self_insertion}${line:point}' - fi - - # Advance mark if it's set and right of (or on) the point. - [[ $mark && mark -gt point ]] && eval "$new_mark"'=$((mark+offset))' - [[ -v "$new_mark" ]] || eval "$new_mark"'=$mark' - - # Advance point. - eval "$new_point"'=$((point+offset))' - # NB: READLINE_ARGUMENT is deliberately ignored, as it doesn't make sense for interactive alias expansion. -} - -_try_abbrev () { - # For use within bash's bind -x builtin. - # Perform interactive alias expansion on the word left to the cursor (no quote parsing) and also *on* the cursor if the - # self-insertion character is empty (which represents pressing enter). - # Takes a single argument which is the character to self-insert (usually space or another shell metacharacter, or empty for newline/enter). - - # To prevent infinite recursion, we need to keep track - # of which aliases have been expanded. This function re-initializes the global associative array (used as a hash set) - declare -gA _expanded_aliases=() - - # Calls the implementation and passes necessary arguments - _try_abbrev_internal "$READLINE_LINE" "$READLINE_POINT" "$READLINE_MARK" READLINE_LINE READLINE_POINT READLINE_MARK "$@" - - # Don't pollute global namespace - declare -gA _expanded_aliases=() -} - -bash_abbr () { - # IFS must be null so "${var[*]}" concatenates without space - declare -r IFS= - - # From the bash man page: - # metacharacter - # A character that, when unquoted, separates words. One of the following: - # | & ; ( ) < > space tab newline - # Note: double quote should be escaped, if added to the list of metacharacters - declare ch - for ch in "${_bash_meta_chars_without_tab_and_nl[@]}"; do - bind_if -x '"\e\C-a'"$ch"'": _try_abbrev "'"$ch"'"' - bind_if '"'"$ch"'": "\e\C-a'"$ch"'"' - # bind_if '"'"$ch"'": "\e\C-a'"$ch"'\e^"' # TODO fix. Moves point weirdly atm. - done - - # TODO To get it fully working in all edge cases (as far as we can parse), - # we need to implement recursive alias expansion on the entire line - # like bash does. Basically something like alias-expand-line but - # called as many times as possible before recursion would happen. - # This is pretty hard, and surely even harder to do efficiently, - # so it will probably never be implemented. - bind_if -x '"\e\C-an": _try_abbrev ""' - bind_if '"\e\C-j": accept-line' - bind_if '"\C-m": "\e\C-an\e^\e\C-j"' - bind_if '"\C-j": "\C-m"' -} - -has_line_editing () { - shopt -qo emacs || shopt -qo vi -} - -bind_if () { - has_line_editing && builtin bind "$@" || true -} - -bash_abbr - -# If the plugin is enabled, we usually don't want regular alias expansion, -# since it can lead to undesirable "double expansions" if an alias is defined recursively. -# fish solves this by treating aliases differently from abbreviations. -# This, however, leads to the invonvenience of defining aliases/abbrevations twice, -# or give up one of the two. This plugin prefers the disabling of normal aliases, since -# it's more transparent to the user. The final command line is exactly what will be -# executed, no hidden aliases that might add hidden flags to some of your commands. -[[ $(type -t bash_abbr) = function ]] && shopt -u expand_aliases # TODO use bash 5.3 `${}` command substitution syntax to avoid subshells diff --git a/tests b/tests deleted file mode 100755 index bc98b78..0000000 --- a/tests +++ /dev/null @@ -1,271 +0,0 @@ -#!/usr/bin/env bash - -printf_if_clr () { - [[ -v NO_COLOR ]] && return - [[ -v FORCE_COLOR ]] || [[ -v CLIFORCE_COLOR ]] && { printf "$@"; return; } - [[ -v CLICOLOR ]] || [[ -v TERM ]] && [[ -t 1 ]] && { printf "$@"; return; } - - # Fallback behavior - [[ -t 1 ]] && printf "$@" - -} -log () { - clr=$1 - shift - case "$clr" in - (white) printf_if_clr '\033[97m' ;; - (grey) printf_if_clr '\033[90m' ;; - (green) printf_if_clr '\033[92m' ;; - (red) printf_if_clr '\033[91m' ;; - (*) printf_if_clr '\033[m' - esac - printf "$@" - printf_if_clr '\033[m' -} -white () { log white "$@"; } -grey () { log grey "$@"; } -green () { log green "$@"; } -red () { log red "$@"; } - -init () { - white '## Unit tests for bash-abbr ##\n' - white '# Running on bash version %s system type %s\n' "${BASH_VERSION@Q}" "${MACHTYPE@Q}" - . bash-abbr || { red 'Failed to source bash-abbr\n'; exit 1; } - unalias -a - declare -gi tests_passed=0 tests_failed=0 tests_total=0 - [[ $1 ]] && declare -gi isolate_test=$1 || declare -gi isolate_test=0 -} - -# inherits all variables from test_abbr function -check () { - [[ ! $new_test_line = "$expected_line" ]] && { red 'Test %s "%s" failed: Line %s did not expand to expected line %s, but %s\n' "$tests_total" "$test_name" "${test_line@Q}" "${expected_line@Q}" "${new_test_line@Q}"; return 1; } - [[ ! $new_test_point = "$expected_point" ]] && { red 'Test %s "%s" failed: Point was %s and did not update to %s, but %s\n' "$tests_total" "$test_name" "${test_point@Q}" "${expected_point@Q}" "${new_test_point@Q}"; return 2; } - [[ ! $new_test_mark = "$expected_mark" ]] && { red 'Test %s "%s" failed: Mark was %s and did not update to %s, but %s\n' "$tests_total" "$test_name" "${test_mark@Q}" "${expected_mark@Q}" "${new_test_mark@Q}"; return 3; } - return 0; -} - -test_abbr () { - tests_total+=1 - [[ isolate_test -gt 0 && isolate_test -ne tests_total ]] && return - - declare test_name=$1 - declare test_line=$2 new_test_line - declare test_point=$3 new_test_point - declare test_mark=$4 new_test_mark - declare expected_line=$5 - declare expected_point=$6 - declare expected_mark=$7 - declare insertion_char=$8 - shift 8 - - unalias -a - alias "$@" - - declare -gA _expanded_aliases=() - _try_abbrev_internal "$test_line" "$test_point" "$test_mark" new_test_line new_test_point new_test_mark "$insertion_char" - unset -v _expanded_aliases - - check && tests_passed+=1 || tests_failed+=1 -} - -summary () { - white 'Total number of tests: %q\n' "$tests_total" - grey 'Tests skipped: %q\n' "$((tests_total - tests_passed - tests_failed))" - green 'Tests passed: %q\n' "$tests_passed" - red 'Tests failed: %q\n' "$tests_failed" -} - -init "$@" - -# empty insertion char means a newline (or a similar accept-line command) was used - -test_abbr "expand regular alias when followed by a space at EOL" 'foo' '3' '0' 'barr ' '5' '0' ' ' 'foo=barr' -test_abbr "expand regular alias when followed by a tab at EOL" 'foo' '3' '0' 'barr ' '5' '0' ' ' 'foo=barr' -test_abbr "expand regular alias when followed by a newline at EOL" 'foo' '3' '0' 'barr' '4' '0' '' 'foo=barr' -test_abbr "expand regular alias when followed by a ')' at EOL" 'foo' '3' '0' 'barr ' '5' '0' ' ' 'foo=barr' -test_abbr "expand regular alias when followed by a '(' at EOL" 'foo' '3' '0' 'barr ' '5' '0' ' ' 'foo=barr' -test_abbr "expand regular alias when followed by a '<' at EOL" 'foo' '3' '0' 'barr ' '5' '0' ' ' 'foo=barr' -test_abbr "expand regular alias when followed by a '>' at EOL" 'foo' '3' '0' 'barr ' '5' '0' ' ' 'foo=barr' -test_abbr "expand regular alias when followed by a '&' at EOL" 'foo' '3' '0' 'barr ' '5' '0' ' ' 'foo=barr' -test_abbr "expand regular alias when followed by a ';' at EOL" 'foo' '3' '0' 'barr ' '5' '0' ' ' 'foo=barr' - -test_abbr "don't expand regular alias when followed by a ']' at EOL" 'foo' '3' '0' 'foo]' '4' '0' ']' 'foo=barr' -test_abbr "don't expand regular alias when followed by a '[' at EOL" 'foo' '3' '0' 'foo[' '4' '0' '[' 'foo=barr' -test_abbr "don't expand regular alias when followed by a '}' at EOL" 'foo' '3' '0' 'foo}' '4' '0' '}' 'foo=barr' -test_abbr "don't expand regular alias when followed by a '.' at EOL" 'foo' '3' '0' 'foo.' '4' '0' '.' 'foo=barr' -test_abbr "don't expand regular alias when followed by a 'b' at EOL" 'foo' '3' '0' 'foob' '4' '0' 'b' 'foo=barr' - -test_abbr "expand regular alias when followed by a space inside word" 'foobar' '3' '0' 'barr bar' '5' '0' ' ' 'foo=barr' -test_abbr "expand regular alias when followed by a tab inside word" 'foobar' '3' '0' 'barr bar' '5' '0' ' ' 'foo=barr' -test_abbr "expand regular alias when followed by a ) inside word" 'foobar' '3' '0' 'barr bar' '5' '0' ' ' 'foo=barr' -test_abbr "expand regular alias when followed by a ( inside word" 'foobar' '3' '0' 'barr bar' '5' '0' ' ' 'foo=barr' -test_abbr "expand regular alias when followed by a < inside word" 'foobar' '3' '0' 'barr bar' '5' '0' ' ' 'foo=barr' -test_abbr "expand regular alias when followed by a > inside word" 'foobar' '3' '0' 'barr bar' '5' '0' ' ' 'foo=barr' -test_abbr "expand regular alias when followed by a & inside word" 'foobar' '3' '0' 'barr bar' '5' '0' ' ' 'foo=barr' -test_abbr "expand regular alias when followed by a ; inside word" 'foobar' '3' '0' 'barr bar' '5' '0' ' ' 'foo=barr' - -test_abbr "don't expand regular alias when followed by a newline inside word" 'foobar' '3' '0' 'foobar' '6' '0' '' 'foo=barr' - -test_abbr "don't expand regular alias when space is entered at the beginning" 'foo' '0' '0' ' foo' '1' '0' ' ' 'foo=barr' -test_abbr "don't expand regular alias when space is entered after 1st character inside alias" 'foo' '1' '0' 'f oo' '2' '0' ' ' 'foo=barr' -test_abbr "don't expand regular alias when space is entered before last character inside alias" 'foo' '2' '0' 'fo o' '3' '0' ' ' 'foo=barr' - -test_abbr "expand regular alias when newline is entered at the beginning" 'foo' '0' '0' 'barr' '4' '0' '' 'foo=barr' -test_abbr "expand regular alias when newline is entered after 1st character inside alias" 'foo' '1' '0' 'barr' '4' '0' '' 'foo=barr' -test_abbr "expand regular alias when newline is entered before last character inside alias" 'foo' '2' '0' 'barr' '4' '0' '' 'foo=barr' - -test_abbr "recursively expand regular alias once" 'foo' '3' '0' 'bazqux ' '7' '0' ' ' 'foo=bar' 'bar=bazqux' -test_abbr "recursively expand regular alias twice" 'foo' '3' '0' 'wowitworks ' '11' '0' ' ' 'foo=bar' 'bar=bazqux' 'bazqux=wowitworks' -test_abbr "recursively expand regular alias thrice with a shorter final alias" 'foo' '3' '0' 'shorter ' '8' '0' ' ' 'foo=bar' 'bar=bazqux' 'bazqux=wowitworks' 'wowitworks=shorter' - -test_abbr "don't recursively expand regular alias infinitely (0 indirections)" 'foo' '3' '0' 'foo ' '4' '0' ' ' 'foo=foo' -test_abbr "don't recursively expand regular alias infinitely (1 indirection)" 'foo' '3' '0' 'foo ' '4' '0' ' ' 'foo=bar' 'bar=foo' -test_abbr "don't recursively expand regular alias infinitely (2 indirections)" 'foo' '3' '0' 'foo ' '4' '0' ' ' 'foo=bar' 'bar=baz' 'baz=foo' -test_abbr "don't recursively expand regular alias infinitely (3 indirections)" 'foo' '3' '0' 'foo ' '4' '0' ' ' 'foo=bar' 'bar=baz' 'baz=qux' 'qux=foo' - -test_abbr "don't recursively expand regular alias infinitely (1 indirection, different lengths)" 'foo' '3' '0' 'foo ' '4' '0' ' ' 'foo=b' 'b=foo' -test_abbr "don't recursively expand regular alias infinitely (2 indirections, different lengths)" 'foo' '3' '0' 'foo ' '4' '0' ' ' 'foo=ba' 'ba=bazz' 'bazz=foo' -test_abbr "don't recursively expand regular alias infinitely (3 indirections, different lengths)" 'foo' '3' '0' 'foo ' '4' '0' ' ' 'foo=barst' 'barst=b' 'b=quxio' 'quxio=foo' - -# Note that there are two spaces after the `foo=` because a single spaces is used as an escape mechanism for different alias flags like * or $. See later test cases. -test_abbr "recursively expand regular alias when alias value has leading spaces before the first command word" 'foo' '3' '0' ' cool ' '7' '0' ' ' 'foo= bar' 'bar=cool' -test_abbr "recursively expand regular alias when alias value has leading tabs before the first command word" 'foo' '3' '0' $'\t\tcool ' '7' '0' ' ' $'foo=\t\tbar' 'bar=cool' -test_abbr "recursively expand regular alias when alias value has leading spaces and assignment words before the first command word" 'foo' '3' '0' ' a=1 b=2 cool ' '14' '0' ' ' 'foo= a=1 b=2 bar' 'bar=cool' -test_abbr "recursively expand regular alias when alias value has leading spaces and redirections before the first command word" 'foo' '3' '0' ' 3>/dev/null 2>&1- cool ' '24' '0' ' ' 'foo= 3>/dev/null 2>&1- bar' 'bar=cool' - -test_abbr "recursively expand regular alias when entering a newline mid-word" 'foo' '2' '0' 'cool' '4' '0' '' 'foo=bar' 'bar=cool' - -test_abbr "don't expand regular alias when value is empty" 'foo' '3' '0' 'foo ' '4' '0' ' ' 'foo=' - -test_abbr "expand regular alias when it has a leading assignment word with spaces" 'A=1 foo' '7' '0' 'A=1 barr ' '9' '0' ' ' 'foo=barr' -test_abbr "expand regular alias when it has leading assignment words with spaces" 'A=1 B=2 C=3 foo' '20' '0' 'A=1 B=2 C=3 barr ' '22' '0' ' ' 'foo=barr' -test_abbr "expand regular alias when it has leading assignment words with spaces and tabs" $'A=1 \t B=2\t C=3\tfoo' '19' '0' $'A=1 \t B=2\t C=3\tbarr ' '21' '0' ' ' 'foo=barr' -test_abbr "expand regular alias when it has a leading null assignment word" 'A= foo' '6' '0' 'A= barr ' '8' '0' ' ' 'foo=barr' -test_abbr "expand regular alias when it has two leading null assignment words" 'A= B= foo' '9' '0' 'A= B= barr ' '11' '0' ' ' 'foo=barr' - -test_abbr "expand regular alias when it has a leading single-quoted assignment word without spaces" "A='whatever' foo" '16' '0' "A='whatever' barr " '18' '0' ' ' 'foo=barr' -test_abbr "expand regular alias when it has a leading single-quoted assignment word with spaces" "A='what ever' foo" '17' '0' "A='what ever' barr " '19' '0' ' ' 'foo=barr' -test_abbr "expand regular alias when it has a leading dollar single-quoted assignment word with spaces" "A=$'what ever' foo" '18' '0' "A=$'what ever' barr " '20' '0' ' ' 'foo=barr' - -test_abbr "expand regular alias when it has a leading double-quoted assignment word without spaces" 'A="whatever" foo' '16' '0' 'A="whatever" barr ' '18' '0' ' ' 'foo=barr' -test_abbr "expand regular alias when it has a leading double-quoted assignment word with spaces" 'A="what ever" foo' '17' '0' 'A="what ever" barr ' '19' '0' ' ' 'foo=barr' -test_abbr "expand regular alias when it has a leading double-quoted assignment word with spaces and an escaped double quote" 'A="what \"ever" foo' '19' '0' 'A="what \"ever" barr ' '21' '0' ' ' 'foo=barr' -test_abbr "expand regular alias when it has a leading dollar double-quoted assignment word with spaces" 'A=$"what ever" foo' '18' '0' 'A=$"what ever" barr ' '20' '0' ' ' 'foo=barr' - -test_abbr "expand regular alias when it has a leading assignment word with backtick command substitution with spaces" 'A=`what ever` foo' '17' '0' 'A=`what ever` barr ' '19' '0' ' ' 'foo=barr' -test_abbr "expand regular alias when it has a leading assignment word with normal command substitution with spaces" 'A=$(echo hi; arbitrary commands; no nesting allowed!; literally anyhing '\'' " > @ [ ] { } ;) foo' '97' '0' 'A=$(echo hi; arbitrary commands; no nesting allowed!; literally anyhing '\'' " > @ [ ] { } ;) barr ' '99' '0' ' ' 'foo=barr' -test_abbr "expand regular alias when it has a leading assignment word with deprecated arithmetic expansion with spaces" 'A=$[1 + 1, 2 ** 4#2] foo' '24' '0' 'A=$[1 + 1, 2 ** 4#2] barr ' '26' '0' ' ' 'foo=barr' -test_abbr "expand regular alias when it has a leading assignment word with normal arithmetic expansion with spaces" 'A=$((1 + 1, 2 ** 4#2)) foo' '26' '0' 'A=$((1 + 1, 2 ** 4#2)) barr ' '28' '0' ' ' 'foo=barr' -test_abbr "expand regular alias when it has a leading assignment word with simple variable expansion" 'A=$FOO foo' '10' '0' 'A=$FOO barr ' '12' '0' ' ' 'foo=barr' -test_abbr "expand regular alias when it has a leading assignment word with special variable expansion" 'A=$- foo' '8' '0' 'A=$- barr ' '10' '0' ' ' 'foo=barr' -test_abbr "expand regular alias when it has a leading assignment word with simple braced variable expansion" 'A=${FOO} foo' '12' '0' 'A=${FOO} barr ' '14' '0' ' ' 'foo=barr' -test_abbr "expand regular alias when it has a leading assignment word with special braced variable expansion" 'A=${-} foo' '12' '0' 'A=${-} barr ' '14' '0' ' ' 'foo=barr' -test_abbr "expand regular alias when it has a leading assignment word with advanced braced variable expansion" 'A=${FOO: -1:2} foo' '18' '0' 'A=${FOO: -1:2} barr ' '20' '0' ' ' 'foo=barr' -test_abbr "expand regular alias when it has a leading assignment word with advanced special braced variable expansion" 'A=${-: -1:2} foo' '16' '0' 'A=${-: -1:2} barr ' '18' '0' ' ' 'foo=barr' -test_abbr "expand regular alias when it has a leading assignment word with process substitution with spaces" 'A=<(echo bla) foo' '17' '0' 'A=<(echo bla) barr ' '19' '0' ' ' 'foo=barr' -test_abbr "expand regular alias when it has a leading backslash quoted assignment word with spaces" 'A=what\ ever foo' '16' '0' 'A=what\ ever barr ' '18' '0' ' ' 'foo=barr' - -test_abbr "expand regular alias when it has multiple leading complex assignment words separated by multiple spaces or tabs" \ - $'_A= B=2 \t \t C=<(echo bla; whatever)test$(bla bla bla)` hey`\'quoted string\'"another \\"one\\""$(( 1+1 ))$[ 2**2 ]$\'and another one\'$"translated \\"string"\\.\\ \\c\\o\\o\\l$foo${bar}${baz%%hey}$-$1${10}\tD=cool foo' '207' '0' \ - $'_A= B=2 \t \t C=<(echo bla; whatever)test$(bla bla bla)` hey`\'quoted string\'"another \\"one\\""$(( 1+1 ))$[ 2**2 ]$\'and another one\'$"translated \\"string"\\.\\ \\c\\o\\o\\l$foo${bar}${baz%%hey}$-$1${10}\tD=cool barr ' '209' '0' \ - ' ' 'foo=barr' - -test_abbr "don't expand regular alias when there's an invalid assignment word (0what=1)" '0what=1 foo' '11' '0' '0what=1 foo ' '12' '0' ' ' 'foo=barr' -test_abbr "don't expand regular alias when there's an invalid assignment word (what=\$[[hey]])" 'what=$[[hey]] foo' '17' '0' 'what=$[[hey]] foo ' '18' '0' ' ' 'foo=barr' - -test_abbr "expand regular alias when it has a leading output redirection" '>/dev/null foo' '14' '0' '>/dev/null barr ' '16' '0' ' ' 'foo=barr' -test_abbr "expand regular alias when it has a leading output redirection with tabs and spaces" $'> \t /dev/null foo' '17' '0' $'> \t /dev/null barr ' '19' '0' ' ' 'foo=barr' -test_abbr "expand regular alias when it has a leading input redirection" '>hey foo' '9' '0' '>>hey barr ' '11' '0' ' ' 'foo=barr' -test_abbr "expand regular alias when it has a leading noclobber output redirection" '>|hey foo' '9' '0' '>|hey barr ' '11' '0' ' ' 'foo=barr' -test_abbr "expand regular alias when it has a leading output redirection with fd" '5>hey foo' '9' '0' '5>hey barr ' '11' '0' ' ' 'foo=barr' -test_abbr "expand regular alias when it has a leading output redirection with a {varname}" '{bla}>hey foo' '13' '0' '{bla}>hey barr ' '15' '0' ' ' 'foo=barr' -test_abbr "expand regular alias when it has a leading read-write redirection" '<>file foo' '10' '0' '<>file barr ' '12' '0' ' ' 'foo=barr' -test_abbr "expand regular alias when it has a leading file descriptor close redirection" '>&- foo' '7' '0' '>&- barr ' '9' '0' ' ' 'foo=barr' -test_abbr "expand regular alias when it has a leading file descriptor move redirection" '>&5- foo' '8' '0' '>&5- barr ' '10' '0' ' ' 'foo=barr' -test_abbr "expand regular alias when it has a leading file descriptor move redirection with fd" '6>&5- foo' '9' '0' '6>&5- barr ' '11' '0' ' ' 'foo=barr' - -test_abbr "expand regular alias when it has multiple leading redirections" '>/dev/null 2>&1 2>/dev/null 3<>file 3>&4- foo' '45' '0' '>/dev/null 2>&1 2>/dev/null 3<>file 3>&4- barr ' '47' '0' ' ' 'foo=barr' - -test_abbr "expand regular alias when it has multiple leading complex redirections separated by multiple spaces or tabs" \ - $'<|../log &- 3<&- 4>&1- >/dev/null 2>&3 \t \t 5<><(echo bla; whatever)test$(bla bla bla)` hey`\'quoted string\'"another \\"one\\""$(( 1+1 ))$[ 2**2 ]$\'and another one\'$"translated \\"string"\\.\\ \\c\\o\\o\\l$foo${bar}${baz%%hey}$-$1${10}\t<<|../log &- 3<&- 4>&1- >/dev/null 2>&3 \t \t 5<><(echo bla; whatever)test$(bla bla bla)` hey`\'quoted string\'"another \\"one\\""$(( 1+1 ))$[ 2**2 ]$\'and another one\'$"translated \\"string"\\.\\ \\c\\o\\o\\l$foo${bar}${baz%%hey}$-$1${10}\t<</dev/null A=1 1>&3- <>file \t _B=$(hello world)"cool"$VAR << <(test) foo' '74' '0' \ - $' >/dev/null A=1 1>&3- <>file \t _B=$(hello world)"cool"$VAR << <(test) barr ' '76' '0' \ - ' ' 'foo=barr' - -test_abbr "don't expand regular alias when there's an invalid redirection operator (6<>&5-)" '6<>&5- foo' '10' '0' '6<>&5- foo ' '11' '0' ' ' 'foo=barr' -test_abbr "don't expand regular alias when there's an invalid redirection operator (>>>)" '>>> foo' '7' '0' '>>> foo ' '8' '0' ' ' 'foo=barr' -test_abbr "don't expand regular alias when there's an invalid redirection operator ({0fail}>file)" '{0fail}>file foo' '16' '0' '{0fail}>file foo ' '17' '0' ' ' 'foo=barr' - -test_abbr "expand regular alias when it has one leading control operator (&&)" ' && foo' '7' '0' ' && barr ' '9' '0' ' ' 'foo=barr' -test_abbr "expand regular alias when it has one leading control operator (||)" ' || foo' '7' '0' ' || barr ' '9' '0' ' ' 'foo=barr' -test_abbr "expand regular alias when it has one leading control operator (|)" ' | foo' '6' '0' ' | barr ' '8' '0' ' ' 'foo=barr' -test_abbr "expand regular alias when it has one leading control operator (;)" ' ; foo' '6' '0' ' ; barr ' '8' '0' ' ' 'foo=barr' -test_abbr "expand regular alias when it has one leading control operator (;;)" ' ;; foo' '7' '0' ' ;; barr ' '9' '0' ' ' 'foo=barr' -test_abbr "expand regular alias when it has one leading control operator (;&)" ' ;& foo' '7' '0' ' ;& barr ' '9' '0' ' ' 'foo=barr' -test_abbr "expand regular alias when it has one leading control operator (;;&)" ' ;;& foo' '8' '0' ' ;;& barr ' '10' '0' ' ' 'foo=barr' -test_abbr "expand regular alias when it has one leading control operator (&)" ' & foo' '6' '0' ' & barr ' '8' '0' ' ' 'foo=barr' -test_abbr "expand regular alias when it has one leading control operator (newline)" $'\nfoo' '4' '0' $'\nbarr ' '6' '0' ' ' 'foo=barr' -test_abbr "expand regular alias when it has one leading control operator (opening parenthesis)" '(foo' '4' '0' '(barr ' '6' '0' ' ' 'foo=barr' - -test_abbr "don't expand regular alias when there's a closing parenthesis" ' ) foo' '7' '0' ' ) foo ' '8' '0' ' ' 'foo=barr' - -test_abbr "expand regular alias when it has two leading control operators" ' && echo hi && foo' '18' '0' ' && echo hi && barr ' '20' '0' ' ' 'foo=barr' -test_abbr "expand regular alias when it has two leading and one trailing control operator(s)" ' && echo hi (foo&& okay)' '16' '0' ' && echo hi (barr && okay)' '18' '0' ' ' 'foo=barr' - -test_abbr "expand regular alias when it has leading and trailing control operators, assignment words and redirections" $'\necho hi&&a=1 echo ho || 2>/dev/null echo bla & {var}<>file another ;;& a=1 2>/dev/null 3>&4- foo; >/dev/null cool <>file' '97' '0' $'\necho hi&&a=1 echo ho || 2>/dev/null echo bla & {var}<>file another ;;& a=1 2>/dev/null 3>&4- barr ; >/dev/null cool <>file' '99' '0' ' ' 'foo=barr' - -test_abbr "don't expand regular alias when it's not the first word" 'hey foo' '7' '0' 'hey foo ' '8' '0' ' ' 'foo=barr' - -test_abbr "don't expand regular alias when there's a double quoted ';' before it" 'echo ";" foo' '12' '0' 'echo ";" foo ' '13' '0' ' ' 'foo=barr' -test_abbr "don't expand regular alias when there is 1 backslash before a ';' before the alias" 'echo \; foo' '11' '0' 'echo \; foo ' '12' '0' ' ' 'foo=barr' -test_abbr "expand regular alias when there are 2 backslashes before a ';' before the alias" 'echo \\; foo' '12' '0' 'echo \\; barr ' '14' '0' ' ' 'foo=barr' -test_abbr "don't expand regular alias when there are 3 backslashes before a ';' before the alias" 'echo \\\; foo' '13' '0' 'echo \\\; foo ' '14' '0' ' ' 'foo=barr' -test_abbr "expand regular alias when there are 4 backslashes before a ';' before the alias" 'echo \\\\; foo' '14' '0' 'echo \\\\; barr ' '16' '0' ' ' 'foo=barr' -test_abbr "don't expand regular alias when there are 5 backslashes before a ';' before the alias" 'echo \\\\\; foo' '15' '0' 'echo \\\\\; foo ' '16' '0' ' ' 'foo=barr' -test_abbr "expand regular alias when there are 6 backslashes before a ';' before the alias" 'echo \\\\\\; foo' '16' '0' 'echo \\\\\\; barr ' '18' '0' ' ' 'foo=barr' - -test_abbr "don't expand regular alias when there is 1 backslash before a & before the alias" 'echo \& foo' '11' '0' 'echo \& foo ' '12' '0' ' ' 'foo=barr' -test_abbr "expand regular alias when there are 2 backslashes before a & before the alias" 'echo \\& foo' '12' '0' 'echo \\& barr ' '14' '0' ' ' 'foo=barr' -test_abbr "don't expand regular alias when there are 3 backslashes before a & before the alias" 'echo \\\& foo' '13' '0' 'echo \\\& foo ' '14' '0' ' ' 'foo=barr' - -test_abbr "don't expand regular alias when there is 1 backslash before a | before the alias" 'echo \| foo' '11' '0' 'echo \| foo ' '12' '0' ' ' 'foo=barr' -test_abbr "expand regular alias when there are 2 backslashes before a | before the alias" 'echo \\| foo' '12' '0' 'echo \\| barr ' '14' '0' ' ' 'foo=barr' -test_abbr "don't expand regular alias when there are 3 backslashes before a | before the alias" 'echo \\\| foo' '13' '0' 'echo \\\| foo ' '14' '0' ' ' 'foo=barr' - -test_abbr "expand regular alias when there is 1 backslash before a ||" 'echo \|| foo' '12' '0' 'echo \|| barr ' '14' '0' ' ' 'foo=barr' -test_abbr "expand regular alias when there are 2 backslashes before a ||" 'echo \\|| foo' '13' '0' 'echo \\|| barr ' '15' '0' ' ' 'foo=barr' -test_abbr "expand regular alias when there are 3 backslashes before a ||" 'echo \\\|| foo' '14' '0' 'echo \\\|| barr ' '16' '0' ' ' 'foo=barr' - -test_abbr "expand regular alias when there is 1 backslash before a &&" 'echo \&& foo' '12' '0' 'echo \&& barr ' '14' '0' ' ' 'foo=barr' -test_abbr "expand regular alias when there are 2 backslashes before a &&" 'echo \\&& foo' '13' '0' 'echo \\&& barr ' '15' '0' ' ' 'foo=barr' -test_abbr "expand regular alias when there are 3 backslashes before a &&" 'echo \\\&& foo' '14' '0' 'echo \\\&& barr ' '16' '0' ' ' 'foo=barr' - -test_abbr "expand regular alias when there is 1 backslash before a ;;" 'echo \;; foo' '12' '0' 'echo \;; barr ' '14' '0' ' ' 'foo=barr' -test_abbr "expand regular alias when there are 2 backslashes before a ;;" 'echo \\;; foo' '13' '0' 'echo \\;; barr ' '15' '0' ' ' 'foo=barr' -test_abbr "expand regular alias when there are 3 backslashes before a ;;" 'echo \\\;; foo' '14' '0' 'echo \\\;; barr ' '16' '0' ' ' 'foo=barr' - -test_abbr "expand regular alias when there is 1 backslash before a ;;&" 'echo \;;& foo' '13' '0' 'echo \;;& barr ' '15' '0' ' ' 'foo=barr' -test_abbr "expand regular alias when there are 2 backslashes before a ;;&" 'echo \\;;& foo' '14' '0' 'echo \\;;& barr ' '16' '0' ' ' 'foo=barr' -test_abbr "expand regular alias when there are 3 backslashes before a ;;&" 'echo \\\;;& foo' '15' '0' 'echo \\\;;& barr ' '17' '0' ' ' 'foo=barr' - -test_abbr "expand regular alias when there is 1 backslash before a ;&" 'echo \;& foo' '12' '0' 'echo \;& barr ' '14' '0' ' ' 'foo=barr' -test_abbr "expand regular alias when there are 2 backslashes before a ;&" 'echo \\;& foo' '13' '0' 'echo \\;& barr ' '15' '0' ' ' 'foo=barr' -test_abbr "expand regular alias when there are 3 backslashes before a ;&" 'echo \\\;& foo' '14' '0' 'echo \\\;& barr ' '16' '0' ' ' 'foo=barr' - -test_abbr "don't expand regular alias when there is 1 backslash before a (" 'echo \( foo' '11' '0' 'echo \( foo ' '12' '0' ' ' 'foo=barr' -test_abbr "expand regular alias when there are 2 backslashes before a (" 'echo \\( foo' '12' '0' 'echo \\( barr ' '14' '0' ' ' 'foo=barr' -test_abbr "expand regular alias when there are 3 backslashes before a (" 'echo \\\( foo' '13' '0' 'echo \\\( foo ' '14' '0' ' ' 'foo=barr' - -test_abbr "don't expand regular alias when newline is entered not after or on the alias" 'foo ' '4' '0' 'foo ' '4' '0' '' 'foo=bar' -test_abbr "don't expand regular alias when space is entered not after or on the alias" 'foo ' '4' '0' 'foo ' '5' '0' ' ' 'foo=bar' - -summary