Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 21 additions & 21 deletions .nvim.lua
Original file line number Diff line number Diff line change
@@ -1,24 +1,24 @@

local ok, mason_lspconfig = pcall(require, "mason-lspconfig")
if ok then
mason_lspconfig.setup({
automatic_enable = { exclude = { "shellcheck" } },
})
end

vim.cmd.LspStop()
vim.api.nvim_create_autocmd({ "BufEnter", "BufRead" }, {
pattern = { "*.sh" },
callback = function()
pcall(vim.cmd.LspStop)
end,
})

require("nvim-treesitter.configs").setup({
highlight = {
enable = false, -- false will disable the whole extension
disable = { "sh", "bash" }, -- list of language that will be disabled
},
})
if false then
local ok, mason_lspconfig = pcall(require, "mason-lspconfig")
if ok then
mason_lspconfig.setup({
automatic_enable = { exclude = { "shellcheck" } },
})
end

vim.cmd.LspStop()
vim.api.nvim_create_autocmd({ "BufEnter", "BufRead" }, {
pattern = { "*.sh" },
callback = function()
pcall(vim.cmd.LspStop)
end,
})

require("nvim-treesitter.configs").setup({
highlight = {
enable = false, -- false will disable the whole extension
disable = { "sh", "bash" }, -- list of language that will be disabled
},
})
end
29 changes: 26 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,25 +16,48 @@ Labrador Bash library. Collection of functions and libraries that I deem useful

# Installation

The library is one file. Download the latest release from GitHub and put in your PATH:
## Package Managers

Install L_lib using your preferred Bash package manager:

### Basher
```bash
basher install Kamilcuk/L_lib
```

### bpkg
```bash
bpkg install Kamilcuk/L_lib
```

### Shpkg
```bash
shpkg install Kamilcuk/L_lib
```

## Manual Installation

The library is one file. Download the latest release from GitHub and put in your PATH:

```bash
mkdir -vp ~/.local/bin/
curl -o ~/.local/bin/L_lib.sh https://raw.githubusercontent.com/Kamilcuk/L_lib/refs/heads/v1/bin/L_lib.sh
export PATH=~/.local/bin:$PATH
```

## Usage

You can use the library in scripts with:

```
```bash
. L_lib.sh -s
```

Unless `-n`, sourcing the library will enable `extglob` and `patsub_replacement` and, if `set -e` is set, register a `ERR` trap that will print a nice traceback on unhandled error.

You can test the library ad-hoc:

```
```bash
bash <(curl -sS https://raw.githubusercontent.com/Kamilcuk/L_lib/refs/heads/v1/bin/L_lib.sh) L_setx L_log 'Hello world'
```

