Skip to content

Instantly share code, notes, and snippets.

@hypersoft
Last active December 13, 2015 18:18
Show Gist options
  • Save hypersoft/4953906 to your computer and use it in GitHub Desktop.
Save hypersoft/4953906 to your computer and use it in GitHub Desktop.
Automatic I/O loop executive with environment sanitation, predefined macro source expansion, and optional script output to file, for consecutive cached calls / common operations / examination. The generated script, or executed function takes multiple files, or stdin as its arguments.
lib.input.file.forEach ()
{
# BOOT STRAPS
[[ "$1$2$3" == --buildprintfxchar ]] && {
echo "builtin printf -- '\\x`builtin printf '%02X' "'${4}"`';";
return;
}
[[ "$1$2$3" == --buildparservars ]] && {
declare IFS=''
echo "declare FILE=\"\";"
echo "declare RS=\"\$(`$FUNCNAME --build printf xchar ${RS:-$'\n'}`)\"; "
echo "declare -i IRL=0 RL=${RL:-1} RC=0 FNO=0 SPOS=0 FPOS=0 EOF=0 EOS=0; "
echo "declare -a TRAP[0]=\"true\";"
return;
}
[[ $1 == --build ]] && { "$FUNCNAME: $1 $2" || return; }
[[ "$1$2$3" == --listparservars ]] && {
echo FILE RS IRL RL RC FNO SPOS FPOS EOF EOS TRAP\;
return;
}
[[ "$1$2$3" == --listinternalvars ]] && {
echo _RECALL_{LABEL,INIT,SCRIPT,THIS,EXACT,EXPORT,HEAD,RECORD_AS};
return;
}
[[ $1 == --list ]] && { "$FUNCNAME: $1 $2" || return; }
#MACRO SECTION, transforms %identifier to 'var label'
[[ "$1$2" == --parseidentifiers ]] && {
declare -u ID="$3";
IFS= declare SCRIPT="$(< /dev/stdin)"
SCRIPT="${SCRIPT//\%record\.length/IRL}";
SCRIPT="${SCRIPT//\%match/BASH_REMATCH}";
SCRIPT="${SCRIPT//\%record\.number/RC}";
SCRIPT="${SCRIPT//\%record/$ID}";
SCRIPT="${SCRIPT//\%source\.position/FPOS}";
SCRIPT="${SCRIPT//\%source\.end/EOF}"; # boolean 1 true: last record
SCRIPT="${SCRIPT//\%source/FILE}";
SCRIPT="${SCRIPT//\%stream\.number/FNO}";
SCRIPT="${SCRIPT//\%stream\.count/#}";
SCRIPT="${SCRIPT//\%stream\.position/SPOS}";
SCRIPT="${SCRIPT//\%stream\.end/EOS}"; # boolean 1 true: last source file
SCRIPT="${SCRIPT//\%stream/@}";
printf %s "${SCRIPT}";
return;
}
[[ $1 == --parse ]] && { "$FUNCNAME: $1 $2" || return; }
# END BOOT STRAPS
declare -u _RECALL_LABEL=$1 || return; shift;
declare -i _RECALL_EXPORT=0;
declare $_RECALL_LABEL='' _RECALL_INIT='' _RECALL_SCRIPT='' \
_RECALL_EXACT="IFS=''" _RECALL_HEAD='' || return;
declare _RECALL_THIS=$(declare NOW=`date +%s`;
printf %08X $(( SECONDS * RANDOM ))
printf -- .%08X \
$(( ( RANDOM * 314 ) + NOW )) \
$(( SECONDS * RANDOM )) \
$(( ( RANDOM * $$ ) + NOW ));
printf '\n';
);
# These are the types of processing loops that are predefined.
if [[ $_RECALL_LABEL == RECORD ]]; then declare _RECALL_RECORD_AS='-rd"$RS"';
elif [[ $_RECALL_LABEL == LINE ]]; then
declare _RECALL_RECORD_AS='-rd"$RS"';
builtin printf -vRS '\n';
elif [[ $_RECALL_LABEL == CHUNK ]]; then declare _RECALL_RECORD_AS='-rN${RL}';
elif [[ $_RECALL_LABEL == CHAR ]]; then declare _RECALL_RECORD_AS='-rN1';
elif [[ $_RECALL_LABEL == FORMAT ]]; then declare _RECALL_RECORD_AS="$2";
else "$FUNCNAME $_RECALL_LABEL:"; return; fi;
[[ $_RECALL_RECORD_AS == "$1" ]] && shift;
# baked means: read will interpret backslashes
if [[ $1 == baked ]]; then
shift;
_RECALL_RECORD_AS=${_RECALL_RECORD_AS/-r/-};
fi;
# opposite of baked
if [[ $1 == raw ]]; then
shift;
"$FUNCNAME: raw is implied, and cannot be reversed" || return;
fi;
declare FILE="" RS=${RS:-`builtin printf '\n'`};
declare -i IRL=0 RL=${RL:-1} RC=0 FNO=0 SPOS=0 FPOS=0 EOF=0 EOS=0;
declare -a TRAP;
if [[ $_RECALL_LABEL =~ ^(RECORD|CHUNK|FORMAT)$ ]]; then
while [[ $2 == '=' && $# -ge 3 ]]; do
if [[ "$1$2" == 'delimiter=' ]]; then
builtin printf -vRS "%b" "$3" || return;
shift 3;
continue;
fi;
if [[ "$1$2" == 'length=' ]]; then
builtin printf -vRL "%i" "$3" || return;
shift 3;
continue;
fi;
"$FUNCNAME: $_RECALL_LABEL: unknown declaration: $1 $2 $3" || return;
break;
done;
fi;
[[ $1 == -i ]] && {
shift; (( $# )) || "$FUNCNAME: option -i: missing script argument" || return
(( ${#1} )) || "$FUNCNAME: option -i: invalid argument: \`'" || return;
builtin printf -v _RECALL_INIT %s "${1}"; shift;
}
while [[ $# -ne 0 && ${1:0:2} == -a ]]; do
shift; (( $# )) || "$FUNCNAME: option -a: missing script argument" || return;
(( ${#1} )) || "$FUNCNAME: option -a: invalid argument: \`'" || return;
builtin printf -v _RECALL_SCRIPT %s\\n "${_RECALL_SCRIPT}${1}";
shift;
done;
[[ -z "$_RECALL_SCRIPT" ]] && { # don't sleep on me
(( $# )) || "$FUNCNAME: default: missing script argument" || return;
(( ${#1} )) || "$FUNCNAME: option default: invalid argument: \`'" || return;
builtin printf -v _RECALL_SCRIPT %s "${1}";
shift;
}
_RECALL_SCRIPT="$(
{ IFS='' echo -En "$_RECALL_SCRIPT"; } | \
$FUNCNAME --parse identifiers $_RECALL_LABEL
)";
[[ $1 == -- ]] || {
[[ $1 == -o ]] && {
_RECALL_THIS="$2";
_RECALL_HEAD="#!`type -p bash`"$'\n\n'"`$FUNCNAME --build parser vars;`";
_RECALL_EXPORT=1;
}
} && shift;
[[ $_RECALL_EXPORT == 1 ]] || {
_RECALL_HEAD="rm $_RECALL_THIS;"$'\n'"unset -v `
$FUNCNAME --list internal vars`;"
}
cat <<EOF > $_RECALL_THIS
$_RECALL_HEAD
new.shared.return.trap ()
{
TRAP[\${#TRAP[@]}]="\${*}";
}
# custom init section for autotrap ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# new.shared.return.trap 'echo user trap'
$_RECALL_INIT
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
$_RECALL_THIS ()
{
$_RECALL_SCRIPT
}
_auto_return_trap () {
unset -f $_RECALL_THIS new.shared.return.trap;
unset -f \$FUNCNAME;
unset -v TRAP;
}
new.shared.return.trap _auto_return_trap;
builtin trap 'for trap in "\${TRAP[@]}"; do eval \$trap; done; unset trap' RETURN;
for FILE in "\${@:-/dev/stdin}"; do
let FPOS=0 EOF=0 FNO++;
(( (\$# - FNO) == 0 )) && let EOS=1;
while (( ! EOF )); do
$_RECALL_EXACT builtin read $_RECALL_RECORD_AS $_RECALL_LABEL || EOF=1;
let IRL=\$((\${#$_RECALL_LABEL}));
{ (( EOF == 1 )) && (( FPOS == 0 )) && (( IRL == 0 )); } || {
let IRL++; # count delimiter
let FPOS+=\$IRL SPOS+=\$IRL RC++
let IRL--; # restore length
}
# execute parser
$_RECALL_THIS || { declare -i e=\$?;
return \$e 2>/dev/null || exit \$e;
}
done < "\$FILE";
done
EOF
[[ $_RECALL_EXPORT == 1 ]] || { builtin source $_RECALL_THIS "$@"; return; }
}
@hypersoft
Copy link
Author

Automatically generated I/O parser. Methods can be custom defined by delimiter or length. char, chunk and line are popular methods. Demonstration of line method follows.

The parser tries to keep track of bytes read, and records read, attempting to read source from a list of file parameters or stdin. If an output file is not specified, a temporary file is created, and the data is sourced from that file, otherwise, an -o option specifies a new script resource exhibiting its own behavior. lib.input.file.forEach bootstraps itself by performing a regular expression match and replace on auto-exported source environment variables. The %ish variable names are special to the function shortcuts for the declared environment.

$ echo hello | lib.input.file.forEach line '(( %- )) && echo $LINE || echo -n $LINE;'
hello

$ lib.input.file.forEach '(( %- )) && echo -E $%_ || echo -En $%_;' -o /dev/stdout;

#!/bin/bash

declare -- RS=$'\n';
declare -- RL="1";
declare -- LINE="";
declare -- FILE="";
declare -i FPOS="0";
declare -i EOF="0";
declare -i RC="0";

builtin trap 'for trap in "${TRAP[@]}"; do eval $trap; done; unset trap' RETURN;

new.shared.return.trap()
{
    TRAP[${#TRAP[@]}]="${*}";
}

_auto_return_trap() {
    unset -f /dev/stdout new.shared.return.trap _auto_return_trap;
    unset -v TRAP;
}

# custom init section for autotrap 
# new.shared.return.trap 'echo user trap'
# -

new.shared.return.trap _auto_return_trap;

/dev/stdout ()
{
(( EOF )) && echo -E $LINE || echo -En $LINE;

}

for FILE in "${@:-/dev/stdin}"; do
    EOF == 0;
    while (( ! EOF )); do
        IFS='' builtin read -rd"$RS" LINE || EOF=1;
        let FPOS+=${#LINE} RC++;
        [[ "$LINE" == $'\0' ]] && let FPOS++;
        /dev/stdout || { return $? 2>/dev/null || exit $?; }
    done < "$FILE";
done

@hypersoft
Copy link
Author

The above generated source is from a prior version of this code.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment