BASH traitement sur l'ensemble des arguments

Fermé
gnugo - 20 janv. 2012 à 15:49
dubcek Messages postés 18722 Date d'inscription lundi 15 janvier 2007 Statut Contributeur Dernière intervention 9 mai 2024 - 25 janv. 2012 à 08:01
Bonjour,
et merci d'avance si vous pouvez me venir en aide!

J'ai besoin d'effectuer un traitement sur les arguments que je passe à un script bash.
J'ai donc à ma disposition 2 variables :
- $* qui est, si je ne me trompe pas, la concaténation de tous les arguments en un seul
GROS PROBLÈME : Si un de mes arguments contient un espace, impossible par la suite de le séparer correctement des autres ( ar1 "arg 2" arg3 => "arg1 arg 2 arg3" )
- $@ qui est, si je ne me trompe pas nom plus, une variable "spéciale" si laquelle on peut itérer avec une boucle for par exemple, et ainsi traiter un par un chaque argument ( arg, puis "arg 2", puis arg3 )
PROBLÈME : Ça ne me convient pas du tout, je ne souhaite pas itérer sur chaque argument, mais appliquer un traitement sur "l'ensemble de mes arguments", traitement incluant des espaces et des sauts de lignes.

Mes questions : Puis-je faire un traitement sur $@ sans faire boucle ? Comment sont séparés les arguments dans $@ ?

Merci

16 réponses

dubcek Messages postés 18722 Date d'inscription lundi 15 janvier 2007 Statut Contributeur Dernière intervention 9 mai 2024 5 615
Modifié par dubcek le 20/01/2012 à 16:03
hello
en changeant IFS et avec des ", on peut avoir un autre caractère qu'espace comme séparateur
$ set "a 1"  "b 2" "c 3" 
$ echo $* 
a 1 b 2 c 3 
$ echo "$*" 
a 1 b 2 c 3 
$ IFS=";" 
$ echo "$*" 
a 1;b 2;c 3 
$ 
0
Merci,
ça répond vraiment bien à ce que je cherche, je peux alors travailler avec $* sans soucis, mais...
maintenant il me faut trouver bon séparateur.

Mes paramètres sont des noms de fichiers, pourrais-tu me dire s'il y a un caractère ne pouvant pas faire parti des noms de fichiers, que je pourrais utiliser ?
0
Et différent de '/' , car j'ai aussi les chemins dans mes paramètres
0
dubcek Messages postés 18722 Date d'inscription lundi 15 janvier 2007 Statut Contributeur Dernière intervention 9 mai 2024 5 615
20 janv. 2012 à 16:39
tout les caractères sont autorisés dans les noms, mais je dirais:
; puisque c'est un séparateur de commande, # commentaires, etc
0

Vous n’avez pas trouvé la réponse que vous recherchez ?

Posez votre question
Oui mais si j'ai un fichier avec un de ces caractères qui sont autorisés, ça ne marchera pas
Ce serait une faille dans mon script que de faire un truc pareil.

Du coup je ne vois pas comment faire... à part en me débrouillant en itérant dans une boucle pour reconstruire ma variable, mais c'est justement pour éviter de faire ça que je suis venu chercher de l'aide.

$@ arrive bien à les séparer pourtant, il fait comment lui ?
0
zipe31 Messages postés 36402 Date d'inscription dimanche 7 novembre 2010 Statut Contributeur Dernière intervention 27 janvier 2021 6 408
20 janv. 2012 à 16:51
Salut,

je ne souhaite pas itérer sur chaque argument, mais appliquer un traitement sur "l'ensemble de mes arguments", traitement incluant des espaces et des sauts de lignes.
Pour aller plus vite et gagner du temps :
Quels traitements comptes-tu faire ?
De quelles façons ?

0
dubcek Messages postés 18722 Date d'inscription lundi 15 janvier 2007 Statut Contributeur Dernière intervention 9 mai 2024 5 615
Modifié par dubcek le 20/01/2012 à 16:59
$@ affiche l'ensemble des $1, $2, etc

