#!/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