#!/bin/bash
#
#
# Copyright (C) 2006-2015 Daniele de Rigo.
#
# This file is part of Mastrave/sh.
# Mastrave/sh is the Bash-oriented part of Mastrave.
#
# Mastrave/sh is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Mastrave/sh is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Mastrave. If not, see .
#
#
version=0.2.3
progname=$(basename $0)
config=$(uname -opm)
package='Mastrave shell utils'
mtv_base_path=$( cd $(dirname $0)/../../../; pwd )
mtv_sh_base_path=$( cd $(dirname $0); pwd )
mtv_sh_command_path=$( cd $(dirname $0); pwd )/bin
mtv_sh_subfunc_path=$( cd $(dirname $0); pwd )/include
mtv_m_base_path=$( cd "$mtv_sh_base_path/../"; pwd )/mtv_m
mtv_cpp_base_path=$( cd "$mtv_sh_base_path/../"; pwd )/mtv_cpp
mtv_php_base_path=$( cd "$mtv_sh_base_path/../"; pwd )/mtv_php
mtv_pl_base_path=$( cd "$mtv_sh_base_path/../"; pwd )/mtv_pl
mtv_xslt_base_path=$( cd "$mtv_sh_base_path/../"; pwd )/mtv_xslt
version( )
{
cat << EOF
$progname ($package) $version
Copyright (C) 2006-2015 Daniele de Rigo.
License GPLv3+: GNU GPL version 3 or later
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
EOF
}
usage()
{
cat << EOF
Usage: $progname [OPTIONS] | command [[arg]... | sub-function [[arg]... ]
The Mastrave/sh library is part of the Mastrave project.
It is a collection of bash utilities aiming to ease the adoption of an
abstract and vectorized approach to the GNU command line.
Mastrave/sh does not attempt to replicate the main set of Mastrave utilities
for GNU Octave and MATLAB computing environments, since the Bash shell is
pretty different from them.
Despite Bash scripting should not be intended as an array programming
language, it natively allows array operations on variables and files.
Mastrave/sh provides a set of tools which strengthen the vectorizability of
Bash programs.
OPTIONS:
--help : shows this message.
--version : outputs version information and exit.
-l, --list : lists the available Mastrave/sh commands.
A given command can be invoked by typing:
$progname ...
--list-subs : lists the available Mastrave/sh sub-functions.
Sub-functions are expected to be loaded and used
within another piece of code ( e.g. another
script). Nevertheless, they can also be invoked
separately (standalone) by typing:
$progname ...
--help-sub SUBFUN : shows the help of the sub-function SUBFUN of
Mastrave/sh.
-m, --m-files : returns the path of the Mastrave modules which
can be invoked within a GNU Octave / MATLAB
computing environment.
-b, --bin-commands : returns the path of the Mastrave/sh commands
which can be invoked within any Bash script or
directly from the command-line.
-i, --include : returns the path of the Mastrave/sh sub-functions
which can be invoked within any Bash script. In
order for your Bash script to invoke a given
Mastrave/sh sub-function , just add
these lines within your script:
mtv_path=\$( mastrave -i )
source "\$mtv_path/"
# ... add your stuffs ...
# ...
# ... invoke the Mastrave/sh sub-funciton:
arg1 arg2 ...
--install PATH : installs Mastrave in the path PATH.
Administration privileges might be required.
After installation, the features of Mastrave/sh
are available invoking the command:
mastrave [OPTIONS] | [[arg]...]
where is either a Mastrave/sh command
or a Mastrave/sh sub-function.
If PATH is omitted, the default path is
/usr/local/bin/.
In order for the installation to succeed,
the complete Mastrave package is expected to be
locally available, its root path being:
$mtv_base_path
If you prefer to install Mastrave from internet,
you should instead use the --net-install option.
--net-install PATH : installs Mastrave from internet in the path PATH.
In order for the installation to succeed, an
internet connection is required. No additional
files of Mastrave are required but for the
Mastrave manager.
You should download the latest version of the
Mastrave manager from:
http://mastrave.org/net-install
See also: http://mastrave.org/download.html
Administration privileges might be required.
After installation, the features of Mastrave/sh
are available invoking the command:
mastrave [OPTIONS] | [[arg]... ]]
where is either a Mastrave/sh command
or a Mastrave/sh sub-function.
If PATH is omitted, the default path is
/usr/local/bin/.
--update : updates the Mastrave library accessing the online
Mastrave repository with anonymous csv.
ARGUMENT:
: command or sub-function to be executed.
... : one or more arguments to be passed to .
Example of usage [GNU Bash]:
# Internet installation in the home directory
mkdir -p "$HOME/bin"
mkdir -p "$HOME/opt/mastrave"
cd "$HOME/opt/mastrave"
# 1. Ensure the Mastrave manager is updated
wget --content-disposition http://mastrave.org/net-install
# 2. Ensure the Mastrave manager is updated
bash mastrave --net-install "$HOME/bin"
# Invoking as standalone a Mastrave/sh sub-function
tmp=\$( mastrave tmp_dir --create )
# Invoking a Mastrave/sh command (by also passing an argument via stdin)
echo '10 60 20 80 30 20' | tr ' ' '\n' | mastrave msort -i "\$tmp"/idx
cat "\$tmp"/idx
mastrave tmp_dir --remove "\$tmp"
Example of usage [GNU Octave/MATLAB]:
% Invoking as standalone a Mastrave/sh sub-function
tmp = mastrave( 'tmp_dir', '--create' )
% Invoking a Mastrave/sh command
mastrave( '-', [10 60 20 80 30 20].', 'msort', '-i', [ tmp '/idx'] )
fileread( [ tmp '/idx'] ).'
mastrave( 'tmp_dir', '--remove', tmp )
EOF
}
#
# Returns either:
# - the command path is installed
# - nothing otherwise
get_bin()
{
# Separate bash is invoked in order for recent modifications
# to be considered
local command="$1"
local matches=$(
echo "$( bash -c "whereis -b '$command'" )" |
tr -d -c ' ' | wc -c | tr -d -c '[0-9]'
)
if [ "_$matches" != "_0" ] ; then
echo "$( bash -c "whereis -b '$command'" )" |
tr -s ' ' | cut -d' ' -f2 | tr -d '\n\r\t'
fi
}
tmpfile()
{
tmpfunc=$( get_bin tempfile )
if [ "_$tmpfunc" == "_" ]; then
tmpfunc=$( get_bin mktemp )
fi
if [ "_$tmpfunc" == "_" ]; then
( cat <&2
fi
"$tmpfunc"
}
#
is_writable()
{
local path=$( echo "$1" | sed -r 's!/$!!' )
local iswritable=0;
mkdir -p "$path" || ( printf "$iswritable"; exit 0 )
local testfile="$path/.mtv${RANDOM}_${RANDOM}_${RANDOM}_${RANDOM}_${RANDOM}"
while [ -e $testfile ]
do
local testfile="$path/.mtv${RANDOM}_${RANDOM}_${RANDOM}_${RANDOM}_${RANDOM}"
done
touch "$testfile" 2>/dev/null && rm -f "$testfile" && iswritable=1;
printf "$iswritable"
}
# Intended for GNU Octave
# computing environment
update_startup_mfile()
{
local computing_env="$1"
local path=$( echo "$2" | sed -r 's!/$!!' )
local iswritable="$( is_writable "$path" )"
if [ "_$iswritable" == "_0" ] ; then
local mroot=$( (
$computing_env -q --eval \
'fprintf(2,"\n%s",matlabroot);exit' 1>/dev/null
) 2>&1 | tail -1 )
local mversion=$( (
$computing_env -q --eval \
'fprintf(2,"\n%s",OCTAVE_VERSION);exit' 1>/dev/null
) 2>&1 | tail -1 )
echo " mroot: $mroot"
echo " mversiont: $mversion"
local rcpath=$(
find "$mroot/share/octave" -name octaverc |
grep -E "$mversion"
)
local rcdir=$(dirname "$rcpath")
else
local rcdir="$( cd ~; pwd )"
local rcpath="$rcdir/.octaverc"
fi
local tmppath="$( tmpfile )"
local command=$( cat< '$tmppath'
cp '$tmppath' '$rcpath'
rm -f '$tmppath'
EOF
)
echo "command: [$command]"
su_eval "$rcdir" "$command" "$command" 'installation on GNU Octave'
# Safety tmp file removal (under normal flow it is superflous)
rm -f "$tmppath"
}
#
# Returns either:
# - 1 if is in $PATH
# - 0 otherwise
do_path_exist()
{
local desired_path="$1"
answer=$(
echo "$PATH" |
tr ':' '\n' | grep -E '^'"$desired_path" | wc -c | tr -d '\n\r\t'
)
if [ "_$answer" != "_0" ] ; then
echo -n '1'
else
echo -n '0'
fi
}
#
#
# Returns either:
# - 1 if is in
# - 0 otherwise
do_completion_exist()
{
local desired_completion="$1"
local profile="$2"
answer=$(
cat "$profile" |
sed -r 's!#.*!!' |
grep "$desired_completion" | wc -l | tr -d '\n\r\t'
)
if [ "_$answer" != "_0" ] ; then
echo -n '1'
else
echo -n '0'
fi
}
#
#
ensure_path()
{
local desired_path="$1"
local profile="$2"
local new_full_path=$( cat <> "$profile"
else
echo "$new_full_path" > "$profile"
fi
fi
}
#
#
ensure_completion()
{
local desired_completion="$1"
local profile="$2"
local new_full_completion=$( cat <> "$profile"
fi
else
echo "$new_full_completion" > "$profile"
fi
}
# generate a temporary bash script with the passed commands
#
#
mk_tmpcommand()
{
local tmpcommand=$( tmpfile )
local commands="$1"
( cat < "$tmpcommand"
printf '%s' "$tmpcommand"
}
# eval a command od depending on
# whether is writable by the current user or not.
#
#
#
#
#
su_eval()
{
local path=$( echo "$1" | sed -r 's!/$!!' )
local command="$2"
local su_command="$3"
local task_descr="$4"
local iswritable="$( is_writable "$path" )"
if [ "_$iswritable" == "_0" ] ; then
# local eval_cmd="$su_command"
local tmpcommand=$( mk_tmpcommand "$su_command" )
local sudo_=$( get_bin sudo )
if [ "_$sudo_" == "_" ] ; then
local su_=$( get_bin su )
if [ "_$su_" == "_" ] ; then
printf 'Error. Unable to complete the $s.' "$task_descr"
echo "Please login as privileged user and repeat the task."
exit 1
fi
echo "In order to complete the task, administration privileges" \
"are required"
echo "Invoking the administration tool $su_ ..."
$su_ -c "bash \"$tmpcommand\""
else
echo "In order to complete the task, administration privileges" \
"are required"
echo "Invoking the administration tool $sudo_ ..."
$sudo_ -i "bash \"$tmpcommand\""
fi
else
# unprivileged
local tmpcommand=$( mk_tmpcommand "$command" )
# eval "$command"
bash "$tmpcommand"
fi
rm -f "$tmpcommand"
}
#
install()
{
local redirect=$( cat << EOF
#!/bin/bash
$mtv_sh_base_path/$progname \$@
EOF
)
local tmpcompletionfile=$( tmpfile )
( cat < "$tmpcompletionfile"
local completionfile='bash_completion.d/mastrave'
printf "The manager to be installed is:\n\n$redirect\n\n"
local path=$( echo "$1" | sed -r 's!/$!!' )
local mtv="mastrave"
local cmd="$path/$mtv"
printf "The manager will be installed at:\n\n$cmd\n\n"
local command=$( cat< '$cmd' && chmod 755 '$cmd'
ensure_path "$path" "$HOME/.bash_profile"
ensure_path "$path" "$HOME/.bashrc"
mkdir -p "$( dirname "$HOME/.$completionfile" )"
mv "$tmpcompletionfile" "$HOME/.$completionfile"
ensure_completion "$HOME/.$completionfile" "$HOME/.bash_profile"
ensure_completion "$HOME/.$completionfile" "$HOME/.bashrc"
[ "_$( do_path_exist "$path" )" == "_0" ] && PATH="$PATH:$path" && export PATH
EOF
)
local su_command=$( cat< '$cmd' && chmod 755 '$cmd' && chown root:root '$cmd'
mv "$tmpcompletionfile" "/etc/${completionfile}"
if [ "_$( do_completion_exist "/etc/${completionfile}" "$HOME/.bash_profile" )" == "_0" ]; then
echo "source /etc/${completionfile}" >> "$HOME/.bash_profile"
fi
if [ "_$( do_completion_exist "/etc/${completionfile}" "$HOME/.bashrc" )" == "_0" ]; then
echo "source /etc/${completionfile}" >> "$HOME/.bashrc"
fi
EOF
)
# local su_command=$(
# printf "echo '%s' > '%s' && chmod 755 '%s' && chown root:root '%s';cp '%s' '%s'" \
# "$redirect" "$cmd" "$cmd" "$cmd" "$tmpcompletionfile" \
# '/etc/bash_completion.d/mastrave'
# )
su_eval "$path" "$command" "$su_command" 'installation'
echo "Requested installation path: '$cmd'"
echo "Current installation path: '$( get_bin $mtv )'"
if [ "_$( get_bin $mtv )" == "_$cmd" ]; then
echo "Mastrave/sh successfully installed at:"
ls "$cmd"
elif [ "_$( get_bin $mtv )" == "_" ]; then
echo "Mastrave seems to have been installed for the first time"
echo "Mastrave/sh should successfully have been installed at:"
ls "$cmd"
else
echo "Error: requested installation path differs from the" \
"current installation path"
echo "Error: removing spurious installation files..."
su_eval "$path" "rm '$cmd'" "rm '$cmd'" 'disinstallation'
echo "Error: Mastrave/sh installation aborted."
exit 1
fi
local comput_env=octave
for i in $(
( eval $(
echo ${PATH}: |
sed -r 's!([^:]+):!ls \1/'"$comput_env"'*;!g'
) 2>/dev/null
) | sed -r 's!.*/!!' | sort | uniq | grep -E "$comput_env[-0-9.]*$"
); do
echo "Updating startup file of computing environment $i ...";
echo $( printf 'update_startup_mfile "$(get_bin %s)" "$path"\n' "$i" );
eval $( printf 'update_startup_mfile "$(get_bin %s)" "$path"\n' "$i" );
done
# update_startup_mfile "$(get_bin octave)" "$path"
exit 0
}
#
#
net_install()
{
local path=$( echo "$1" | sed -r 's!/$!!' )
local mtv_base_path=$( echo "$2" | sed -r 's!/$!!' )
local path=$( cd "$path"; pwd )
echo "Installing Mastrave from internet (remote cvs repository:"
echo " http://cvs.savannah.gnu.org/viewvc/mastrave/ ..."
local cvs_=$( get_bin cvs )
if [ "_$cvs_" == "_" ] ; then
echo 'Error. Unable to find the required tool' \
'Concurrent Versions System (cvs)'
echo "Please install it before repeating the task."
exit 1
fi
local command=$( cat<
update()
{
echo "Updating Mastrave (local cvs repository: '$mtv_base_path')..."
local cvs_=$( get_bin cvs )
if [ "_$cvs_" = "_" ] ; then
echo 'Error. Unable to find the required tool' \
'Concurrent Versions System (cvs)'
echo "Please install it before repeating the task."
exit 1
fi
local command=$( cat<
get_subfun_help()
{
local subfuncs=$( get_subfuncs )
local subfun="$1"
is_valid_subfun=$(
echo "$subfuncs" |
grep "^$subfun$" |
wc -l
)
if [ "_$is_valid_subfun" = '_1' ]; then
local subfun_help="$(
source $mtv_sh_subfunc_path/$subfun.sh;
printf "mastrave/sh sub-function: %s\n\n" \
"$subfun version $( $subfun --version )"
$subfun --help
)"
echo "$subfun_help"
else
printf "Error : unrecognized sub-function '%s'\n\n" "$subfun"
printf ' Type %s --list-subs for listing valid sub-functions.\n\n' \
"$progname"
exit 1
fi
}
if [ $# -lt 1 ] ; then
usage
exit 1
fi
# getopt is unsuitable for the special purpose of this dispatcher
case "$1"
in
--help|--hel|--he|--h)
usage
exit 0
;;
--version|--versio|--versi|--vers|--ver|--ve|--v)
version
exit 0
;;
-l|--list|--lis|--li|--l)
commands=$( get_commands )
printf "mastrave/sh commands:\n%s\n" "$commands"
exit 0
;;
--list-subs|--list-sub|--list-su|--list-s|--list-)
subfuncs=$( get_subfuncs )
printf "mastrave/sh sub-functions:\n%s\n" "$subfuncs"
exit 0
;;
--help-sub|--help-su|--help-s|--help-)
get_subfun_help "$2"
exit 0
;;
-i|--include|--includ|--inclu|--incl|--inc|--in|--i)
printf "%s" "$mtv_sh_subfunc_path"
exit 0
;;
-b|--bin-commands|--bin-command|--bin-comman|--bin-comman|--bin-comman|--bin-comma|--bin-comm|--bin-com|--bin-co|--bin-c|--bin-|--bin|--bi|--b)
printf "%s" "$mtv_sh_command_path"
exit 0
;;
-m|--m-files|--m-file|--m-fil|--m-fi|--m-f|--m-|--m)
printf "%s" "$mtv_m_base_path"
exit 0
;;
--install)
if [ $# -lt 2 ] ; then
path='/usr/local/bin/'
else
path="$2"
fi
install "$path"
exit 0
;;
--net-install)
if [ $# -lt 2 ] ; then
path='/usr/local/bin/'
else
path="$2"
fi
net_install "$path" "$mtv_sh_base_path"
exit 0
;;
--update)
update
exit 0
;;
-*|--*)
printf 'Error: unrecognized option "%s".\n\n' "$1"
printf ' Type %s --help to list valid options.\n\n' "$progname"
exit 1
;;
*)
;;
esac
commands=$( get_commands )
is_valid_command=$(
echo "$commands" |
grep "^$1$" |
wc -l
)
if [ "_$is_valid_command" = '_1' ]; then
command="$mtv_sh_command_path/$1.sh"
shift
$command "$@"
else
subfuncs=$( get_subfuncs )
is_valid_subfun=$(
echo "$subfuncs" |
grep "^$1$" |
wc -l
)
if [ "_$is_valid_subfun" = '_1' ]; then
subfun="$1"
shift
subfun_out="$(
source $mtv_sh_subfunc_path/$subfun.sh;
$subfun "$@"
)"
echo "$subfun_out"
else
printf "Error : unrecognized command or sub-function '%s'\n\n" "$1"
printf ' Type %s --list for listing valid commands.\n' \
"$progname"
printf ' Type %s --list-subs for listing valid sub-functions.\n\n' \
"$progname"
exit 1
fi
fi