[bash]Éviter de devoir doubler 1boucle for si fichier ou dossier

Résolu
Swiss Knight Messages postés 1956 Date d'inscription   Statut Membre Dernière intervention   -  
Swiss Knight Messages postés 1956 Date d'inscription   Statut Membre Dernière intervention   -
Salut,

Je fais un petit script pour pouvoir signer des photos, et je souhaite pouvoir lui passer en argument ;
- soit le nom d'un fichier.
- soit le nom d'un répertoire.
pour l'instant du moins...

Dans ce script, j'ai une boucle "for" relativement conséquente et je ne trouve pas le moyen de ne pas la dédouble entièrement en fonction du type (fichier ou dossier) d'argument donnée en entrée.

for i in "${fichier}"/*.[Jj][Pp][Gg]; do j='echo "${i}" | sed -e 's/\.jpg/\_processed.jpg/gI''; \


Si l'argument passé est un dossier, son nom sera contenu dans la variable ${fichier} et la boucle travaille sur tous les fichiers jpg de ce dossier. Nickel.
Mais si l'argument passé est un simple fichier, dont l'extension est déjà .jpg, ça nous donne un truc pas drôle du genre "photo.jpg/*.[Jj][Pp][Gg] et bien sûr plus rien ne fonctionne après puisqu'il ne reconnait pas le fichier dans la boucle.

Merci par avance de vos tuyaux !

ps : question bonus, mais c'est pas grave si y a pas de solution :
for i in "${fichier}"/*.[Jj][Pp]([Ee])?[Gg]; do j='echo "${i}" | sed -e 's/\.jpg/\_processed.jpg/gI''; \

j'ai une erreur avec ça, apparemment les parenthèses ne sont pas appréciées... comment faire pour lui dire que le 'e' (jpg vs jpeg) est optionnel ?

"Si vous ne pouvez expliquer un concept à un enfant de six ans, c'est que vous ne le comprenez pas complètement." -A. Einsten-
A voir également:

4 réponses

zipe31 Messages postés 36402 Date d'inscription   Statut Contributeur Dernière intervention   6 431
 
Salut,

1 - Déterminer le type du paramètre, soit avec "getopts", soit avec une commande (file ou stat) et appliquer une commande différente pour chacun. Si c'est un fichier seul la boucle est inutile, seule la commande "sed" doit être exécutée.

2 - for i in *.[jJ][pP]*[gG]; do...
0
Swiss Knight Messages postés 1956 Date d'inscription   Statut Membre Dernière intervention   110
 
Salut zipe,

merci pour la partie 2) ça marche.
j'ai juste dû changer un peu le sed qui suivait :
for i in "${fichier}"/*.[Jj][Pp]*[Gg]; do j='echo "${i}" | sed -e 's/\.jp\(e\)*g/\_processed.jp\1g/gI''; \


pour la partie 1) j'ai ça dans les getopt :

while getopts ":d: :hs: :e" option; do   
    case "${option}"   
    in   
        d) fichier="${OPTARG}";;   
        e) exifdata="oui";;   
        \?)   
        echo "Invalid option: -${OPTARG}. Possible options are : -d, -e and -h.   
                    See help [-h] for more informations." >&2   
        exit 1;;   
        :)   
        echo "Option -${OPTARG} requires an argument." >&2   
        exit 1;;   
        h) echo "${help}"   
        exit;;   
        s) seed=${OPTARG};;   
    esac   
done


Et ensuite, je fais un test avant la boucle for :
if [ ! -e "${fichier}" ]; then   
 echo "${fichier} does not exist, please input a valid folder or file name."   
 exit   
elif [ -f "${fichier}" ]; then   
 echo "${fichier} is a file."   
 for.......   
elif [ -d "${fichier}" ]; then   
 for......


sauf que là j'ai pas d'autre choix que de mettre à double l'intégralité de la boucle for où il y a les trois points de suspension :
une fois pour le cas où ${fichier} est un fichier.
et une fois pour le cas où ${fichier} est un dossier.

et c'est ce que j'ai envie d'éviter parce qu'il y aura juste le nom du fichier à traiter qui changera dans toutes les commandes qui sont traitées dans la boucle for, et il y en a un paquet.

"Si vous ne pouvez expliquer un concept à un enfant de six ans, c'est que vous ne le comprenez pas complètement." -A. Einsten-
0
zipe31 Messages postés 36402 Date d'inscription   Statut Contributeur Dernière intervention   6 431
 
j'ai juste dû changer un peu le sed qui suivait :
Ceci devrait aller mieux ;-\
sed 's#\..*#_processed&#'

elif [ -f "${fichier}" ]; then
echo "${fichier} is a file."

Là pas besoin de boucle, juste la commande "sed".
0
Swiss Knight Messages postés 1956 Date d'inscription   Statut Membre Dernière intervention   110
 
Re-salut,

En fait, j'ai besoin de passer les éventuelles majuscules des extensions en minuscules, d'où le sed que j'utilisais. Sinon c'est évident que ta solution est meilleure pour le renommage des fichiers.

Pis pour la boucle for, il n'y a pas que le sed, mais une quantité innombrable de commandes imagemagick qui suivent, je dois donc pouvoir les appliquer sur un fichier seul, ou à tous les fichiers d'un répertoire.
Donc le : "juste la commande sed" se traduit par : "juste la commande sed et toutes les commandes qui suivent jusqu'à la fin de la boucle for", or c'est justement ce que je souhaite éviter parce que ça me ferait deux énormes blocs de commandes quasiment identiques dans mon code. Et je reste persuadé qu'on peut n'en faire qu'un (mais peut-être que ce n'est pas possible quand même...je sais pas)
0
zipe31 Messages postés 36402 Date d'inscription   Statut Contributeur Dernière intervention   6 431
 
En fait, j'ai besoin de passer les éventuelles majuscules des extensions en minuscules, d'où le sed que j'utilisais.
sed 's#\..*#_processed\L&#'


Pour le reste, tu mets ta commande sed et les autres dans une fonction que tu appelles ensuite précédée d'un for ou pas selon le type (fichier ou répertoire).
0
Swiss Knight Messages postés 1956 Date d'inscription   Statut Membre Dernière intervention   110
 
Salut, merci pour le tuyau du \L
Ça a l'air de jouer ! Je ne connaissais pas !

Je veux éviter aussi de multiplier les fichiers ; ce script étant déjà assez spécifique.

Par contre je me demande si en transformant le for i in file en un bête for i in ls ${fichier} ne permettrait pas d'y arriver ?!

Je teste ça dès que possible.
Surtout qu'en plus d'un fichier unique passé en argument, je souhaite, à terme, pouvoir donner à manger à mon script :
- un fichier,
- plusieurs fichiers,
- un dossier (où il traitera tous les fichiers contenus dans ce dossier)
0
Swiss Knight Messages postés 1956 Date d'inscription   Statut Membre Dernière intervention   110
 
Re Salut par ici !!

Il y a apparemment un petit couac avec ta commande sed zipe :
Elle me retourne un truc bizarre (je ne m'en aperçois que maintenant).

Ma précédente commande :
$ echo "./Desktop/test/image.JPG" | sed -e 's/\.jp\(e\)*g/\_processed.jp\1g/gI'
./Desktop/test/image_processed.jpg


Ta commande :
$ echo "./Desktop/test/image.JPG" | sed 's#\..*#_processed\L&#'
_processed./desktop/test/image.jpg


Non seulement ça place le suffixe en début de ligne mais ça vire la majuscule à "Desktop".
0
zipe31 Messages postés 36402 Date d'inscription   Statut Contributeur Dernière intervention   6 431
 
Salut,

Il y a apparemment un petit couac avec ta commande sed zipe :
Normal, n'ayant pas toutes les données du problème, j'ai fait avec ce qu tu m'avais donné et forcément je ne pouvais pas deviner qu'il y aurait un "." en début ;-((

Ceci devrait faire l'affaire :

$ echo "./Desktop/test/image.JPG" | sed 's#\b\..*#_processed\L&#'
./Desktop/test/image_processed.jpg
0
Swiss Knight Messages postés 1956 Date d'inscription   Statut Membre Dernière intervention   110
 
Okay,
j'ai trouvé une solution.

1. je me suis demandé en quoi c'était si mal de faire un "for i in 'ls bla bla | grep -i .*.jpg'

parce que c'est une solution qui fonctionne et m'évite de doubler ma boucle de 200 lignes.

(si quelqu'un saurait m'expliquer en quoi c'est mal de faire un for avec un ls dedans pour boucler sur la liste des fichiers trouvés par ls... ce serait gentil !)


Sujet résolu à moitié donc ! ;)

2. fail... j'étais sur une bonne idée... qui était la suivante :

if [ ! -e "${dossier}" ]; then    
 echo "${dossier} does not exist, please input a valid folder or file name."    
 exit    
elif [ -f "${dossier}" ]; then    
 dossier='dirname "${dossier}"'    
fi    
        
for i in "${dossier}"/*.[Jj][Pp]*[Gg]; do j='echo "${i}" | sed -e 's/\.jp\(e\)*g/\_processed.jp\1g/gI''; etc...; done


à l'aide de la commande dirname, remplacer la variable dossier par le dossier parent si on ne travaille que sur une image, puis lancer le for comme avant, sans 'ls'.
Mais bien sûr, il va travailler sur tous les fichiers de ce dossier parent s'il en contient plusieurs, quand bien même un seul fichier aurait été spécifié.

Je suis entrain d'étudier une variante :

if [ ! -e "${dossier}" ]; then    
 echo "${dossier} does not exist, please input a valid folder or file name."    
 exit    
elif [ -f "${dossier}" ]; then    
 dossier='dirname "${dossier}"'     
 for_string="${fichier}"     
elif [ -d "${dossier}" ]; then    
    for_string='"${dossier}"/*.[Jj][Pp]*[Gg]'    
fi    
        
for i in "${for_string}"; do j='echo "${i}" | sed -e 's/\.jp\(e\)*g/\_processed.jp\1g/gI'';    
  


conserver tout ce qui suit le "in" du "for", dans une variable, et la lui donner à manger. ça marche pour un fichier unique, mais pas avec tout le charabia qu'il lui faut lorsqu'on traite un dossier : "${dossier}"/*.[Jj][Pp]*[Gg]
i.e. la commande n'est pas interprétée ; elle est lue telle quelle comme du texte tout simple, avec simples, doubles, ou backquotes :'(

EDIT : ça y est ; il suffit d'assigner la variable for_string comme ceci :
for_string="${dossier}"/*.[Jj][Pp]*[Gg]   
fi 

sans les simples quotes, et d'y faire appel dans le for :
for i in "${for_string}"; do j='echo "${i}" | sed -e 's/\.jp\(e\)*g/\_processed.jp\1g/gI''; 


Et ça marche ! ... Presque mais c'est pas loin, question de réglages...
EDIT 2 : Les réglages s'appellent "array" ;

elif [ -f "${dossier}" ]; then    
 dossier='dirname "${dossier}"'     
 for_string=( "${fichier}" ) 
elif [ -d "${dossier}" ]; then    
    for_string=( "${dossier}"/*.[Jj][Pp]*[Gg] ) 
fi    
        
for i in "${for_string[@]}"; do j='echo "${i}" | sed -e 's/\.jp\(e\)*g/\_processed.jp\1g/gI'';   


:D

En tout cas un grand merci pour tous vos tuyaux et conseils, qui me permettent d'apprendre petit à petit !

"Si vous ne pouvez expliquer un concept à un enfant de six ans, c'est que vous ne le comprenez pas complètement." -A. Einsten-
0
zipe31 Messages postés 36402 Date d'inscription   Statut Contributeur Dernière intervention   6 431
 
Re-

(si quelqu'un saurait m'expliquer en quoi c'est mal de faire un for avec un ls dedans pour boucler sur la liste des fichiers trouvés par ls... ce serait gentil !)

Comme des exemples sont toujours plus parlant que certaines explications...

$ ls -1
ma tata
mon tonton
titi
toto

$ for i in $(ls *);do echo "$i";done
ma
tata
mon
tonton
titi
toto

$ for i in *;do echo "$i";done
ma tata
mon tonton
titi
toto

0
Swiss Knight Messages postés 1956 Date d'inscription   Statut Membre Dernière intervention   110
 
Salut,
je te remercie pour ces exemples, j'y vois plus clair :-)

Par contre j'ai eu l'occasion de tester deux trois trucs, et avec le for tel que présenté ici, le caractère étoile servant de joker, n'est pas pris en compte si par exemple je donne un dossier ~/pictures/img_09* au script pour imaginer traiter tous les fichiers commençant par img_09

for i in "${for_string[@]}"; do ...


Est-ce qu'un for basé sur "find" ne serait-il pas plus élégant et plus efficace ?
Ou est-ce aussi quelque chose de "pas très bien" à faire ?
0
zipe31 Messages postés 36402 Date d'inscription   Statut Contributeur Dernière intervention   6 431
 
Re-

le caractère étoile servant de joker, n'est pas pris en compte si par exemple je donne un dossier ~/pictures/img_09*[...]
for i in "${for_string[@]}"; do ...


Je ne vois pas le rapport avec le tableau cité en exemple ? ;-\

$ for i in Images/gkrellShoot_2012-10* ; do echo "Image $i"; done
Image Images/gkrellShoot_2012-10-03_154043.png
Image Images/gkrellShoot_2012-10-03_154345.png
Image Images/gkrellShoot_2012-10-03_155447.png

$ for i in Images/gkrellShoot_2012-11* ; do echo "Image $i"; done
Image Images/gkrellShoot_2012-11-07_183406.png
Image Images/gkrellShoot_2012-11-20_144426.png
Image Images/gkrellShoot_2012-11-22_155627.png



Est-ce qu'un for basé sur "find" ne serait-il pas plus élégant et plus efficace ?
En général avec la commande "find" on emploie son option "-exec" ou bien on passe le résultat à "xargs" via un pipe. Après tout dépend du traitement exact à effectuer.
0
Swiss Knight Messages postés 1956 Date d'inscription   Statut Membre Dernière intervention   110
 
Salut !
Le tableau cité en exemple ?
Tu veux parler du :
for i in "${for_string[@]}"; do 

?

C'est simplement le début de la boucle principale du script.
Script qui prend en argument un dossier (ou un fichier) qui va être conservé dans la variable ${dossier}
et qui elle même sera traitée comme suit avant le lancement de la boucle :
fichier="${dossier}";    
if [ ! -e "${dossier}" ]; then    
 echo "${dossier} does not exist, please input a valid folder or file name."    
 exit    
elif [ -f "${dossier}" ]; then    
 dossier='dirname "${dossier}"' # si on travaille sur un fichier, on store le répertoire parent.    
 for_string=( "${fichier}" )     
elif [ -d "${dossier}" ]; then    
    for_string=( "${dossier}"/*.[Jj][Pp]*[Gg] )    
fi


et ensuite on lance la boucle principale comme ci-dessus.
or si la variable ${dossier} contient une étoile, ça ne va pas ; test simple en console :
$ dossier="test/img*"    
fichier="${dossier}"    
dossier='dirname "${dossier}"'    
$ for_string=( "${fichier}" )    
$ echo "${for_string[@]}"    
img_2012-24.jpg img_2013_04.jpg    
$ echo "${#for_string[@]}"    
1    

Il ne voit qu'une ligne avec tous les noms des fichiers...
J'aurai juré qu'il y aurait un nom par entrée du tableau, comme lorsqu'on lui passe un dossier en argument.
Je creuse la question...

EDIT 1 : en fait, dans les tests précédant le "for" principal, si je donne dossier=test/img*, la variable $dossier n'est ni vue comme un dossier, ni comme un fichier, du coup il ne va pas plus loin. Pire ; ça plante la console (i.e. elle se ferme d'un coup net). En fait ça plante parce qu'il y a un "exit" après le premier test. Donc c'est lui qui est vérifié : la variable ${dossier} n'existe pas. C'est à n'y rien comprendre.
0
zipe31 Messages postés 36402 Date d'inscription   Statut Contributeur Dernière intervention   6 431
 
$ dossier="test/img*"
fichier="${dossier}"
dossier='dirname "${dossier}"'
$ for_string=( "${fichier}" )

L'erreur est là. Pas de guillemet autour de la variable ;-(

for_string=( ${fichier} )


Exemple :

$ A="/home/jp/trash/gkrellShoot*"

$ B="${A}"

$ echo "${B}"
/home/jp/trash/gkrellShoot*

$ echo ${B}
/home/jp/trash/gkrellShoot_2012-10-03_154043.png /home/jp/trash/gkrellShoot_2012-10-03_154345.png /home/jp/trash/gkrellShoot_2012-10-03_155447.png
0