BASH traitement sur l'ensemble des arguments

gnugo -  
dubcek Messages postés 18627 Date d'inscription   Statut Contributeur Dernière intervention   -
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

  1. dubcek Messages postés 18627 Date d'inscription   Statut Contributeur Dernière intervention   5 660
     
    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
  2. gnugo
     
    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
  3. gnugo
     
    Et différent de '/' , car j'ai aussi les chemins dans mes paramètres
    0
  4. dubcek Messages postés 18627 Date d'inscription   Statut Contributeur Dernière intervention   5 660
     
    tout les caractères sont autorisés dans les noms, mais je dirais:
    ; puisque c'est un séparateur de commande, # commentaires, etc
    0
  5. Vous n’avez pas trouvé la réponse que vous recherchez ?

    Posez votre question
  6. gnugo
     
    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
  7. zipe31 Messages postés 34620 Date d'inscription   Statut Contributeur Dernière intervention   6 501
     
    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
  8. dubcek Messages postés 18627 Date d'inscription   Statut Contributeur Dernière intervention   5 660
     
    $@ 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
  9. gnugo
     
    -->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
    1. zipe31 Messages postés 34620 Date d'inscription   Statut Contributeur Dernière intervention   6 501
       
      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
    2. gnugo
       
      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
    3. zipe31 Messages postés 34620 Date d'inscription   Statut Contributeur Dernière intervention   6 501
       
      Mais alors pour en finir, les noms sont séparés avec quoi dans "$@" ?
      Par un caractère null.
      0
    4. gnugo
       
      c'est le fameux \0 ?
      0
    5. gnugo
       
      Il y a un moyen de le reproduire dans un script ?
      ex : aa="fichier1\0fichier2"
      0
  10. gnugo
     
    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
  11. Utilisateur anonyme
     
    salut,

    bash ne sait pas manipuler le caractère NULL.

    pourquoi pas une boucle ?
    pourquoi pas find ?
    0
  12. gnugo
     
    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
  13. gnugo
     
    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
  14. Utilisateur anonyme
     
    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
    1. gnugo
       
      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
    2. Utilisateur anonyme
       
      un conseil évident : réécrit le script pour qu'il accepte des arguments.
      0
    3. gnugo
       
      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
    4. Utilisateur anonyme
       
      montre-nous le script, parce que comme ça...
      0
    5. gnugo
       
      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
  15. gnugo
     
    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
    1. dubcek Messages postés 18627 Date d'inscription   Statut Contributeur Dernière intervention   5 660
       
      essayer
      $ IFS=$'\n' ; ./script $(ls -1) 
      # ou
      $ ./script  *
      
      0
  16. Utilisateur anonyme
     
    normalement
    ./script *
    devrait traiter tous les fichiers du répertoire courant
    0
  17. gnugo
     
    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
    1. dubcek Messages postés 18627 Date d'inscription   Statut Contributeur Dernière intervention   5 660
       
      l'option -print0 de la commande find remplace le \n par \0 après chaque nom de fichier
      $ find . -maxdepth 1 -print0
      0