Contenu | Rechercher | Menus

Annonce

Si vous avez des soucis pour rester connecté, déconnectez-vous puis reconnectez-vous depuis ce lien en cochant la case
Me connecter automatiquement lors de mes prochaines visites.

À propos de l'équipe du forum.

#1 Le 04/09/2018, à 00:36

Arbiel

Césure d'une chaîne de caractères passée en argument à une fonction

Bonsoir à tous

J'éprouve une grande difficulté à extraire les mots d'une chaîne de caractères en la passant comme argument à une fonction qui doit travailler sur chacun de ces mots. Que j'utilise l'une quelconque des formulations "${*}", ${*}, "${@}" ou ${@} ne change rien à l'affaire. À ce sujet le manuel bash indique

3.4.2 Special Parameters

The shell treats several parameters specially. These parameters may only be referenced; assignment to them is not allowed.

*

    ($*) Expands to the positional parameters, starting from one. When the expansion is not within double quotes, each positional parameter expands to a separate word. In contexts where it is performed, those words are subject to further word splitting and pathname expansion. When the expansion occurs within double quotes, it expands to a single word with the value of each parameter separated by the first character of the IFS special variable. That is, "$*" is equivalent to "$1c$2c…", where c is the first character of the value of the IFS variable. If IFS is unset, the parameters are separated by spaces. If IFS is null, the parameters are joined without intervening separators.
@

    ($@) Expands to the positional parameters, starting from one. When the expansion occurs within double quotes, each parameter expands to a separate word. That is, "$@" is equivalent to "$1" "$2" …. If the double-quoted expansion occurs within a word, the expansion of the first parameter is joined with the beginning part of the original word, and the expansion of the last parameter is joined with the last part of the original word. When there are no positional parameters, "$@" and $@ expand to nothing (i.e., they are removed).


J'ai introduit dans le script, dont je donne le code en fin de message, des fonctions prises dans un script plus volumineux pour illustrer cette difficulté. J'ai enregistré ce script sur mon bureau. Les fonctions sont appelées directement pas une commande telle qui (voir la dernière ligne du script)
bash '/home/remi/Bureau/test.sh' fonction arguments

Voici ce que font ces fonctions

- test : permet d'activer la trace bash dans le fichier /tmp/xtrace. Il faut alors passer la commande
bash '/home/remi/Bureau/test.sh' test fonction arguments

- permutations : produit les permutations des arguments

remi@remi-Vostro-3550:~$ bash '/home/remi/Bureau/test.sh' permutations a b c
 a|b|c b|c|a b|a|c a|c|b c|b|a c|a|b
remi@remi-Vostro-3550:~$ 

- combinaisons : produit les combinaisons des arguments

remi@remi-Vostro-3550:~$ bash '/home/remi/Bureau/test.sh' combinaisons a b c
a  a|b a|b|c a|c b  b|c c
remi@remi-Vostro-3550:~$ 

- ev : affiche sur /dev/stderr la valeur du paramètre

remi@remi-Vostro-3550:~$ param="exemple"  bash '/home/remi/Bureau/test.sh' ev param
declare -x param="exemple"
remi@remi-Vostro-3550:~$ 

(dans les commandes précédentes, j'ai désactivé l'affichage en commentant la ligne 3 du script).

- xcompose : doit produire les lignes d'un fichier Xcompose pour la création des caractères marqués de diacritiques du grec ancien (j'ai conçu un clavier "bépo", et j'ai constaté que le fichier Xcompose système n'est pas complet, et ne répond pas à mes besoins pour des raisons qu'il n'est pas utile de préciser dans le cadre de la présente demande d'assistance).