Expand Down
247 changes: 245 additions & 2 deletions bin/L_lib.sh
Original file line number Diff line number Diff line change
Expand Up @@ -1968,7 +1968,9 @@ L_var_is_integer() { [[ "$(declare -p "$1" 2>/dev/null || :)" =~ ^declare\ -[A-Z
# @arg $1 variable nameref
L_var_is_exported() { [[ "$(declare -p "$1" 2>/dev/null || :)" =~ ^declare\ -[A-Za-z]*x ]]; }

L_var_to_string_v() {
fi

_L_var_to_string_v_declare() {
L_v=$(LC_ALL=C declare -p "$1") || return 2
# If it is an array or associative array.
if [[ "$L_v" == declare\ -[aA]* && "${L_v#*=}" == \'\(*\)\' ]]; then
Expand All @@ -1982,7 +1984,6 @@ L_var_to_string_v() {
fi
}

fi

# @description Get the namereference variable name that the variable references to.
# If the variable is not a namereference, return 1
Expand Down Expand Up @@ -9627,6 +9628,248 @@ L_proc_terminate() { L_proc_send_signal "$1" SIGTERM; }
# @arg $1 PID from L_proc_popen
L_proc_kill() { L_proc_send_signal "$1" SIGKILL; }

# ]]]
# foreach [[[
# @section foreach

# @description Iterate over elements of an array by assigning it to variables.
#
# Each loop the arguments to the function are REQUIRED to be exactly the same.
#
# The function takes positional arguments in the form:
# - at least one variable name to assign to,
# - followed by a required ':' colon character,
# - followed by at least one array variable to iterate over.
#
# Without -k option:
# - For each array variable:
# - If -s option, sort array keys.
# - For each element in the array:
# - Assign the element to the variables in order.
#
# With -k option:
# - Accumulate all keys of all arrays into a set of keys.
# - If -s option, sort the set.
# - For each value in the set of keys:
# - Assign the values of each array[key] to corresponding variable.
#
# @option -s Output in sorted keys order. Does nothing on non-associative arrays.
# @option -r Output in reverse sorted keys order. Implies -s.
# @option -n <num> Each variable name is repeated as an array variable with indexes from 0 to num-1.
# For example: '-n 3 a : arr' is equal to 'a[0] a[1] a[2] : arr'.
# @option -i <var> Store loop index in specified variable. First loop has index 0.
# @option -v <var> Store state in the variable, instead of picking unique name starting with _L_FOREACH_*.
# @option -k <var> Store key of the first element in specified variable.
# @option -f <var> First loop stores 1 into the variable, otherwise 0 is stored in the variable.
# @option -l <var> Last loop stores 1 into the variable, otherwise 0 is stored in the variable.
# @option -h Print this help and return 0.
# @arg $@ Variable names to assign, followed by : colon character, followed by arrays variables.
# @env _L_FOREACH
# @env _L_FOREACH_[0-9]+
# @return 0 if iteration should be continued,
# 1 on interanl error,
# 2 on usage error,
# 4 if iteration should stop.
# @example
# local array1=(a b c d) array2=(d e f g)
# while L_foreach a : array1; do echo $a; done # a b c d
# while L_foreach a b : array1; do echo $a,$b; done # a,b c,d
# while L_foreach a b : array1 array2; do echo $a,$b; done # a,d b,e c,f g,d
# while L_foreach a b c : array1 array2; do echo $a,$b; done # a,d,b e,c,f g,d,<unset>
# while L_foreach -n 3 a : array1; do echo ${#a[@]},${a[*]},; done # 3,a b c, 1,d,
#
# local -A dict1=([a]=b [c]=d) dict2=([a]=e [c]=f)
# while L_foreach -n 3 a : dict1; do echo ${#a[@]},${a[*]},; done # 2,b d or 2,d b
# # the order of elements is unknown in associative arrays
# while L_foreach -s -k k a b : dict1 dict2; do echo $k,$a,$b; done # a,b,e c,d,f
# while L_foreach -s -k k a b : dict1 dict2; do echo $k,$a,$b; done # a,b,e c,d,f
L_foreach() {
local OPTIND OPTARG OPTERR \
_L_opt_v="" _L_opt_s=0 _L_opt_r=0 _L_opt_n="" _L_opt_i="" _L_opt_v="" _L_opt_k="" _L_opt_f="" _L_opt_l="" \
_L_i IFS=' ' _L_vidx="" \
_L_s_keys _L_s_loopidx=0 _L_s_colon=1 _L_s_arridx=0 _L_s_idx=0
while getopts srn:i:v:k:f:l:h _L_i; do
case "$_L_i" in
s) _L_opt_s=1 ;;
r) _L_opt_r=1 ;;
n) _L_opt_n=$OPTARG ;;
i) _L_opt_i=$OPTARG ;;
v) _L_opt_v=$OPTARG ;;
k) _L_opt_k=$OPTARG ;;
f) _L_opt_f=$OPTARG ;;
l) _L_opt_l=$OPTARG ;;
h) L_func_help; return 0 ;;
*) L_func_error; return 2 ;;
esac
done
shift "$((OPTIND-1))"
# Pick variable name to store state in.
if [[ -z "$_L_opt_v" ]]; then
local _L_context="${BASH_SOURCE[*]}:${BASH_LINENO[*]}:${FUNCNAME[*]}:$*"
# Find the context inside _L_FOREACH array.
if ! L_array_index -v _L_vidx _L_FOREACH "$_L_context"; then
# If not found, add it.
_L_vidx=$(( ${_L_FOREACH[*]:+${#_L_FOREACH[*]}}+0 ))
_L_FOREACH[_L_vidx]=$_L_context
fi
_L_opt_v=_L_FOREACH_$_L_vidx
fi
# Restore variables state.
eval "${!_L_opt_v:-}"
# First run.
if (( _L_s_loopidx == 0 )); then
# Parse arguments. Find position of :.
while (( _L_s_colon <= $# )) && [[ "${!_L_s_colon}" != ":" ]]; do
_L_s_colon=$(( _L_s_colon + 1 ))
done
if (( _L_s_colon > $# )); then
L_panic "Colon ':' not found in the arguments: $*"
fi
# If -k option, accumulate all keys into one set.
if [[ -n "$_L_opt_k" ]]; then
local -n _L_arr
for _L_arr in "${@:_L_s_colon + 1}"; do
for _L_i in "${!_L_arr[@]}"; do
if ! L_array_contains _L_s_keys "$_L_i"; then
_L_s_keys+=("$_L_i")
fi
done
done
if (( _L_opt_r )); then
L_sort_bash -r _L_s_keys
elif (( _L_opt_s )); then
L_sort_bash _L_s_keys
fi
fi
fi
local _L_vars=("${@:1:_L_s_colon - 1}") _L_arrs=("${@:_L_s_colon + 1}")
if (( _L_opt_n > 1 )); then
# If -n options is given, repeat each variable with assignment as an array with indexes.
# _L_vars=(a b) n=3 -> _L_vars=(a[0] a[1] a[2] b[0] b[1] [2])
# shellcheck disable=SC2175
eval eval \''_L_vars=('\' \\\"\\\${_L_vars[{0..$(( ${#_L_vars} - 1))}]}[{0..$(( _L_opt_n - 1 ))}]\\\" \'')'\'
fi
local _L_varslen=${#_L_vars[*]} _L_arrslen=${#_L_arrs[*]}
if [[ -n "$_L_opt_k" ]]; then
if (( _L_s_idx >= ${_L_s_keys[*]:+${#_L_s_keys[*]}}+0 )); then
# Iterated through all the keys.
unset -v "$_L_opt_v" ${_L_vidx:+"_L_FOREACH[$_L_vidx]"}
return 4
fi
local _L_key=${_L_s_keys[_L_s_idx++]}
printf -v "$_L_opt_k" "%s" "$_L_key"
# With -k option, stuff is vertical.
if (( _L_varslen == 1 )); then
# When there is one variable, it is an array with the results.
for (( _L_i = 0; _L_i < _L_arrslen; ++_L_i )); do
local -n _L_arr=${_L_arrs[_L_i]}
if [[ -v _L_arr[$_L_key] ]]; then
printf -v "${_L_vars[_L_i]}" "%s" "${_L_arr[$_L_key]}"
fi
done
else
# Otherwise, extra arrays are just ignored.
for (( _L_i = 0; _L_i < _L_varslen && _L_i < _L_arrslen; ++_L_i )); do
local -n _L_arr=${_L_arrs[_L_i]}
if [[ -v _L_arr[$_L_key] ]]; then
printf -v "${_L_vars[_L_i]}" "%s" "${_L_arr[$_L_key]}"
else
unset -v "${_L_vars[_L_i]}"
fi
done
fi
if [[ -n "$_L_opt_l" ]]; then
printf -v "$_L_opt_l" "%s" "$(( _L_s_idx >= ${#_L_s_keys[*]} ))"
fi
else
# Without -k option, stuff is horizontal.
local _L_varsidx=0
# For each array.
while (( _L_s_arridx < _L_arrslen )); do
local -n _L_arr=${_L_arrs[_L_s_arridx]}
# L_debug "_L_s_idx=${_L_s_idx} arridx=$_L_s_arridx arrslen=$_L_arrslen arrayvar=${_L_arrs[_L_s_arridx]}"
# Sorted array keys are cached. Unsorted are not.
if (( _L_opt_s || _L_opt_r )); then
if (( _L_s_idx == 0 )); then
# Compute keys in the sorted order if requested.
_L_s_keys=("${!_L_arr[@]}")
if L_var_is_associative _L_arr; then
if (( _L_opt_r )); then
L_sort_bash -r _L_s_keys
elif (( _L_opt_s )); then
L_sort_bash _L_s_keys
fi
else
if (( _L_opt_r )); then
L_array_reverse _L_s_keys
fi
fi
local -n _L_keys=_L_s_keys
fi
else
local _L_keys=("${!_L_arr[@]}")
fi
# For each element in the array.
while (( _L_s_idx < ${_L_arr[*]:+${#_L_arr[*]}}+0 )); do
if (( _L_varsidx >= ${#_L_vars[*]} )); then
# L_debug "Assigned all variables from the list. ${_L_varsidx} vars=[${#_L_vars[*]}]"
break 2
fi
# L_debug "Set varsidx=$_L_varsidx var=${_L_vars[_L_varsidx]} val=${_L_arr[${_L_keys[_L_s_idx]}]} key=${_L_keys[_L_s_idx]}"
if [[ -v _L_arr[${_L_keys[_L_s_idx]}] ]]; then
printf -v "${_L_vars[_L_varsidx++]}" "%s" "${_L_arr[${_L_keys[_L_s_idx]}]}"
else
unset -v "${_L_vars[_L_varsidx++]}"
fi
_L_s_idx=$(( _L_s_idx + 1 ))
done
_L_s_idx=0
_L_s_arridx=$(( _L_s_arridx + 1 ))
done
#
if (( _L_varsidx == 0 )); then
# Means no variables were assigned -> end the loop.
unset -v "$_L_opt_v" ${_L_vidx:+"_L_FOREACH[$_L_vidx]"}
return 4
fi
if [[ -n "$_L_opt_l" ]]; then
if (( _L_s_arridx > _L_arrslen )); then
# Loop ends when we looped through all the arrays, i.e. condition from the 'while' loop above.
# L_debug "set -l arridx=$_L_s_arridx arrslen=$_L_arrslen varsidx=$_L_varsidx"
printf -v "$_L_opt_l" 1
else
# Or when on the next loop we would finish. Which means we have to calculate all remaining elements.
local _L_todo=-$_L_s_idx # Substract the count processed in the current array.
for (( _L_i = _L_s_arridx; _L_i < _L_arrslen; ++_L_i )); do
local -n _L_arr=${_L_arrs[_L_i]}
if (( ( _L_todo += ${#_L_arr[*]} ) > 0 )); then
break
fi
done
# L_debug "set -l todo=$_L_todo varslen=$_L_varslen val=$(( _L_todo < _L_varslen )) arridx=$_L_s_arridx arrslen=$_L_arrslen varsidx=$_L_varsidx idx=$_L_s_idx"
printf -v "$_L_opt_l" "%s" "$(( _L_todo <= 0 ))"
fi
fi
# Unset rest of variables that have not been assigned.
while (( _L_varsidx < ${#_L_vars[*]} )); do
unset -v "${_L_vars[_L_varsidx++]}"
done
fi
if [[ -n "$_L_opt_f" ]]; then
printf -v "$_L_opt_f" "%s" "$(( _L_s_loopidx == 0 ))"
fi
if [[ -n "$_L_opt_i" ]]; then
printf -v "$_L_opt_i" "%s" "$_L_s_loopidx"
fi
# Serialize and store state.
# shellcheck disable=SC2059
printf -v _L_i "${_L_s_keys[*]:+%q} " "${_L_s_keys[@]}"
printf -v "$_L_opt_v" "local _L_s_keys=(%s) _L_s_loopidx=%d _L_s_colon=%d _L_s_arridx=%d _L_s_idx=%d" \
"${_L_i%% }" "$(( _L_s_loopidx + 1 ))" "$_L_s_colon" "$_L_s_arridx" "$_L_s_idx"
# L_debug "State:${!_L_opt_v}"
# Yield
}

# ]]]
# lib [[[
# @section lib
Expand Down
Loading
Loading