[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 -
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.
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 :
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-
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:
- [bash]Éviter de devoir doubler 1boucle for si fichier ou dossier
- Fichier bin - Guide
- Fichier epub - Guide
- Downloader for pc - Télécharger - Téléchargement & Transfert
- Fichier rar - Guide
- Dossier appdata - Guide
4 réponses
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...
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...
Salut zipe,
merci pour la partie 2) ça marche.
j'ai juste dû changer un peu le sed qui suivait :
pour la partie 1) j'ai ça dans les getopt :
Et ensuite, je fais un test avant la boucle 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-
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-
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)
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)
En fait, j'ai besoin de passer les éventuelles majuscules des extensions en minuscules, d'où le sed que j'utilisais.
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).
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).
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)
Ç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)
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 :
Ta commande :
Non seulement ça place le suffixe en début de ligne mais ça vire la majuscule à "Desktop".
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".
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 :
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
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 :
à 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 :
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 :
sans les simples quotes, et d'y faire appel dans le for :
Et ça marche ! ... Presque mais c'est pas loin, question de réglages...
EDIT 2 : Les réglages s'appellent "array" ;
: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-
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-
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...
(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
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
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 ?
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 ?
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 ? ;-\
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.
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.
Salut !
Le tableau cité en exemple ?
Tu veux parler du :
?
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 :
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 :
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.
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.
$ dossier="test/img*"
fichier="${dossier}"
dossier='dirname "${dossier}"'
$ for_string=( "${fichier}" )
L'erreur est là. Pas de guillemet autour de la variable ;-(
Exemple :
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