tester si le caractère est présent dans $*, si oui en prendre un autre
$  set "a 1"  "b 2" "c;3" 
$  
$ echo "$*" 
a 1 b 2 c;3 
$  
$ grep -q ";"  <<<"$*"
$ echo $? 
0 
$  
$ IFS="#"  # on utilise # et pas ; 
$  
$ echo "$*" 
a 1#b 2#c;3 
$  
$  
0
-->Réponse à zipe31 :
Bah en fait peu importe le traitement, je sais que je pourrai éventuellement le faire avec une boucle mais ce n'est pas cette solution que je cherche, même si elle fonctionnerait parfaitement.

Pour simplifier au maximum, disons que j'ai en arguments des noms de fichiers
Et je souhaite en faire une liste dans une seule variable qui contiendra alors tous mes noms de fichiers séparés par un caractère.
Ensuite, je passe cette liste à un programme exécutable qui lui effectuera le traitement.

Mais je ne trouve pas comment séparer mes noms de fichiers puisque tous les caractères peuvent être utilisés.
Pourtant je constate que dans $@ ils semblent correctement séparés, donc ça doit être possible.

J'espère être suffisamment clair. N'hésitez pas à me poser des questions si ça ne l'est pas.
Et encore merci


-->À ducek :

Intéressant mais si dans mes arguments j'ai tous les caractères possibles de noms de fichiers, ce qui est possible, le script ne peut plus rien faire,non?

Cette façon de faire à l'air en plus très compliquée, tester tous les caractères possibles pour en trouver un de libre...ça fait beaucoup
En mettant directement Le séparateur qu'il faut (celui de $@ peut être?) ça sera quand même beaucoup plus simple et efficace
0
zipe31 Messages postés 36402 Date d'inscription dimanche 7 novembre 2010 Statut Contributeur Dernière intervention 27 janvier 2021 6 408
20 janv. 2012 à 17:27
Normalement le traitement de "$@" (guillemets compris) devrait faire l'affaire.

$ cat plop 
Salut

$ cat mon\ fich 
bonjour

$ set plop "mon fich"

$ cat $@
Salut
cat: mon: Aucun fichier ou dossier de ce type
cat: fich: Aucun fichier ou dossier de ce type

$ cat "$@"
Salut
bonjour

$ 

;-))
0
Effectivement j'ai fait plusieurs tests avec cat et ça marche !
Merci
Mais alors pour en finir, les noms sont séparés avec quoi dans "$@" ?

Et puis-je effectuer des traitements de type sed sur $@ tout en conservant sa structure ?
0
zipe31 Messages postés 36402 Date d'inscription dimanche 7 novembre 2010 Statut Contributeur Dernière intervention 27 janvier 2021 6 408
20 janv. 2012 à 18:07
Mais alors pour en finir, les noms sont séparés avec quoi dans "$@" ?
Par un caractère null.
0
c'est le fameux \0 ?
0
Il y a un moyen de le reproduire dans un script ?
ex : aa="fichier1\0fichier2"
0
Rebonsoir,
Alors j'ai un peu creusé là dessus et mon traitement fonctionne parfaitement si je sépare mes fichiers par le caractère NUL ( '\0' )
J'ai pu faire les tests grâce à l'option -print0 de find qui me sépare bien les fichiers avec un '\0'

Maintenant il faut juste que je fasse la même chose sans find
Comment mettre sur la sortie de mon script la liste de ses arguments séparés par ce caractère ?

En effet, je dois passer cette liste à l'entrée standard de mon programme de traitement
J'ai essayé des choses comme : ./traitement <<<"$@" mais pas concluant

(Je suis ouvert à toute suggestion cette fois :] )
0
Utilisateur anonyme
20 janv. 2012 à 18:59
salut,

bash ne sait pas manipuler le caractère NULL.

pourquoi pas une boucle ?
pourquoi pas find ?
0
Je veux bien faire une boucle si au final j'obtiens ma liste
Quelque chose comme

for f in "$@"
do
   liste="$liste\0$f"
done


Conviendrait parfaitement.

Et find ne convient pas car je ne souhaite traiter qu'une liste de fichiers précis.

./script fic1 file5 "aaa bb" | traitement
find recherche des fichiers. dans mon cas je connais déjà les fichiers que je veux traiter. Find peut prendre ma liste et me la renvoyer au bon format avec print0 ?
0
Bon alors pour l'instant j'ai fait ça :

