1
1
Fork 0
mirror of https://github.com/tonydamage/nux-env.git synced 2025-12-13 13:44:28 +01:00

Compare commits

..

10 commits

15 changed files with 421 additions and 106 deletions

View file

@ -1,3 +1,3 @@
## Maven configuration
export MAVEN_OPTS='-Xms256m -XX:MaxPermSize=512m -Xmx1024m'
export MAVEN_OPTS='-Xms256m -Xmx1024m'
export GOPATH='/home/tony/.go/'

166
bin/mark
View file

@ -2,10 +2,118 @@
nux.use nux/fs
@prefix fs nux.fs.
@prefix check nux.check.
MARK_DIR_NAME=.by
MARK_DIR_NAME="${MARK_DIR_NAME:-.by}"
MARK_PREFIX=""
## Manages symlinks in closest mark (**.by**) directory, provides functionality to batch create
## them with relative paths.
##
## #Available tasks:
## tag:: <task> <task arguments...>
## Performs specified task in tag namespace (marks prefixed with **tag/**)
@command tag task {
MARK_PREFIX="tag/"
nuxr.run "$task" "$@"
}
## this:: <mark> [marks...]
## Marks **current folder** with specific markers.
## This creates symlinks in **mark** folder pointing to **current folder**.
@command this mark {
nux.log debug "Args $#"
item=$(pwd)
mark_root=$(mark.dir "$item")
mark.mark "$mark_root" "$item" "$MARK_PREFIX$mark"
while [ "$#" -gt 0 ]; do
mark="$1"; shift;
mark.mark "$mark_root" "$item" "$MARK_PREFIX$mark"
done
}
## multiple:: <mark> <files...>
## Marks **files** with specific **mark**.
## This creates symlinks for files in **mark** folder.
@command multiple mark {
pwd=$(pwd)
mark_root=$(mark.dir "$pwd")
while [ "$#" -gt 0 ]; do
item="$1"; shift;
mark.mark "$mark_root" "$pwd/$item" "$MARK_PREFIX$mark"
done
}
## display:: [mark]
## Displays path to current mark folder and displays available marks.
## If **mark** is provided list nested marks.
@command display mark {
mark_root=$(mark.dir $pwd)
prefix="$MARK_PREFIX"
if [ -n "$mark" ]; then
prefix="$MARK_PREFIX$mark/";
fi
echo $mark_root:
(
cd "$mark_root";
for mark in "$prefix"* ; do
echo ${mark#$MARK_PREFIX};
done;
)
}
## mark-to-target:: mark target [suffix]
## Moves non-symlinks to *target* and creates symlinks in mark folder
## This also applies to submarks
@command mark-to-target mark target {
suffix="$1";
nux.log info "Suffix is $suffix"
mark=$(mark.dir $pwd)/$mark
nux.log debug $(mark.dir $pwd) $mark;
find "$mark" -type f | while read file; do
if ! nux.check.file.symlink "$file"; then
name=$(nux.fs.name "$file")
file_mark=$(nux.fs.dirname "$file")
fs:info "$file" Moving to $target/$name
fs:move "$target" "$file"
fs:info "$target/$name" Creating symlink in $file_mark
fs:symlink "$target/$name" "$file_mark" "$name"
fi
done
}
## visual:: [image+]
## Display images using feh and allows adding marks using 1-9 key.
## The list of marks is speficied by environment variable *MARK_TAGS*
##
@command visual {
nux.require feh
marks=${MARK_TAGS:-person woman man selfie}
mark_root=$(nux.fs.path.relative.pwd $(mark.dir $pwd/))
actions="";
for mark in $marks; do
((i++))
mark=$MARK_PREFIX$mark
if [ $i -gt 9 ]; then
break;
fi
action="--action$i '[$mark] mkdir -p $mark_root/$mark; ln -svft $mark_root/$mark \$(realpath -Lms --relative-to=$mark_root/$mark %F)'";
actions="$actions $action";
done;
nux.log debug Feh actions "$actions"
nux.eval feh \
--zoom max \
--scale-down \
-g 900x1000 \
-G \
"--action '[keep]echo %F: Next file.'" \
"$actions" \
"--info 'echo %n: %wx%h'" \
--draw-tinted "$@"
}
@namespace mark. {
function :dir item {
if [ -n "$MARK_DIR" ]; then
@ -28,59 +136,3 @@ MARK_PREFIX=""
}
}
## Manages symlinks in closest mark (**.by**) directory, provides functionality to batch create
## them with relative paths.
##
## #Available tasks:
@namespace task. {
## tag:: <task> <task arguments...>
## Performs specified task in tag namespace (marks prefixed with **tag/**)
@command tag task {
MARK_PREFIX="tag/"
nuxr.run "$task" "$@"
}
## this:: <mark> [marks...]
## Marks **current folder** with specific markers.
## This creates symlinks in **mark** folder pointing to **current folder**.
@command this mark {
nux.log debug "Args $#"
item=$(pwd)
mark_root=$(mark.dir "$item")
mark.mark "$mark_root" "$item" "$MARK_PREFIX$mark"
while [ "$#" -gt 0 ]; do
mark="$1"; shift;
mark.mark "$mark_root" "$item" "$MARK_PREFIX$mark"
done
}
## multiple:: <mark> <files...>
## Marks **files** with specific **mark**.
## This creates symlinks for files in **mark** folder.
@command multiple mark {
pwd=$(pwd)
mark_root=$(mark.dir "$pwd")
while [ "$#" -gt 0 ]; do
item="$1"; shift;
mark.mark "$mark_root" "$pwd/$item" "$MARK_PREFIX$mark"
done
}
## display:: [mark]
## Displays path to current mark folder and displays available marks.
## If **mark** is provided list nested marks.
@command display mark {
mark_root=$(mark.dir $pwd)
prefix="$MARK_PREFIX"
if [ -n "$mark" ]; then
prefix="$MARK_PREFIX$mark/";
fi
echo $mark_root:
(
cd $(mark_root);
for mark in "$prefix"* ; do
echo ${mark#$MARK_PREFIX};
done;
)
}

View file

@ -5,7 +5,7 @@
## highlight:: <language> <file>
task.highlight() {
nux.use nux/nuxsh
nux.dsl.process highlight nux.nuxsh.language.def "$2"
}
@ -23,8 +23,10 @@ task.plan.compile() {
}
task.plan.preview() {
nux.dsl.plan nux.nuxsh.language "$2"
plan="$(nudsl.plan.file "$1" "$2")"
nux.use nux/nuxsh
nux.dsl.plan nux.nuxsh.language.def "$1"
plan="$(nudsl.plan.file nux.nuxsh.language.def "$1")"
nux.log info "Plan file: $plan"
if [ -e "$plan" ]; then
if [ -n "$(which pygmentize)" ]; then
pygmentize -l bash "$plan";

View file

@ -1,9 +1,9 @@
#!/usr/bin/env nuxr-nuxsh
## Portable *nix environment by tonydamage
## status::
## Show status of nux-env installation
##
## status::
## Show status of nux-env installation
@namespace task. {
function :status {
echo nux-env folder: $NUX_ENV_DIR
@ -12,8 +12,9 @@
popd > /dev/null
}
## update::
## pulls latest nux-env from repository.
##
## update::
## pulls latest nux-env from repository.
function :update {
pushd $NUX_ENV_DIR > /dev/null
git stash
@ -22,12 +23,35 @@
popd > /dev/null
}
##
## install::
## Install nux-env recommended binaries if not present
function :install {
:
echo $NUX_ENV_DIR
:symlink bashrc .bashrc
}
function :symlink source target {
local env=~
target=${env}/${target}
source=${NUX_ENV_DIR}/${source}
real_source=$(realpath $source)
nux.log debug "$target" is symlink to "$real_source"
if [ -e "$target" ] {
nux.log debug "$target" exists
if [ -h "$target" ] {
link_target=$(realpath $target)
if [ "$real_source" = "$link_target" ] {
nux.log debug "$target" is already symlink to $source
return;
}
nux.log debug "$target" is symlink to "$link_target"
}
}
}
##
## fixmes::
## List all fixmes for nux-env
function :fixmes {
@ -35,9 +59,10 @@
find "$NUX_INC_DIR" -iname "*.sh" | xargs fgrep -n FIXME
}
##
## help library:: <inc>
## Displays help for specified nuxs-env library.
##
function :help.library name {
nux.log debug "Library"
if [ -e "$NUX_INC_DIR/$name.inc.sh" ] {

View file

@ -132,6 +132,9 @@ if [ -n "$NUX_SCRIPT" ]; then
NUX_SCRIPTNAME=$(basename "$NUX_SCRIPT")
NUX_APP_NAME="${NUX_APP_NAME:=$NUXR_APP_NAME}"
NUX_APP_DIR="${NUX_APP_DIR:=$NUXR_APP_DIR}"
nux.log trace "NUX_SCRIPT env: " $(set | grep NUX_SCRIPT)
if [ -z "$NUX_NO_INCLUDE" ]
then

View file

@ -143,7 +143,7 @@ if [ -n "$NUX_SCRIPT" ]; then
compilefile=$(realpath "$NUX_SCRIPT" | md5sum | cut -d" " -f1)
#FIXME: check if nux_cache should be used.
compilefile="/tmp/$compilefile.nuxr.nuxsh"
compilefile="$TMPDIR/$compilefile.nuxr.nuxsh"
nux.log debug "Compiled script:" $compilefile
nux.nuxsh.use "$NUX_SCRIPT" "$compilefile";
NUX_NO_INCLUDE="no-include"

View file

@ -14,7 +14,7 @@ nux.use taskie/backend.dir
function with.backend backendId {
backend=$(echo $backendId | cut -d: -f1);
nux.exec.optional backend.$backend.with;
nux.exec.optional backend.$backend.with "$backendId";
}
function endwith.backend {
@ -40,6 +40,7 @@ function endwith.backend {
if ! backend.$backend.issue.exists "$@" ; then
local labels=$(backend.$backend.labels.id)
nux.log debug "Labels: $labels"
label=$(echo "$labels" | grep -G "^$1:")
local labelName=""
local labelId=""

View file

@ -1,13 +1,20 @@
#!/usr/bin/env nuxr-nuxsh
nux.use nux/fs
type ffmpeg > /dev/null 2>&1 && FFMPEG_OR_LIBAV=ffmpeg
type avconv > /dev/null 2>&1 && FFMPEG_OR_LIBAV=avconv
type gm > /dev/null 2>&1 && NUX_MAGICK=gm
QUALITY=${QUALITY:=90}
QUALITY=${QUALITY:=95}
DUPLICATE_THRESHOLD=${DUPLICATE_THRESHOLD:=90%}
SKIP_CHECK=no_skip
nux.log debug "FFMPEG: $FFMPEG_OR_LIBAV Magick: $NUX_MAGICK"
## Performs operations on media such as images or videos.
## Use env **QUALITY** to specify compression quality.
##
## # Available tasks:
@command video.change.container {
CONTAINER=$1;
shift;
@ -23,32 +30,63 @@ QUALITY=${QUALITY:=90}
task.video.change.container mp4 "$@"
}
@command keep-path task {
}
## remove-orig:: <task> <task arguments...>
## Runs specified task, but remove original images if applicable.
## This keyword is applicable for *downscale*, *to*
@command remove-orig task {
nuxr.run "$task" "$@"
}
## no-overwrite:: <task> <task arguments...>
## Runs specified task, but remove original images if applicable.
## This keyword is applicable for *downscale*, *to*
@command no-overwrite task {
SKIP_CHECK=nux.fs.exists
nuxr.run "$task" "$@"
}
@command preserve task {
PRESERVE=true
nuxr.run "$task" "$@"
}
## downscale:: <target> <size> <image...>
## Creates downscaled copy of image in *target* directory.
## Image is downsampled to fit in *size*. Smaller images are not upscaled.
@command downscale TARGET SIZE {
local i=0;
local count="$#";
mkdir -p $TARGET;
for image in "$@"; do
let "i=i+1"
name=$(basename $image);
target_file=$TARGET/$name;
name="$(basename "$image")";
target_file="$TARGET/$name";
if [ -n "$PRESERVE" ]; then
target_dir="${TARGET}/$(dirname "$image")";
mkdir -p "$target_dir";
target_file="${target_dir}/$name";
fi
echo "Image: $i/$count Downsampling $image -> $target_file"
$NUX_MAGICK convert $image \
-filter Lanczos -sampling-factor 1x1 \
-resize "${SIZE}x${SIZE}>" \
-quality $QUALITY \
$target_file
if $SKIP_CHECK "$target_file" ; then
echo "Image: $i/$count Skipping"
else
echo "Image: $i/$count Downsampling $image -> '$target_file'"
$NUX_MAGICK convert "$image" \
-filter Lanczos -sampling-factor 1x1 \
-resize "${SIZE}x${SIZE}>" \
-quality $QUALITY \
"$target_file"
fi
done
}
## to:: <jpg|png> <image...>
## Convert image to specified format
##
## to:: <**jpg|png**> <image...>
## Convert image to specified format.
@command to target {
if ! nux.check.function "media.to.$target" ; then
echo Target type "$target" is not supported.
@ -63,8 +101,129 @@ QUALITY=${QUALITY:=90}
done
}
## list-smaller:: <size> <image...>
## Lists images smaller than **size**.
@command list-smaller size {
$NUX_MAGICK identify -format "%f;%w;%h\n" "$@" | while IFS=";" read file w h ; do
if [ -z "$file" ]; then
continue;
fi
if [ $w -gt $size ] && [ $h -gt $size ]; then
continue;
fi
echo "$file $w $h"
done
}
@command list-different size {
$NUX_MAGICK identify -format "%f;%w;%h\n" "$@" | while IFS=";" read file w h ; do
if [ -z "$file" ]; then
continue;
fi
if [ $w -eq $size ] && [ $h -eq $size ]; then
continue;
fi
echo "$file $w $h"
done
}
## duplicates:: <image...>
## Analyse images for duplicates and display duplicate groups using feh.
## When image is displayed following actions are available:
## 0:: Keep file
## 1:: Move to folder with name of first image
## 2:: Replace with symlink to first image
## 3:: Remove file
###
### Environment variables:
### DUPLICATE_THRESHOLD:: Sets similarity treshold when images are
### considered similar. Default value is *90%*
###
@command duplicates {
nux.require findimagedupes
nux.require feh
findimagedupes -t $DUPLICATE_THRESHOLD "$@" | while read duplicate_set; do
largest=$(img.largest $duplicate_set)
set_name=${largest%.*}
nux.fs.info $largest Set name: $set_name
feh_files="";
for file in $duplicate_set; do
file=$(nux.fs.path.relative.pwd "$file");
#nux.fs.info $largest Possible duplicate $file
feh_files="$feh_files $file";
done
feh \
--zoom max \
--scale-down \
-G \
--action '[keep]echo %F: Keeping file.' \
--action1 "[move]echo %F: Moving to $set_name ; mkdir -p $set_name ; mv -t $set_name %F ;" \
--action2 "[symlink]echo %F: Replacing with symlink to $largest ; ln -sf $largest %F ;" \
--action3 "[delete] echo %F Removing file; rm -f %F;" \
--info 'echo %n: %wx%h' \
--draw-tinted $feh_files
done
}
## feh:: <image...>
## Displays images using feh.
## When image is displayed following actions are available:
## 0:: Next file
## 1:: -
## 2:: -
## 3:: Remove file
@command feh {
feh \
--zoom max \
--scale-down \
-G \
--action '[keep]echo %F: Keeping file.' \
--action3 "[delete] echo %F Removing file; rm -f %F;" \
--info 'echo %n: %wx%h' \
--draw-tinted "$@"
}
function img.largest {
## FIXME: Identify largest image
nux.fs.path.relative.pwd "$1";
}
function media.to.jpg {
$NUX_MAGICK convert "$1" -quality $QUALITY -auto-orient "$2"
}
function media.to.png {
$NUX_MAGICK convert "$1" -quality $QUALITY -auto-orient "$2"
}
function no_skip {
return 1;
}
@command mass-crop label target {
for img in "$@"; do
without_suffix=${img%.*};
suffix=${img##*.};
id=${without_suffix##*/}
nux.log debug "Image: $img ID: $id"
img_size=$($NUX_MAGICK identify "$img" -format "%h %w" )
img_h=${img_size%% *};
img_w=${img_size##* };
i=1;
if [ -e "$ANNOTATIONS/${id}.bboxes" ]; then
grep "^$label" "$ANNOTATIONS/${id}.bboxes" | while read label cx cy w h; do
w=$(nux.round $(nux.calc "$w*$img_w") 0);
h=$(nux.round $(nux.calc "$h*$img_h") 0);
x=$(nux.round $(nux.calc "$cx*$img_w-$w*0.5") 0);
y=$(nux.round $(nux.calc "$cy*$img_h-$h*0.5") 0);
nux.fs.info "$img" "cropping ${w}x${h}+${x}+${y} to " "$target/$id.$i.$suffix"
$NUX_MAGICK convert "$img" -crop "${w}x${h}+${x}+${y}" "$target/$id.$i.$suffix"
let "i=i+1"
done;
fi
done;
}

58
bin/vfs
View file

@ -4,17 +4,25 @@ nux.use nux/fs
@prefix fs nux.fs.
VFS_SOURCES_FILE=".vfs.sources"
declare -gA CURRENT_SOURCES
CURRENT_SOURCES_FILE=""
CURRENT_MOUNT=""
CURRENT_SOURCES_FILE=$(nux.fs.closest "$VFS_SOURCES_FILE");
function vfs.init path {
CURRENT_SOURCES_FILE=$(nux.fs.closest "$VFS_SOURCES_FILE" "$path");
vfs.merger.load $CURRENT_SOURCES_FILE;
}
if fs:exists "$CURRENT_SOURCES_FILE" {
CURRENT_MOUNT="${CURRENT_SOURCES_FILE%/*}";
declare -gA CURRENT_SOURCES
nux.log debug "Current VFS mount: ${CURRENT_MOUNT%/*}";
function vfs.merger.load path {
if fs:exists "$path" {
CURRENT_MOUNT="${path%/*}";
while read name path; do
CURRENT_SOURCES[${name}]="$path"
done < "$CURRENT_SOURCES_FILE"
nux.log debug "Current VFS mount: ${CURRENT_MOUNT%/*}";
while read name path; do
CURRENT_SOURCES[${name}]="$path"
done < "$CURRENT_SOURCES_FILE"
}
}
function vfs.path name {
@ -31,6 +39,15 @@ function vfs.path.real file {
done
}
function vfs.path.real.all file {
for root in "${CURRENT_SOURCES[@]}" ; do
nux.log trace "Testing $root$file"
if fs:exists "${root}${file}" {
echo ${root}${file};
}
done
}
## list::
## Lists all **mergerfs** based virtual filesystems managed by **vfs** tool
@ -40,7 +57,10 @@ function vfs.path.real file {
## info::
## Displays info about current path
@command info {
@command info path {
: ${path:=.}
nux.log debug "path" $path
vfs.init "$path"
nux.log debug "VFS mount: ${CURRENT_MOUNT}";
echo "path:" $CURRENT_MOUNT;
echo "sources:"
@ -62,7 +82,8 @@ function vfs.path.real file {
### FIXME: Switch does not support merging of directories
@command switch storage {
@command switch storage {
vfs.init .
target="$(vfs.path "$storage")"
if [ -z "$target" ] {
nux.fatal "$storage does not exists."
@ -101,20 +122,29 @@ function vfs.path.real file {
source_path="${source_path%/}"
nux.log debug " Source: $source_name Path: $source_path";
mount_paths="$mount_paths:${source_path}"
echo "$source_name $source_path" >> "$source_tempfs/.vfs.sources"
echo "$source_name $source_path" >> "$source_tempfs/$VFS_SOURCES_FILE"
done
echo "temp $source_tempfs" >> "$source_tempfs/$VFS_SOURCES_FILE="
echo "temp $source_tempfs" >> "$source_tempfs/$VFS_SOURCES_FILE"
mergerfs_mounts="$source_tempfs:${mount_paths}"
mergerfs_mounts="${source_tempfs}=RO${mount_paths}"
nux.log debug "MergerFS command:" $mergerfs_mounts;
mergerfs "$mergerfs_mounts" "$target"
(cd $target; vfs info )
}
}
## unmount:: <target>
## Unmounts target VFS filesystem.
@command :unmount target {
vfs.init "$target"
task.info "$target"
fusermount -u "$target"
fs:info "${CURRENT_SOURCES[temp]}" removing temporary metadata.
fs:stage rm -rf "${CURRENT_SOURCES[temp]}"
}
@command path file {
: ${file:=.}
vfs.init "$file"
rooted_path="/$(realpath -m --relative-to="$CURRENT_MOUNT" ${file%/})";
vfs.path.real.all "$rooted_path";
}

View file

@ -78,7 +78,6 @@ function :read {
nux.fatal "Unknown config store $store".
;;
esac
shift;
:write.direct "$(nux.cfg.file.$store)" "$@"
}
@ -146,7 +145,7 @@ function :read {
mkdir -p "$(dirname "$file")";
touch "$file";
fi
shift;
nux.log debug "Args $@"
yaml w "$file" "$@" -i
}

View file

@ -43,6 +43,14 @@ function nux.require {
}
function nux.round {
echo $(printf %.$2f $(echo "scale=$2;(((10^$2)*$1)+0.5)/(10^$2)" | bc))
};
function nux.calc {
echo "$@" | bc
}
## nux.use:: <library>
function nux.use {
nux.log trace "nux.use: Including: $1"
@ -91,7 +99,7 @@ function nux.dirty.urlencode {
function nux.url.parse {
format=${2:-"protocol:\2\nuser:\4\nhost:\5\nport:\7 \npath:\8"}
echo "$1" | sed \
-re "s/(([^:\/]*):\/\/)?(([^@\/:]*)@)?([^:\/]+)(:([0-9]+))?(\/(.*))?/$format/g"
-re "s/(([^:\/]*):\/\/)?(([^@\/:]*)@)?([^:\/]+)(:([0-9]+))?([:\/](.*))?\$/$format/g"
}

View file

@ -30,3 +30,7 @@ function nux.check.exec {
function nux.check.file.exists {
test -e "$1" -o -h "$1";
}
function nux.check.file.symlink {
test -h "$1";
}

View file

@ -7,8 +7,30 @@ nux.use nux/check
check:file.exists "$target";
}
function :check.absolute target {
case "$target" in
/*) return 0;;
esac
return 1
}
function :name file {
echo "${file##*/}";
}
function :dirname file {
case "$file" in
*/*)
echo "${file%/*}"
;;
*)
echo ".";;
esac
}
function :closest target {
cdir="${2:-$(pwd)}";
nux.log trace "Secondary Path" "$@"
cdir=$(realpath -m "${1:-$(pwd)}");
nux.log trace "Searching in: " $cdir;
until [ -e "$cdir/$target" -o "$cdir" == "/" ]; do
cdir=$(dirname "$cdir");
@ -20,17 +42,25 @@ nux.use nux/check
}
function :path.relative.pwd target {
realpath -Lms --relative-to="$(pwd)" "$target"
:path.relative "." "$target"
}
function :path.relative base target {
realpath -Lms --relative-to="$base" "$target"
}
function :symlink.target symlink {
readlink "$symlink";
}
function :path.display target {
echo $NC_LightPurple$(nux.fs.path.relative.pwd "$target")$NC_No;
}
function :move target {
:stage mv -t "$target" "$@"
}
function :symlink target dir name {
relative=$(nux.fs.path.relative "$dir" "$target")
nux.log debug "Relative path is: $relative"

View file

@ -120,7 +120,7 @@ nux.nuxsh.language.def() {
prepend=$_namespace;
fi
if [ -z "$prepend" ] ; then
nudsl.process.fail "undefined prefix: $prefix";
nux.dsl.process.fail "undefined prefix: $prefix";
fi
echo "$prepend$identifier"
else
@ -175,6 +175,7 @@ nux.nuxsh.language.def() {
for arg in ${args//,/ }; do
echo "${indent} local $arg="'"$1"'";shift;"
echo "${indent} nux.log trace ' ' arg $arg: "'$'$arg";"
echo "${indent} nux.log trace ' ' rest: " '"$@";'
done
}
@ -189,6 +190,7 @@ nux.nuxsh.language.def() {
for arg in ${args//,/ }; do
echo "${indent} local $arg="'"$1"'";shift;"
echo "${indent} nux.log trace ' ' arg $arg: "'$'$arg";"
echo "${indent} nux.log trace ' ' rest: " '"$@";'
done
}

View file

@ -85,11 +85,11 @@ nux.use nuxr/repl
local task_dot=$(tr " " "." <<< "$task")
nux.log trace "Trying to figure task documentation location for $task $task_dot"
doc_start=$(grep -hn -E "## +($task)::" "$script" | cut -d: -f1)
code_start=$(grep -hn -E "((@command +:?$task_dot .*)|(function +task.$task_dot)|(task.$task_dot *\(\))) +{" "$script" | cut -d: -f1)
code_start=$(grep -hn -E "((@command +:?$task_dot)|(function +task.$task_dot)|(task.$task_dot *\(\))) +{" "$script" | cut -d: -f1)
nux.log trace "doc_start" $doc_start $code_start
if [ -n "$doc_start" -a -n "$code_start" ] {
sed -n "$doc_start,$code_start"p "$script" \
| grep "^\#\#" \
| grep "^##" \
| sed -re "s/^#+ ?(.*)/\1/gi" \
| nux.help.shelldoc
return 0