En activant l'affichage de la fonction ev, voila ce que j'obtiens (j'ai marqué les lignes que je mentionne plus bas avec un chiffre entre crochets) :

remi@remi-Vostro-3550:~$ bash '/home/remi/Bureau/test.sh' xcompose
declare -- bloc="Grec étendu"
declare -- carg="ᾓ"
declare -- codeU="U1F93"
declare -- codeh="e1-be-93"
declare -- lettre="êta"
[1]declare -- liste_dia="<dead_dasia> <dead_grave> <dead_iota>"
declare -- comment="Lettre minuscule grecque êta esprit rude accent grave et iota souscrit"
declare -- proc="gen_compose"
declare -- liste_dia="<dead_dasia> <dead_grave> <dead_iota>"
[2]declare -- combi=""
declare -- proc="gen_lignes"
[3]declare -- pa="<dead_dasia> <dead_grave> <dead_iota>"
declare -- ne="<dead_dasia> <dead_grave> <dead_iota>"
declare -- dia="<dead_dasia> <dead_grave> <dead_iota>"
<dead_dasia> <dead_grave> <dead_iota> <> : "ᾓ" U1F93 # Lettre minuscule grecque êta esprit rude accent grave et iota souscrit
declare -- combi="<dead_dasia> <dead_grave> <dead_iota>"
declare -- proc="gen_lignes"
declare -- pa="<dead_dasia> <dead_grave> <dead_iota> "
declare -- ne="<dead_dasia> <dead_grave> <dead_iota> "
declare -- dia="<dead_dasia> <dead_grave> <dead_iota> "
<dead_dasia> <dead_grave> <dead_iota>  <> : "ᾓ" U1F93 # Lettre minuscule grecque êta esprit rude accent grave et iota souscrit
declare -- bloc="Grec étendu"
declare -- carg="ᾔ"
declare -- codeU="U1F94"
declare -- codeh="e1-be-94"
declare -- lettre="êta"
declare -- liste_dia="<dead_acute> <dead_iota> <dead_psili>"
declare -- comment="Lettre minuscule grecque êta esprit doux accent aigu et iota souscrit"
declare -- proc="gen_compose"
declare -- liste_dia="<dead_acute> <dead_iota> <dead_psili>"
declare -- combi=""
declare -- proc="gen_lignes"
declare -- pa="<dead_acute> <dead_iota> <dead_psili>"
declare -- ne="<dead_acute> <dead_iota> <dead_psili>"
declare -- dia="<dead_acute> <dead_iota> <dead_psili>"
<dead_acute> <dead_iota> <dead_psili> <> : "ᾔ" U1F94 # Lettre minuscule grecque êta esprit doux accent aigu et iota souscrit
declare -- combi="<dead_acute> <dead_iota> <dead_psili>"
declare -- proc="gen_lignes"
declare -- pa="<dead_acute> <dead_iota> <dead_psili> "
declare -- ne="<dead_acute> <dead_iota> <dead_psili> "
declare -- dia="<dead_acute> <dead_iota> <dead_psili> "
<dead_acute> <dead_iota> <dead_psili>  <> : "ᾔ" U1F94 # Lettre minuscule grecque êta esprit doux accent aigu et iota souscrit
declare -- bloc="Grec étendu"
declare -- carg="ᾕ"
declare -- codeU="U1F95"
declare -- codeh="e1-be-95"
declare -- lettre="êta"
declare -- liste_dia="<dead_acute> <dead_dasia> <dead_iota>"
declare -- comment="Lettre minuscule grecque êta esprit rude accent aigu et iota souscrit"
declare -- proc="gen_compose"
declare -- liste_dia="<dead_acute> <dead_dasia> <dead_iota>"
declare -- combi=""
declare -- proc="gen_lignes"
declare -- pa="<dead_acute> <dead_dasia> <dead_iota>"
declare -- ne="<dead_acute> <dead_dasia> <dead_iota>"
declare -- dia="<dead_acute> <dead_dasia> <dead_iota>"
<dead_acute> <dead_dasia> <dead_iota> <> : "ᾕ" U1F95 # Lettre minuscule grecque êta esprit rude accent aigu et iota souscrit
declare -- combi="<dead_acute> <dead_dasia> <dead_iota>"
declare -- proc="gen_lignes"
declare -- pa="<dead_acute> <dead_dasia> <dead_iota> "
declare -- ne="<dead_acute> <dead_dasia> <dead_iota> "
declare -- dia="<dead_acute> <dead_dasia> <dead_iota> "
<dead_acute> <dead_dasia> <dead_iota>  <> : "ᾕ" U1F95 # Lettre minuscule grecque êta esprit rude accent aigu et iota souscrit
remi@remi-Vostro-3550:~$ 

Je ne comprends pas la valeur de "pa" (ligne 3 ci-dessus,  ligne 110 du script), alors que pa est initialisé, à la ligne 109, par

pa=${1}

c'est-à-dire par le premier argument de la fonction gen_lignes. Celle-ci est appelée à la ligne 121 par

[[ -z "${combi}" ]] && gen_lignes ${@} || gen_lignes $(retirer "${combi}" "${liste_dia}") ;

combi étant "" (ligne 2, 119 du script), c'est gen_lignes ${@} qui est exécutée. Les paramètres sont initialisés par la ligne 130

[[ -n "${liste_dia}" ]] && gen_compose ${liste_dia}

La ligne 1 présente la valeur de "liste_dia", dont voici le contenu précis

remi@remi-Vostro-3550:~$ echo "<dead_dasia> <dead_grave> <dead_iota>" | hexdump -C
00000000  3c 64 65 61 64 5f 64 61  73 69 61 3e 20 3c 64 65  |<dead_dasia> <de|
00000010  61 64 5f 67 72 61 76 65  3e 20 3c 64 65 61 64 5f  |ad_grave> <dead_|
00000020  69 6f 74 61 3e 0a                                 |iota>.|
00000026
remi@remi-Vostro-3550:~$ 

qui montre clairement que les mots "<dead_…>" sont bien séparés par des espaces.

Texte du script

#! /bin/bash
	function ev {
		for _ in "${@}"; do declare -p ${_} > /dev/stderr ; done ;
		: ;
		} ;
	
############################################################
# Activation de la trace
#############################################################
	function test {
		gbl_test=true;
		exec 6> /tmp/xtrace
		export BASH_XTRACEFD=6
		set -o xtrace
		"${@}";
		set +o xtrace
	}

#######################################################
# Fonctions combinatoires
#######################################################

# Les deux fonctions "permutations" et "combinaisons" permutent et combinent leurs arguments
# et produisent les chaînes correspondantes sur la sortie standard.
# Les chaînes sont séparées les unes des autres par un " " et dans chaque chaîne, les paramètres le sont par un "|".
# Le caractère "-" est utilisé lors de la construction des chaînes de sorte que
# les caractères " ", "-" et "|" ne doivent pas apparaître dans les paramètres.
# L'ordre des paramètres est préservé dans les chaînes ainsi créées.
# Ces deux procédures sont récursives.


#############################################################
# Calcul des permutations des termes passés en paramètres
##############################################################
	function permutations {
		local x="";
		local l="";
		local ne="${1}";
		ev ne
		if [[ $((${#})) -le 1 ]]; then
# lorsque le dernier élément est atteint, on en retourne la valeur pour lancer la constitution des permutations
			l="${1}" ;
		else
# créer toutes les permutations de rang immédiatement inférieur après retrait du premier élément mémoriser dans ${ne}
			shift;
# récursivité : 
			for x in $(permutations "${@}"); do
# créer deux nouveaux éléments, un avec le premier élément réintroduit en tête, l'autre avec le premier élément réintroduit en queue
				l="${l} ${ne}-${x} ${x}-${ne}";
# jusqu'à ce qu'il n'y ait plus de | dans la suite rendue par le niveau inférieur
				until [[ "${x}" = "${x%|*}" ]]; do
# introduire dans les permutations une nouvelle permutation en remplaçant successivement chaque occurrence de | par le nouvel élément
					l="${l} ${x/|/-${ne}-}";
# remplacer la première | par un - pour reculer l'insertion du premier élément dans la permutation à suivre
					x="${x/|/-}"
					done;
				done;
# réinsérer des | dans toutes les permutations pour le niveau supérieur
			l=${l//-/|};
		fi;
	echo "${l}"
	l=;
	};
	
#############################################################
# calcul des combinaisons, autres que la combinaison vide, des éléments d'un ensemble
##############################################################
	function combinaisons {
		local l
		function cb {
			local al;
			local ne="${1}";
			if [[ $((${#})) -le 1 ]]; then
				l="${1}" ;
			else
				shift 1;
				al="$(cb "${@}")";
				l="${ne} ${l}";
				for elem in ${al}; do
# ajouter à la liste une combinaison contenant le nouvel élément en plus des éléments précédents
					l="${l} ${ne}|${elem}";
					done;
				l="${l} ${al}"
			fi;
			echo "${l}" ;
			}			
		cb ${@}
	}
##########################################
# Enlever les chaînes "retrait" des chaînes "ensemble"
##########################################$
	function retirer {
		local fic_retrait="$(tempfile --prefix="ucg_" --suffix=".txt")";
		local fic_ensemble="$(tempfile --prefix="ucg_" --suffix=".txt")";
		tr "|" "\n" <<< "${1}" > "${fic_retrait}";
		tr " " "\n" <<< "${2}" > "${fic_ensemble}";
		grep -v -f "${fic_retrait}" "${fic_ensemble}" | tr "\n" " " ;
		}
		
	function ecrire_ligne {
		echo "${permutation} <${1}> : \"${carg}\" ${codeU} # ${comment}" ;
		} ;
				
	function xcompose {
		function gen_compose {
			function gen_lignes {
				proc="gen_lignes"
				ev proc
				pa=${1}
				ev pa
				for dia in $(permutations ${@}) ; do
					ev dia
					permutation="$(echo ${dia} | tr "|" " ")";
					ecrire_ligne "${codeU_base}";
					done;
				}
			proc="gen_compose"; ev proc ;
			ev liste_dia
			for combi in "" $(combinaisons ${@}) ; do
				ev combi
				[[ -z "${combi}" ]] && gen_lignes ${@} || gen_lignes $(retirer "${combi}" "${liste_dia}") ;
				done;
			}
		ifs="${IFS}"
		IFS=":"
		cat "${gbl_dir_alphabets}"/*.alp \
		  | while read bloc carg codeU codeh lettre liste_dia comment bloc; do
			ev bloc carg codeU codeh lettre liste_dia comment
			[[ -z "${carg}${codeU}${codeh}" ]] && break;
			[[ -n "${liste_dia}" ]] && gen_compose ${liste_dia} ; # >> "${gbl_dir_ucg}/xcompose_${bloc}.cps" ;
			done
		IFS="${ifs}";
		}

gbl_dir_alphabets="$(mktemp -d)"
cat 1>"${gbl_dir_alphabets}/grec.alp" <<AUIE
:ᾓ:U1F93:e1-be-93:êta:<dead_dasia> <dead_grave> <dead_iota>:Lettre minuscule grecque êta esprit rude accent grave et iota souscrit:Grec étendu:
:ᾔ:U1F94:e1-be-94:êta:<dead_acute> <dead_iota> <dead_psili>:Lettre minuscule grecque êta esprit doux accent aigu et iota souscrit:Grec étendu:
:ᾕ:U1F95:e1-be-95:êta:<dead_acute> <dead_dasia> <dead_iota>:Lettre minuscule grecque êta esprit rude accent aigu et iota souscrit:Grec étendu:
AUIE
"${@}";

Merci par avance à quiconque pourra m'expliquer pourquoi la chaîne <dead_dasia> <dead_grave> <dead_iota> est passée en un seul argument et non pas en trois arguments.

Arbiel


Arbiel Perlacremaz
LDLC Aurore NK3S-8-S4 Ubuntu 20.04
Abandon d'azerty au profit de bépo, de google au profit de Lilo et de la messagerie électronique violable au profit de Protonmail, une messagerie chiffrée de poste de travail à poste de travail.

Hors ligne

#2 Le 04/09/2018, à 01:45

Watael

Re : Césure d'une chaîne de caractères passée en argument à une fonction

salut,

déclarer des fonctions dans des fonctions n'a aucun intérêt.

il peut être périlleux de modifier l'IFS durablement.

while IFS=':' read a b c
do
   :
done <fichier
cat <<mot >fichier #par défaut c'est toujours &1
:
mot

c'est plus lisible, amha.

mais ça ne va pas résoudre ton souci. sad


Connected \o/
Welcome to sHell. · eval is evil.

Hors ligne

#3 Le 04/09/2018, à 11:13

Arbiel

Re : Césure d'une chaîne de caractères passée en argument à une fonction

Bonjour

Merci pour tes conseils, qui, comme tu l'avoues, ne résolvent pas mon problème.

Pour ce qui est de l'inclusion de fonctions dans des fonctions, c'est une habitude que j'avais prise, et que j'utilise maintenant beaucoup moins. Je l'utilise essentiellement pour simplifier du code avec des conditions imbriquées devenu illisible au fil de diverses modifications. Cela me permet de conserver les instructions déportées dans la fonction proches du code ainsi simplifié.

Pour IFS, je peux effectivement limiter la portée de sa modification, pour éviter de perturber le fonctionnement d'autres fonctions dans lesquelles il intervient, à mon insu. Mais nul n'est censé ignorer…

Pour revenir à mon problème, je me demande si le comportement qui me surprend ne découle pas d'une option de shopt. Je viens de les relire, et je n'ai pas vraiment compris

Manuel bash a écrit :

extquote

    If set, $'string' and $"string" quoting is performed within ${parameter} expansions enclosed in double quotes. This option is enabled by default.

Par ailleurs, je pense aussi pouvoir contourner le problème en écrivant un fichier avec l'appel de ma ou de mes fonctions, pour l'exécuter ensuite avec source. Cela revient pratiquement au même que "eval", que tu détestes, mais cela risque fort de fonctionner.

Merci encore, et si tu as d'autres idées, n'hésite pas à me les communiquer.

Arbiel


Arbiel Perlacremaz
LDLC Aurore NK3S-8-S4 Ubuntu 20.04
Abandon d'azerty au profit de bépo, de google au profit de Lilo et de la messagerie électronique violable au profit de Protonmail, une messagerie chiffrée de poste de travail à poste de travail.

Hors ligne