for f in "$@"
do
echo -n "$f"
echo -ne "\0"
done | traitement

Ça marche, mais c'est pas terrible je trouve, surtout quand on pense que "$@" est déjà au bon format.

Voilà. Qu'en pensez-vous? Comment auriez-vous fait ?
0
Utilisateur anonyme
20 janv. 2012 à 19:48
ah, j'ai oublié : pourquoi «./traitement <<<"$@" [...] pas concluant » ?
$ premiere() {  deuxieme "$@";}
$ deuxieme() { printf '%s\n' "$@";}
$ premiere foo bar "bar baz"
foo
bar
bar baz
là, c'est des fonctions, mais ce serait pareil avec des scripts.
0
oui ça ça marche mais là tu passes "$@" en argument à deuxieme()

Alors que j'ai besoin d'envoyer "$@" sur l'entrée standard de mon script, et là ça ne marche plus.
Le echo "$@" | script
ou script <<<"$@"
enlève les '\0' de "$@"
0
un conseil évident : réécrit le script pour qu'il accepte des arguments.
0
Non c'est justement ce qu'il faisait au départ.
Mais parfois le traitement est amené a travailler sur un grand nombre de fichiers, Et quand il y a trop de noms j'ai une erreur "argument line too long"

Voilà pourquoi je cherche maintenant à faire passer cette liste sur l'entrée standard. Comme ça , pas de limite, plus de problème
0
Utilisateur anonyme
20 janv. 2012 à 21:20
montre-nous le script, parce que comme ça...
0
Bah je vois pas ou est le problème je pense avoir donné tous les détails
Je n'ai plus le script ni le prog de traitement sous les yeux

Si vous voulez tester le même comportement:

#Version avec lecture de l'entrée standard
for f in "$@"
do
echo -n "$f"
echo -ne "\0"
done | ssh 127.0.0.1 cat

#Version utilisant les paramètres
ssh 127.0.0.1 "echo \"$@\""

Les 2 fonctionnent mais... faites le test avec 4000 noms de fichiers dans "$@" , vous verrez une belle erreur too many arguments
Si vous avez une solution pour éviter le pb tout en conservant le fonctionnement par arguments, je suis ouvert
Mais à mon avis avec une liste de fichiers pouvant atteindre une grosse taille, un traitement par flux dans l'entrée standard c'est mieux que la ligne de commande limitée
0
J'ai une autre interrogation sur le même sujet :

Est-il possible de récupérer avec la commande ls une liste de fichiers pour la traiter en arguments ?
C'est à dire : "./script $(ls)" pour obtenir la liste des fichiers dans la liste des arguments "$@" du script.
Ça ne marche pas avec un fichier contenant un espace, il pense qu'il y a 2 fichiers. "aa bb" donne "aa" et "bb" dans "$@"
En faisant ./script "$(ls)" c'est pire, il considère toute la liste de fichiers comme un seul
0
dubcek Messages postés 18722 Date d'inscription lundi 15 janvier 2007 Statut Contributeur Dernière intervention 9 mai 2024 5 615
23 janv. 2012 à 11:21
essayer
$ IFS=$'\n' ; ./script $(ls -1) 
# ou
$ ./script  *
0
Utilisateur anonyme
23 janv. 2012 à 11:13
normalement
./script *
devrait traiter tous les fichiers du répertoire courant
0
Effectivement le IFS=$'\n' ; peut être une bonne solution mais il le problème c'est qu'il faudra le taper à chaque fois, on ne peut pas l'automatiser ou le mettre dans le script car la "transformation" du retour du 'ls' et même le ls lui même qui renvoie un mauvais résultat(\n séparateur) se fait en amont. (ls sépare par des \n alors que c'est un caractère possible pour les noms de fichiers...dommage). Donc soit je l'utilise et je fais avec son résultat sachant à quoi m'en tenir, soit je ne l'utilise pas.

En tout cas mon souci premier est résolu et je vous remercie pour votre aide !
Merci
0
dubcek Messages postés 18722 Date d'inscription lundi 15 janvier 2007 Statut Contributeur Dernière intervention 9 mai 2024 5 615
25 janv. 2012 à 08:01
l'option -print0 de la commande find remplace le \n par \0 après chaque nom de fichier
$ find . -maxdepth 1 -print0
0