init
This commit is contained in:
commit
a0b5dfe0e6
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
.installation_prefix
|
37
Makefile
Normal file
37
Makefile
Normal file
@ -0,0 +1,37 @@
|
||||
BIN := "nexclamation"
|
||||
SYMLINK := "n!"
|
||||
COMPL := "completion"
|
||||
TMP := "./.installation_prefix"
|
||||
|
||||
all:
|
||||
@echo Nothing to do. Available targets: install, uninstall, set-prefix
|
||||
@echo
|
||||
@echo Deafult PREFIX for root user: /usr/local
|
||||
@echo Deafult PREFIX for non-root user: ~/.local
|
||||
@echo Set up custom installation PREFIX by:
|
||||
@echo make PREFIX=/your/path install
|
||||
|
||||
install: set-prefix
|
||||
$(eval PREFIX := $(shell cat $(TMP)))
|
||||
@echo Installation PREFIX $(PREFIX)
|
||||
COMPDIR="$(PREFIX)/share/bash-completion/completions"; \
|
||||
mkdir -p "$(PREFIX)" && \
|
||||
mkdir -p "$$COMPDIR" && \
|
||||
cp "$(BIN)" "$(PREFIX)/bin/$(BIN)" && \
|
||||
ln -s "$(PREFIX)/bin/$(BIN)" "$(PREFIX)/bin/$(SYMLINK)" && \
|
||||
cp "$(COMPL)" "$$COMPDIR/$(BIN)"
|
||||
@echo Successfully installed
|
||||
|
||||
uninstall:
|
||||
$(eval PREFIX := $(shell cat $(TMP)))
|
||||
@echo Installation PREFIX $(PREFIX)
|
||||
COMPDIR="$(PREFIX)/share/bash-completion/completions"; \
|
||||
rm -f "$(PREFIX)/bin/$(BIN)" && \
|
||||
rm -f "$(PREFIX)/bin/$(SYMLINK)" && \
|
||||
rm -f "$$COMPDIR/$(BIN)"
|
||||
@echo Successfully uninstalled
|
||||
|
||||
set-prefix:
|
||||
if [ "$$UID" == "0" ]; then \
|
||||
echo $${PREFIX:-/usr/local} > $(TMP); else \
|
||||
echo $${PREFIX:-$$HOME/.local} > $(TMP); fi;
|
33
README.md
Normal file
33
README.md
Normal file
@ -0,0 +1,33 @@
|
||||
# n!
|
||||
|
||||
**n!** is shell powered note taking tool. Notes are stored locally in `$HOME/.local/nexclamation/notes` by default as plain text.
|
||||
|
||||
**n!** depends on Bash, GNU coreutils, find, sed, awk. Usually these programs are already installed on most Linux distros.
|
||||
|
||||
# Installation
|
||||
|
||||
```
|
||||
make install
|
||||
```
|
||||
|
||||
Uninstall by:
|
||||
|
||||
```
|
||||
make uninstall
|
||||
```
|
||||
|
||||
# Usage
|
||||
|
||||
To create new note just run `n!` or:
|
||||
|
||||
```
|
||||
n! note.md
|
||||
```
|
||||
|
||||
See more help at:
|
||||
|
||||
```
|
||||
n! --help
|
||||
```
|
||||
|
||||
If you're having trouble using an exclamation mark in a command name, use command `nexclamation` instead of `n!`.
|
69
completion
Executable file
69
completion
Executable file
@ -0,0 +1,69 @@
|
||||
#!/usr/bin/env bash
|
||||
# n! (nexclamation) v0.0.11 completion script.
|
||||
# Homepage: <http://nixhacks.net/nexclamation>
|
||||
|
||||
NPATH="${NPATH:-$HOME/.local/share/nexclamation/notes}"
|
||||
|
||||
_n_list_dirs() {
|
||||
find "$NPATH" -type d -exec echo {}/ \; | sed -E "s%$NPATH/?%%g;/^$/d";
|
||||
}
|
||||
|
||||
_n_list_files() {
|
||||
find "$NPATH" -type f | sed -E "s%$NPATH/?%%g;/^$/d"
|
||||
}
|
||||
|
||||
_n_get_opts() {
|
||||
local all_opts="$1"
|
||||
|
||||
# Find matched opts.
|
||||
local used_opts="$(echo "${COMP_WORDS[@]} $all_opts" \
|
||||
| tr ' ' '\n' | sort | uniq -d \
|
||||
)"
|
||||
|
||||
if [ "$used_opts" ]; then
|
||||
# Delete 'help' option.
|
||||
all_opts="$(sed 's%help%%' <<< "$all_opts")"
|
||||
# Delete opts if match.
|
||||
for opt in $used_opts; do
|
||||
all_opts="$(sed "s%$opt%%" <<< "$all_opts")"
|
||||
done
|
||||
fi
|
||||
echo "$all_opts"
|
||||
}
|
||||
|
||||
_nexclamation() {
|
||||
local cur prev
|
||||
cur=${COMP_WORDS[COMP_CWORD]}
|
||||
prev=${COMP_WORDS[COMP_CWORD-1]}
|
||||
|
||||
case ${COMP_CWORD} in
|
||||
1) # Commands and options
|
||||
COMPREPLY=($(compgen -W \
|
||||
"-v --version -h --help
|
||||
q quick s search l last mkdir ls lsd rm i info
|
||||
$(_n_list_dirs) $(_n_list_files)" -- ${cur}))
|
||||
;;
|
||||
2) # Subcommand completion
|
||||
case ${prev} in
|
||||
ls) COMPREPLY=($(compgen -W "$(_n_list_dirs)" -- ${cur}))
|
||||
;;
|
||||
rm) COMPREPLY=($(compgen -W "-f --force
|
||||
$(_n_list_dirs) $(_n_list_files)" -- ${cur}))
|
||||
;;
|
||||
i|info) COMPREPLY=($(compgen -W "$(_n_list_files)" -- ${cur}))
|
||||
;;
|
||||
*) COMPREPLY=()
|
||||
;;
|
||||
esac;;
|
||||
*) # Complete file and directory names
|
||||
case ${COMP_WORDS[2]} in
|
||||
*)
|
||||
COMPREPLY=($(compgen -W \
|
||||
"$(_n_get_opts "$(_n_list_dirs) $(_n_list_files)")" -- ${cur}))
|
||||
;;
|
||||
esac;;
|
||||
esac
|
||||
}
|
||||
|
||||
complete -F _nexclamation nexclamation
|
||||
complete -F _nexclamation n!
|
430
nexclamation
Executable file
430
nexclamation
Executable file
@ -0,0 +1,430 @@
|
||||
#!/usr/bin/env bash
|
||||
set -o errexit
|
||||
|
||||
#
|
||||
# n! (nexclamation or nfactorial) -- command line note taking.
|
||||
# Homepage: <http://nixhacks.net/nexclamation>
|
||||
#
|
||||
# COPYING
|
||||
#
|
||||
# MIT License
|
||||
#
|
||||
# Copyright (c) 2021-2022 ge <http://nixhacks.net/nexclamation>
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining
|
||||
# a copy of this software and associated documentation files (the
|
||||
# "Software"), to deal in the Software without restriction, including
|
||||
# without limitation the rights to use, copy, modify, merge, publish,
|
||||
# distribute, sublicense, and/or sell copies of the Software, and to
|
||||
# permit persons to whom the Software is furnished to do so, subject to
|
||||
# the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be
|
||||
# included in all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
n_version=0.0.11 # Current n! version.
|
||||
|
||||
# Defaults.
|
||||
# All of them can be overrided in $n_config.
|
||||
|
||||
# Main config.
|
||||
n_last_opened=$HOME/.local/share/nexclamation/last_opened
|
||||
|
||||
# Path ot save notes.
|
||||
NPATH="${NPATH:-$HOME/.local/share/nexclamation/notes}"
|
||||
|
||||
# Editor.
|
||||
# User's default editor. Set this value for override.
|
||||
NEDITOR="${NEDITOR:-$EDITOR}"
|
||||
# Quick note filename.
|
||||
NQNOTENAME="${NQNOTENAME:-NOTE}"
|
||||
|
||||
# Color scheme.
|
||||
R="\e[31m" # red
|
||||
B="\e[34m" # blue
|
||||
N="\e[0m" # no color
|
||||
b="\e[1m" # bold font
|
||||
|
||||
n_version() {
|
||||
cat << EOF
|
||||
n! (nexclamation or 'n factorial') $n_version
|
||||
Copyright (C) 2021-2022 ge <http://nixhacks.net/nexclamation>
|
||||
License MIT: <https://opensource.org/licenses/MIT>.
|
||||
This is free software: you are free to change and redistribute it.
|
||||
There is NO WARRANTY, to the extent permitted by law.
|
||||
EOF
|
||||
exit 0
|
||||
}
|
||||
|
||||
n_help() {
|
||||
cat << EOF
|
||||
Command line note taking.
|
||||
|
||||
Usage: n! [-v|--version] [-h|--help] [<command>] [<args>...]
|
||||
|
||||
Commands and options:
|
||||
q, quick [<name>] take a quick note in current directory.
|
||||
s, search <query> search in notes via grep.
|
||||
l, last open last opened file in editor.
|
||||
mkdir <dir> add new directory. Creates new dir in NPATH.
|
||||
ls [<dir>] list all notes, or notes from <dir> in NPATH.
|
||||
lsd list dirs in NPATH (empty dirs too).
|
||||
rm [-f|--force] <file> remove notes or directories.
|
||||
i, info [<file>] print info about notes and configuration.
|
||||
-h, --help print this help message and exit.
|
||||
-v, --version print version and exit.
|
||||
|
||||
Examples:
|
||||
|
||||
Take a new note or open existing note:
|
||||
n! [<note>]
|
||||
Take new note in 'work' directory:
|
||||
n! work/my_note
|
||||
|
||||
Environment:
|
||||
|
||||
n! uses user's default editor. You can specify editor in EDITOR environment
|
||||
variable in your .bashrc or .bash_profile, or select default editor by 'select-editor'
|
||||
command.
|
||||
|
||||
Also you can set specific editor for nexclamation in ~/.nexclamation file. For example:
|
||||
NEDITOR=/usr/bin/vim.tiny
|
||||
|
||||
Other configuration options:
|
||||
NPATH path to save notes. Default: $HOME/.local/share/nexclamation/notes
|
||||
NQNOTENAME default file name for quick notes. Default: NOTE
|
||||
EOF
|
||||
exit 0
|
||||
}
|
||||
|
||||
n_err() {
|
||||
echo -e "$1" >&2
|
||||
echo -e "See 'n! --help' or 'nexclamation --help' for info." >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
# ------------------------------------------------------------------------- #
|
||||
# App init #
|
||||
# ------------------------------------------------------------------------- #
|
||||
|
||||
n_initialise() {
|
||||
# Make $NPATH if not exists.
|
||||
if [ ! -d "$NPATH" ]; then
|
||||
mkdir -p "$NPATH" 2>/dev/null
|
||||
fi
|
||||
|
||||
# Set editor.
|
||||
n_set_editor
|
||||
}
|
||||
|
||||
n_set_editor() {
|
||||
|
||||
get_selected_editor() {
|
||||
# shellcheck source=/dev/null
|
||||
source "$HOME/.selected_editor"
|
||||
echo "$SELECTED_EDITOR"
|
||||
}
|
||||
|
||||
# Detect default editor.
|
||||
# Do nothing if editor is set in $n_config
|
||||
if [ -z "$EDITOR" ]; then
|
||||
NEDITOR="$(get_selected_editor)"
|
||||
elif [ -f /usr/bin/select-editor ]; then
|
||||
select-editor
|
||||
NEDITOR="$(get_selected_editor)"
|
||||
else
|
||||
NEDITOR=/usr/bin/vi
|
||||
fi
|
||||
}
|
||||
|
||||
# ------------------------------------------------------------------------- #
|
||||
# Functions #
|
||||
# ------------------------------------------------------------------------- #
|
||||
|
||||
n_take_note() {
|
||||
# Take a note.
|
||||
#
|
||||
# Arguments:
|
||||
# $@ -- file names
|
||||
# Examples of $1:
|
||||
# my_note.txt
|
||||
# some_dir/my_note.txt
|
||||
|
||||
if [[ -n "$*" ]]; then
|
||||
for file in "$@"; do
|
||||
if [[ "$file" =~ .+/.+ ]]; then
|
||||
# if filename contains path
|
||||
if [ -d "$NPATH/${file%/*}" ]; then
|
||||
files+=("$NPATH/$file")
|
||||
else
|
||||
echo -n "${0##*/}: $NPATH/${file%/*}" >&2
|
||||
echo ": destination does not exist" >&2
|
||||
echo "Run 'n! mkdir <dir>' to create new directory." >&2
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
files+=("$NPATH/$file")
|
||||
fi
|
||||
done
|
||||
"$NEDITOR" "${files[@]}"
|
||||
else
|
||||
cd "$NPATH" || { echo "${0##*/}: Cannot cd into $NPATH"; exit 1; }
|
||||
"$NEDITOR"
|
||||
cd - >/dev/null || { echo "${0##*/}: Cannot cd into $OLDPWD"; exit 1; }
|
||||
fi
|
||||
}
|
||||
|
||||
n_quick_note() {
|
||||
# Take note in current working directory.
|
||||
#
|
||||
# Default $NQNOTENAME can be set in ~/.nexclamation file.
|
||||
|
||||
if [[ -n "$*" ]]; then
|
||||
local temp="$*"
|
||||
NQNOTENAME="${temp%% *}"
|
||||
fi
|
||||
|
||||
echo "${PWD}/${NQNOTENAME}" > "$n_last_opened"
|
||||
"$NEDITOR" "$NQNOTENAME"
|
||||
exit 0 # Prevent new note taking
|
||||
}
|
||||
|
||||
n_last() {
|
||||
# Open last opened file.
|
||||
#
|
||||
# Filename is saved in service file $n_last_opened
|
||||
|
||||
if [ -f "$n_last_opened" ]; then
|
||||
local file
|
||||
file="$(cat "$n_last_opened")"
|
||||
else
|
||||
echo "${0##*/}: No opened files yet" >&2; exit 1
|
||||
fi
|
||||
|
||||
if [ -f "$file" ]; then
|
||||
"$NEDITOR" "$file"; exit 0
|
||||
else
|
||||
echo "${0##*/}: $file: No such file" >&2; exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
n_search() {
|
||||
# Search in notes.
|
||||
#
|
||||
# $1 is search query.
|
||||
|
||||
while (( "$#" )); do
|
||||
case "$1" in
|
||||
-h|--help) n_help;;
|
||||
-v|--version) n_version;;
|
||||
-*) n_err "${0##*/}: $1: Unknown option";;
|
||||
*) local q="$1"; shift;;
|
||||
esac
|
||||
done
|
||||
|
||||
cd "$NPATH" || { echo "${0##*/}: Cannot cd into $NPATH"; exit 1; }
|
||||
grep --recursive --ignore-case --line-number --color=auto "$q"
|
||||
cd - >/dev/null || { echo "${0##*/}: Cannot cd into $OLDPWD"; exit 1; }
|
||||
}
|
||||
|
||||
n_mkdir() {
|
||||
# Create new directory in $NPATH
|
||||
#
|
||||
# $1 -- dirname
|
||||
|
||||
while (( "$#" )); do
|
||||
case "$1" in
|
||||
-h|--help) n_help;;
|
||||
-v|--version) n_version;;
|
||||
-*) n_err "${0##*/}: $1: Unknown option";;
|
||||
*) local dir="$1"; shift;;
|
||||
esac
|
||||
done
|
||||
|
||||
mkdir -p "${NPATH}/${dir}" 2>/dev/null
|
||||
echo -e "${b}Created:${N} ${NPATH}/${B}${b}${dir}/${N}"
|
||||
exit 0
|
||||
}
|
||||
|
||||
n_list() {
|
||||
# List files from dir or dirs.
|
||||
|
||||
while (( "$#" )); do
|
||||
case "$1" in
|
||||
-d|--dirs) list_dirs=1; shift;;
|
||||
-h|--help) n_help;;
|
||||
-v|--version) n_version;;
|
||||
-*) n_err "${0##*/}: $1: Unknown option";;
|
||||
*) local dir="$1"; shift;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [ ${#dir[@]} -gt 1 ]; then
|
||||
echo -e "${0##*/}: too many arguments" >&2; exit 1
|
||||
fi
|
||||
|
||||
if [ ! -d "${NPATH}/${dir}" ]; then
|
||||
echo -e \
|
||||
"${0##*/}: ${NPATH}/${dir}: No such directory" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -n "$list_dirs" ]; then # list only dirs (append / for coloring)
|
||||
list="$(find "$NPATH" -type d -exec echo {}/ \;)"
|
||||
elif [ "$dir" ]; then # list files from specific directory
|
||||
list="$(find "${NPATH}/${dir}" -type f)"
|
||||
else # list all of files and dirs
|
||||
list="$(find "$NPATH" -type f)"
|
||||
fi
|
||||
|
||||
# Print output.
|
||||
for path in $(echo "$list" | sed -E "s%${NPATH}/?%%g;/^$/d"); do
|
||||
# Add color for dirs (blue) with brainfucking parameter expansion
|
||||
local temp
|
||||
temp="${path//\//\\/}" # Escape slashes
|
||||
# shellcheck disable=SC2059
|
||||
echo "${path}" | sed "/${temp%\\*}\//s//$(printf "${B}${b}${temp%/*}\/${N}")/"
|
||||
done | sort
|
||||
exit 0
|
||||
}
|
||||
|
||||
n_remove() {
|
||||
# Remove files or directories.
|
||||
|
||||
while (( "$#" )); do
|
||||
case "$1" in
|
||||
-f|--force) forced=1; shift;;
|
||||
-h|--help) n_help;;
|
||||
-v|--version) n_version;;
|
||||
-*) n_err "${0##*/}: $1: Unknown option";;
|
||||
*) local files+=("$1"); shift;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [ ${#files[@]} -eq 0 ]; then
|
||||
echo -e "${0##*/}: Nothing to remove" >&2; exit 1
|
||||
fi
|
||||
|
||||
if [ ! "$forced" ]; then
|
||||
echo -e "These files will be removed: ${R}${files[*]}${N}" | fmt -t
|
||||
|
||||
while [ -z "$answer" ]; do
|
||||
echo -en "Remove files permanently? (y/n) "
|
||||
read -r reply
|
||||
case "${reply,,}" in
|
||||
y|yes) answer=1;;
|
||||
n|no) echo "Abort"; exit 1;;
|
||||
*) echo 'Please, answer y or n';;
|
||||
esac
|
||||
done
|
||||
fi
|
||||
|
||||
for file in "${files[@]}"; do
|
||||
file="${NPATH}/${file}"
|
||||
if [ -d "$file" ]; then
|
||||
rm -rf "$file"
|
||||
echo -e "${b}Removed:${N} $file"
|
||||
elif [ -f "$file" ]; then
|
||||
rm -f "$file"
|
||||
echo -e "${b}Removed:${N} $file"
|
||||
else
|
||||
echo "${0##*/}: $file: No such file or directory" >&2; exit 1
|
||||
fi
|
||||
done
|
||||
exit 0
|
||||
}
|
||||
|
||||
n_info() {
|
||||
# Show information about notes and configuration.
|
||||
|
||||
while (( "$#" )); do
|
||||
case "$1" in
|
||||
-h|--help) n_help;;
|
||||
-v|--version) n_version;;
|
||||
-*) n_err "${0##*/}: $1: Unknown option";;
|
||||
*) local file="$1"; shift;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [ -n "$file" ]; then
|
||||
file="$NPATH/$file"
|
||||
if [ ! -f "$file" ]; then
|
||||
echo "${0##*/}: $file: No such file" >&2; exit 1
|
||||
fi
|
||||
{
|
||||
echo -e "${b}Lines:${N}|$(wc -l "$file" |
|
||||
tail -n 1 | awk '{print $1}')"
|
||||
echo -e "${b}Words:${N}|$(wc -w "$file" |
|
||||
tail -n 1 | awk '{print $1}')"
|
||||
echo -e "${b}Size:${N}|$(du -hs "$file" | cut -f 1)"
|
||||
} | column -t -s '|'
|
||||
exit 0
|
||||
fi
|
||||
|
||||
local all_files
|
||||
local all_dirs
|
||||
all_files="$(find "$NPATH" -type f)"
|
||||
all_dirs="$(find "$NPATH" -type d)"
|
||||
{
|
||||
echo -e "${b}Editor:${N}|${NEDITOR}"
|
||||
echo -e "${b}Quick notes:${N}|${NQNOTENAME}"
|
||||
echo -e "${b}Notes save path:${N}|${NPATH}"
|
||||
echo -e "${b}Dirs:${N}|$(<<< "$all_dirs" wc -l)"
|
||||
echo -e "${b}Files:${N}|$(<<< "$all_files" wc -l)"
|
||||
# shellcheck disable=SC2001
|
||||
echo -e "${b}Total lines:${N}|$(<<< "$all_files" sed 's/.*/"&"/' |
|
||||
xargs wc -l | tail -n 1 | awk '{print $1}')"
|
||||
# shellcheck disable=SC2001
|
||||
echo -e "${b}Total words:${N}|$(<<< "$all_files" sed 's/.*/"&"/' |
|
||||
xargs wc -w | tail -n 1 | awk '{print $1}')"
|
||||
echo -e "${b}Total size:${N}|$(du -hs "$NPATH" | cut -f 1)"
|
||||
echo -e "${b}Last opened:${N}|$([ -f "$n_last_opened" ] && \
|
||||
cat "$n_last_opened" || echo 'no opened files yet')"
|
||||
} | column -t -s '|'
|
||||
exit 0
|
||||
}
|
||||
|
||||
# ------------------------------------------------------------------------- #
|
||||
# n! #
|
||||
# ------------------------------------------------------------------------- #
|
||||
|
||||
n_checkopt() {
|
||||
if [ "$2" ]; then
|
||||
return 0
|
||||
else
|
||||
n_err "${0##*/}: Missing argument for $1"
|
||||
fi
|
||||
}
|
||||
|
||||
n_initialise
|
||||
|
||||
while (( "$#" )); do
|
||||
case "$1" in
|
||||
q|quick) shift; n_quick_note "$@"; shift "$#";;
|
||||
l|last) n_last;;
|
||||
s|search) n_checkopt "$1" "$2"; n_search "$2"; exit 0;;
|
||||
mkdir) n_checkopt "$1" "$2"; n_mkdir "$2"; exit 0;;
|
||||
ls) shift; n_list "$@"; shift "$#";;
|
||||
lsd) shift; n_list -d; shift "$#";;
|
||||
rm) shift; n_remove "$@"; shift "$#";;
|
||||
i|info) shift; n_info "$@"; shift "$#";;
|
||||
-v|--version) n_version;;
|
||||
-h|--help) n_help;;
|
||||
-*) n_err "${0##*/}: $1: Unknown option";;
|
||||
*) pos_args+=("$1"); shift;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [ "${#pos_args[@]}" -gt 0 ]; then
|
||||
echo "${NPATH}/${pos_args[-1]}" > "$n_last_opened"
|
||||
fi
|
||||
|
||||
n_take_note "${pos_args[@]}"
|
Loading…
Reference in New Issue
Block a user