nutools/foreach

305 lines
9.8 KiB
Bash
Executable File

#!/bin/bash
# -*- coding: utf-8 mode: sh -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8
source "$(dirname -- "$0")/lib/ulib/auto" || exit 1
function display_help() {
uecho "$scriptname: lancer une commande pour un ensemble d'arguments
USAGE
$scriptname [options] [VAR=]expr cmd...
$scriptname [options] [VAR=]items... -- cmd...
La commande est lancée avec la variable 'VAR', qui vaut par défaut 'item',
définie à la valeur courante de l'énumération. De plus, la variable 'index'
reçoit une valeur incrémentale commençant à 0.
La deuxième syntaxe appelée syntaxe alternative permet de spécifier un ensemble
d'éléments directement, mais nécessite l'utilisation du séparateur '--' pour
identifier où s'arrête la liste des éléments.
En plus de VAR, les variables file, dir, name, basename, ext et dotext sont
définies. Elle valent respectivement le chemin absolu du fichier, le répertoire
absolu du fichier, le nom du fichier, le nom de base sans extension, l'extension
sans le point et l'extension avec le point, e.g pour item=dir/file.ext on a
file=/path/to/dir/file.ext, dir=/path/to/dir, name=file.ext, basename=file,
ext=ext, dotext=.ext
Les 3 exemples suivants sont équivalents:
$scriptname '*.c' cp %item dest/dir
$scriptname item='*.c' cp %item dest/dir
$scriptname *.c -- cp %item dest/dir
OPTIONS
-b, --basedir BASEDIR
Chercher les expressions -d, -f, -a à partir de BASEDIR au lieu du
répertoire courant.
-d, --dir
-f, --file
-a, --all
-s, --string
Ces options permettent de spécifier le type d'expression et la façon de
les traiter. Avec -d, faire la correspondance de l'expression sur les
répertoires uniquement. Avec -f, faire la correspondance sur les fichier
uniquement. Avec -a, faire la correspondance sur les répertoires et les
fichiers. Avec -s, évaluer l'expression avec la fonction 'eval' du
shell.
Si la syntaxe alternative est utilisée, le premier élément est évalué
conformément à ces options *si et seulement s'il* est précédé d'une
chaine 'VAR='. Les autres éléments sont pris tels-quels. e.g:
cette commande affiche uniquement la chaine '*.c':
$scriptname '*.c' --
alors que celle-ci liste les fichiers qui ont l'extension '.c':
$scriptname 'item=*.c' --
-p, --parent
Pour chaque fichier/répertoire, se placer dans le répertoire parent
avant de lancer la commande. item est aussi modifié pour ne plus
contenir que le nom de la cible (qui est désormais dans le répertoire
courant)
Cette option a la priorité sur --go
-g, --go
Si la cible est un répertoire, en faire le répertoire courant avant de
lancer la commande. Si la cible est un fichier, faire comme --parent et
se placer dans le répertoire du fichier avant de lancer la commande.
Dans les deux cas, item est modifié pour ne plus contenir que le nom de
la cible, soit '.' pour le répertoire courant, soit le nom du fichier
dans le répertoire courant.
Si cette option est mentionnée seule, elle implique --dir
-x, --expand
-n, --no-expand
Reconnaitre et traiter (resp. ne pas reconnaitre) la syntaxe %var dans
les arguments. C'est le cas par défaut, ce qui permet de simplifier
l'écriture d'une ligne de commande. Pour écrire le caractère '%', il
suffit de le doubler e.g %%item
Si l'expansion est désactivée, il faut protéger le caractère \$ pour
qu'il soit traité, e.g, avec les examples ci-dessus:
$scriptname -n '*.c' 'cp \"\$item\" dest/dir'
--title
--pt, --parent-title
--nt, --no-title
Afficher (resp. ne pas afficher) chaque correspondance avant de lancer
la commande. Par défaut, l'affichage est effectué.
Avec --parent-title, pour chaque correspondance afficher plutôt le
répertoire parent (valide uniquement avec l'option -p)
-S, --git-status
Equivalent à spécifier la commande 'git status --p'
Utiliser par exemple avec -g, -G ou -C
-G, --git-projects
Equivalent à '--ptitle -p */.git --' e.g '$scriptname -G git pull' pour
mettre à jour les dépôts situés dans un répertoire
-C, --composer-projects
Equivalent à '--ptitle -p */composer.json --'
--cc, --composer-cmd
Equivalent à '--ptitle -p */composer.json -- dk composer'"
}
basedir=
match=auto
changedir=
parentdir=
expand=1
title=auto
command=
shortcut=
args=(+
--help '$exit_with display_help'
-b:,--basedir: basedir=
-d,--dir match=dir
-f,-file match=file
-a,--all match=all
-p,--parent parentdir=1
-g,--go changedir=1
-s,--string match=string
-x,--expand expand=1
-n,--no-expand expand=
--title title=1
--pt,--parent-title title=p
--nt,--no-title title=
-S,--git-status command=git-status-p
-G,--git-projects shortcut=git
-C,--composer-projects shortcut=composer
--cc,--composer-cmd shortcut=composer-cmd
)
parse_args "$@"; set -- "${args[@]}"
case "$command" in
git-status-p)
if [ -n "$shortcut" ]; then
set -- git status --p "$@"
else
set -- "$@" -- git status --p
fi
;;
esac
case "$shortcut" in
git)
set -- */.git -- "$@"
parentdir=1
[ "$title" == auto ] && title=p
;;
composer)
set -- */composer.json -- "$@"
parentdir=1
[ "$title" == auto ] && title=p
;;
composer-cmd)
set -- */composer.json -- "$scriptdir/dk" composer "$@"
parentdir=1
[ "$title" == auto ] && title=p
;;
esac
if [ "$match" == auto ]; then
[ -n "$changedir" ] && match=dir || match=all
fi
[ "$title" == auto ] && title=1
alt_syntax=
for sep in "$@"; do
if [ "$sep" == -- ]; then
alt_syntax=1
break
fi
done
suppl_items=()
varexpr="$1"; shift
if [ -n "$alt_syntax" ]; then
while [ $# -gt 0 ]; do
[ "$1" == -- ] && break
array_add suppl_items "$1"
shift
done
[ "$1" == -- ] && shift
fi
splitfsep2 "$varexpr" = varname expr
if [ -n "$alt_syntax" ]; then
if [ -n "$varname" ]; then
[ -n "$expr" ] || expr="*"
elif [[ "$varexpr" == *=* ]]; then
[ -n "$varname" ] || varname=item
[ -n "$expr" ] || expr="*"
else
items=("$varexpr")
varname=item
expr=
fi
else
[ -n "$varname" ] || varname=item
[ -n "$expr" ] || expr="*"
fi
if [ -n "$expr" ]; then
if [ -n "$basedir" ]; then
case "$match" in
all) array_lsall items "$basedir" "$expr";;
dir) array_lsdirs items "$basedir" "$expr";;
file) array_lsfiles items "$basedir" "$expr";;
string) eval "items=($expr)";;
esac
else
basedir=.
case "$match" in
all) array_from_lines items "$(list_all "$basedir" "$expr")";;
dir) array_from_lines items "$(list_dirs "$basedir" "$expr")";;
file) array_from_lines items "$(list_files "$basedir" "$expr")";;
string) eval "items=($expr)";;
esac
fi
fi
array_extend items suppl_items
cmd=("$@")
if [ ${#cmd[*]} -eq 0 ]; then
if [ -n "$expand" ]; then
cmd=(echo %item)
else
cmd=('echo "$item"')
fi
fi
[ -n "$title" ] && einfo "${#items[@]} correspondance(s) trouvée(s)"
let index=0
for item in "${items[@]}"; do
if [ -n "$parentdir" -a "$title" == p ]; then
etitle "$(dirname -- "$item")"
elif [ -n "$title" ]; then
etitle "$item"
fi
i="$item"
setx file=abspath "$item"
setx dir=dirname -- "$file"
setx name=basename -- "$file"
splitname "$name" basename ext
[[ "$name" == *.* ]] && dotext=".$ext" || dotext=
# ne pas changer les noms des variables sans modifier le script awk
# ci-dessous
__VARS=(i file dir name basename ext dotext)
export "${__VARS[@]}"
(
if [ -n "$changedir" ]; then
if [ -f "$item" ]; then
parentdir=1
elif [ ! -d "$item" ]; then
ewarn "$item: n'est pas un répertoire"
changedir=
fi
fi
if [ -n "$parentdir" ]; then
cd "$dir"
item="$name"
elif [ -n "$changedir" ]; then
cd "$item"
item=.
fi
i="$item"
__VALUES=()
for __VALUE in "${__VARS[@]}"; do
array_add __VALUES "$__VALUE=${!__VALUE}"
done
if [ -n "$expand" ]; then
array_from_lines cmd "$(awkrun -f "${__VALUES[@]}" xitem="$index" item="$item" varname="$varname" cmd[@] '
function replace_vars(src, re, vs, dest) {
re = "(%(" varname "|i|file|dir|name|basename|ext|dotext|index))"
while (src != "") {
if (match(src, "%%") != 0) {
dest = dest substr(src, 1, RSTART)
src = substr(src, RSTART + RLENGTH)
} else if (match(src, "%" re) != 0) {
dest = dest substr(src, 1, RSTART)
src = substr(src, RSTART + RLENGTH)
} else if (match(src, re, vs) != 0) {
dest = dest substr(src, 1, RSTART - 1)
src = substr(src, RSTART + RLENGTH)
} else {
break
}
var = vs[2]
if (var == varname) dest = dest item
else if (var == "i") dest = dest item
else if (var == "file") dest = dest file
else if (var == "dir") dest = dest dir
else if (var == "name") dest = dest name
else if (var == "basename") dest = dest basename
else if (var == "ext") dest = dest ext
else if (var == "dotext") dest = dest dotext
else if (var == "index") dest = dest xitem
}
if (src != "") dest = dest src
return dest
}
BEGIN {
for (i=1; i<=cmd_count; i++) {
arg = replace_vars(cmd[i])
print arg
}
}')"
eval "export $(echo_setv "$varname" "$item")"
"${cmd[@]}"
else
eval "export $(echo_setv "$varname" "$item"); ${cmd[*]}"
fi
)
[ -n "$title" ] && eend "$item"
let index=$index+1
done