logo Debian Debian Debian-France Debian-Facile Debian-fr.org Forum-Debian.fr Debian ? Communautés logo inclusivité

Debian-facile

Bienvenue sur Debian-Facile, site d'aide pour les nouveaux utilisateurs de Debian.

Vous n'êtes pas identifié(e).

#1 25-04-2022 21:08:26

VBrice
Membre
Inscription : 04-10-2021

[Résolu] IFS=$'\n' qu'est ce que c'est ? Échappement noms de fichiers

Bonjour,

Dans un script, en essayant d'organiser une liste de fichiers dans un tableau, si le nom du fichier comporté un espace, il été considéré comme 2 fichiers distinct (la partie du nom avant l'espace dans une cellule du tableau et la seconde partie,avec l’extension dans une autre).

Après quelque heure de recherche je tombe sur un bout de code avec sortie de nul part :

IFS=$'\n'

j'essaye donc dans mon script sans y croire et la mon fichier dont le nom comporte un espace n'est plus diviser en 2 et ne prend qu'une seul cellule du tableau! le résultat es-contéscratchhead.gif

Du coup quelqu'un pourrait m'en dire plus ?
D’où ça sort ?
Il y en as d'autre des comme ça à connaitre?

En vous remerciant.

Dernière modification par VBrice (26-04-2022 18:38:36)

Hors ligne

#2 25-04-2022 22:27:09

Tawal
Membre
Distrib. : Debian Stable à jour
Noyau : amd64
(G)UI : Xfce
Inscription : 25-02-2021

Re : [Résolu] IFS=$'\n' qu'est ce que c'est ? Échappement noms de fichiers

Hello,

La variable IFS (Internal Field Separator) sert à définir le caractère servant de séparateur de champs.

La forme de variable $'....' est une forme ksh je crois à l'origine, mais elle est supportée par bash.
Elle permet d'interpréter les caractères échappés tel que \n qui équivaut à "nouvelle ligne".

Ensuite, si tu as un souci avec les noms de fichier comportant un espace, alors il est fort probable que tu n'utilises par correctement les protections des variables.
Un exemple pour bien "appréhender" les fichiers :

$ ls -l
total 0
-rw-r--r-- 1 tawal tawal 0 25 avril 23:22  fichsansespace
-rw-r--r-- 1 tawal tawal 0 25 avril 23:22 'fich espace'
$
$ tab=( * )
$ for elem in "${tab[@]}"; do echo "$elem"; done
fich espace
fichsansespace
$



Edit
Par contre :

$ for elem in ${tab[@]}; do echo "$elem"; done
fich
espace
fichsansespace
$


Cela vient de comment bash développe les mots.
Quand on utilise l'indice @ pour un tableau cela signifie tous les indices et est développé en 1 mot par élément, contrairement à l'indice * qui de même indique tous les indices mais est développé en 1 seul mot pour tous les éléments.
Mais pour que ça fonctionne bien, il faut protéger les appels des tableaux par des guillemets.
En gros, dans la 1ère boucle, la variable "${tab[@]}" est développée ainsi : "fich espace" "fichsansespace"
Tandis que dans la 2ième, la variable ${tab[@]} est développée ainsi : fich espace fichsansespace
D'où les 2 résultats différents.

Dernière modification par Tawal (25-04-2022 22:49:34)


Comme la science n'est pas infuse, elle se diffuse.
Useless Use of Cat Award
Filenames and Pathnames in Shell: How to do it Correctly
À chaque problème sa solution, à chaque solution son moyen, si pas de moyen, toujours le problème !

Hors ligne

#3 25-04-2022 22:30:14

raleur
Membre
Inscription : 03-10-2014

Re : [Résolu] IFS=$'\n' qu'est ce que c'est ? Échappement noms de fichiers

VBrice a écrit :

si le nom du fichier comporté un espace, il été considéré comme 2 fichiers distinct


Normal, par défaut le shell considère l'espace comme un séparateur.
Il faut "échapper" les espaces en les faisant précéder par  \  ou en entourant les noms de fichiers avec des guillemets simples ou doubles. Comment le script récupère-t-il la liste des fichiers ?

VBrice a écrit :

IFS=$'\n'


IFS = Input Field Separator = la liste des caractères considérés comme séparateurs. Par défaut (vide), espace, tabulation et saut de ligne.
$'\n' = caractère saut de ligne (LF). Attention : c'est un "bashisme", une extension propre à bash qui ne fait pas partie des spécifications POSIX. Le shell par défaut /bin/sh (dash) ne connaît pas cette syntaxe.
man bash
man sh

En redéfinissant IFS comme seulement le saut de ligne, l'espace n'est plus considéré comme un séparateur. Mais cela risque d'avoir d'autres effets indésirables. Il vaudrait mieux échapper les noms de fichiers.


Il vaut mieux montrer que raconter.

Hors ligne

#4 26-04-2022 16:10:10

VBrice
Membre
Inscription : 04-10-2021

Re : [Résolu] IFS=$'\n' qu'est ce que c'est ? Échappement noms de fichiers

RE-bonjour.

En ce qui concerne IFS, il vaut mieux ne pas l'utiliser alors.

Voici comment j'essaye de lister les fichiers:


StringListeFichiers=$(find *.png *.bmp *.jpg);
echo ${StringFichiers};

string2array=($StringListeFichiers);
#echo ${string2array[0]};

nbCellules=${#string2array[@]};
echo "Nombre de fichier à convertir: ${nbCellules}"
 



Après j'ai un


ArrayFull=($(find *.png *.bmp *.jpg));
echo "${ArrayFull[2]}";
 


mais j'ai toujours le nom du fichier coupé

Et avec un

ArrayFull=(*.png);
 


si j'ai pas de fichier png dans le dossier il m'ajoute une entré '*.png' scratchhead.gif



Je pensé avoir trouver avec :


ArrayFull=("$(find *.png *.bmp *.jpg)");
 


Malheureusement "${ArrayFull[0]}" comporte tous les noms de fichier


C'est à ce niveau la que ça coince: ArrayFull=($(find *.png *.bmp *.jpg)); j'ai donc procéder autrement : j'ai créer un tableau de tous les fichier avec ArrayFull=(*) puis dans ce tableau si un item comporte une extention de fichier image je l'ajoute dans un second tableau. Puis j'utilise ce second tableau pour faire la convertion

Dernière modification par VBrice (26-04-2022 17:59:14)

Hors ligne

#5 26-04-2022 18:20:46

VBrice
Membre
Inscription : 04-10-2021

Re : [Résolu] IFS=$'\n' qu'est ce que c'est ? Échappement noms de fichiers

Merci pour vos commentaires constructifs, j'ai finalement réussit à avoir un résultat qui me conviens, je partage mon codes si vous avez d'autre recommandation.


#!/bin/bash

#Conversion d'images par lot
#Prérequis: apt install imagemagick;

#chemin source et destination
pathSource="/home/debian/Images/";
pathDestination="/home/debian/Images/0/";

#dimension du redimensionnement en pourcentage: 25% ou longueur en pixel: 1000
ResizeValue="25%";

#Remplacer les espaces par des _ true ou false
remplacementEspaceUnderscore=false;



#création du chemin de destination si non présent
if [ ! -d ${pathDestination} ];then
echo "Création du dossier de destination: \"${pathDestination}\"";
mkdir ${pathDestination}
fi



#recuperation de la liste des fichiers et creation du tableau contenant les images
arrayListOfAllFiles=(*);

arrayListOfImage=();
for (( i=0; i<="${#arrayListOfAllFiles[@]}"-1; i++ ))
    do
    if [[ ${arrayListOfAllFiles[i]} == *".png" || ${arrayListOfAllFiles[i]} == *".bmp" || ${arrayListOfAllFiles[i]} == *".jpg" ]]; then
        arrayListOfImage+=("${arrayListOfAllFiles[i]}");
    fi
done

nbCellules="${#arrayListOfImage[@]}";
echo "Nombre de fichier à convertir: ${nbCellules}"



#suppression des espaces dans les noms de fichier
if [ $remplacementEspaceUnderscore == true ]
then
    echo "Renommage activé";
    for (( i=0; i<${nbCellules}; i++ ))
        do  
        if [[ ${arrayListOfImage[i]} == *" "* ]]; then
            #{arrayListOfAllFiles[i]//" "/"_"}   // pour toute les occurrences
            nomRenomer=${arrayListOfImage[i]//" "/"_"};
            echo "Le fichier "${arrayListOfImage[i]}" comporte un ou des espace(s), il va être renommé en: "${nomRenomer};
            mv "${arrayListOfImage[i]}" ${nomRenomer};
            arrayListOfImage[i]=${nomRenomer};
        fi
    done
else
    echo "Renommage désactivé";
fi



#conversion des fichiers Methode1
#for (( i=0; i<=${nbCellules}-1; i++ ))
#    do  
#    echo ${arrayListOfImage[i]};
#    convert ${pathSource}"${arrayListOfImage[i]}" -resize ${ResizeValue} ${pathDestination}"${arrayListOfImage[i]}";
#done



#conversion des fichiers Methode2
for element in "${arrayListOfImage[@]}"
    do
    echo "${element}";
    convert ${pathSource}"${element}" -resize ${ResizeValue} ${pathDestination}"${element}";
done

echo "Terminé!"
exit;
 



jusque la, le point négatif de ce code est le filtrage des extensions de fichier: en effet vu que Linux est sensible à la casse, .png sera diffèrent de . PNG ou .pNg ...
Je me pencherais sur les regex quand je serais plus à l’aise avec bash smile

Merci encore smile

Hors ligne

#6 26-04-2022 18:38:34

empanada
Membre
Distrib. : Debian 11 (Bullseye)
Noyau : 5.10.0-13-amd64
(G)UI : LXDE
Inscription : 19-09-2018

Re : [Résolu] IFS=$'\n' qu'est ce que c'est ? Échappement noms de fichiers

Plus court (les espaces ne sont pas un problème si on utilise "filename expansion" seulement, car elle est évalué APRÈS "world splitting":

#!/bin/bash

#Conversion d'images par lot
#Prérequis: apt install imagemagick;

#chemin source et destination
pathSource="/home/debian/Images/";
pathDestination="/home/debian/Images/0/";

#dimension du redimensionnement en pourcentage: 25% ou longueur en pixel: 1000
ResizeValue="25%";

#Activer listes de patrons (dans mon bullseye c'est activé par défaut, mais, quand même, on s'assure)
shopt -s extglob

#Désactiver case sensitive
shopt -s nocaseglob

cd /home/debian/Images/

#conversion des fichiers Methode2
for element in  @(*.png|*.bmp|*.jpg)
    do
    echo "${element}";
    convert ${pathSource}"${element}" -resize ${ResizeValue} ${pathDestination}"${element}";
done

echo "Terminé!"

 



Salut


"blues are the roots and the other musics are the fruits" . Willie Dixon

Hors ligne

#7 26-04-2022 21:21:08

Tawal
Membre
Distrib. : Debian Stable à jour
Noyau : amd64
(G)UI : Xfce
Inscription : 25-02-2021

Re : [Résolu] IFS=$'\n' qu'est ce que c'est ? Échappement noms de fichiers

C'est pas top de cd dans un script surtout si on ne revient pas au répertoire d'origine.
Il te manque un cd - à la fin.
Ou bien, tu peux aussi intégrer le chemin des fichiers directement dans la boucle :

#!/bin/bash

#chemin source et destination
pathSource="/home/debian/Images";
pathDestination="/home/debian/Images/0"

#dimension du redimensionnement en pourcentage: 25% ou longueur en pixel: 1000
ResizeValue="25%"

#conversion des fichiers Methode3
for element in  "$pathSource"/{*.png,*.bmp,*.jpg}
    do
    echo "$element"
    convert "$element" -resize "$ResizeValue" "$pathDestination/${element##*/}"
done

echo "Terminé!"

Dernière modification par Tawal (26-04-2022 21:22:19)


Comme la science n'est pas infuse, elle se diffuse.
Useless Use of Cat Award
Filenames and Pathnames in Shell: How to do it Correctly
À chaque problème sa solution, à chaque solution son moyen, si pas de moyen, toujours le problème !

Hors ligne

#8 26-04-2022 21:41:31

Tawal
Membre
Distrib. : Debian Stable à jour
Noyau : amd64
(G)UI : Xfce
Inscription : 25-02-2021

Re : [Résolu] IFS=$'\n' qu'est ce que c'est ? Échappement noms de fichiers

@VBrice : Pour mieux comprendre une de tes erreurs :

$ ls -l DossTest/
total 0
-rw-r--r-- 1 tawal tawal 0 26 avril 22:23  r1.png
-rw-r--r-- 1 tawal tawal 0 26 avril 22:23  r2.png
-rw-r--r-- 1 tawal tawal 0 26 avril 22:23  r3.png
-rw-r--r-- 1 tawal tawal 0 26 avril 22:23 'r r1.png'
-rw-r--r-- 1 tawal tawal 0 26 avril 22:23 'r r2.png'
-rw-r--r-- 1 tawal tawal 0 26 avril 22:23 'r r3.png'
-rw-r--r-- 1 tawal tawal 0 26 avril 22:23  t1.txt
-rw-r--r-- 1 tawal tawal 0 26 avril 22:23  t2.txt
-rw-r--r-- 1 tawal tawal 0 26 avril 22:23  t3.txt
-rw-r--r-- 1 tawal tawal 0 26 avril 22:23 't t1.txt'
-rw-r--r-- 1 tawal tawal 0 26 avril 22:23 't t2.txt'
-rw-r--r-- 1 tawal tawal 0 26 avril 22:23 't t3.txt'
$
$ t=( "$(find DossTest -type f)" )
$ for i in "${t[@]}"; do echo "$i"; done
DossTest/t t2.txt
DossTest/t3.txt
DossTest/t t3.txt
DossTest/r r2.png
DossTest/r2.png
DossTest/t t1.txt
DossTest/r3.png
DossTest/r r3.png
DossTest/r r1.png
DossTest/r1.png
DossTest/t1.txt
DossTest/t2.txt
$
$ t=( $(find DossTest -type f) )
$ for i in "${t[@]}"; do echo "$i"; done
DossTest/t
t2.txt
DossTest/t3.txt
DossTest/t
t3.txt
DossTest/r
r2.png
DossTest/r2.png
DossTest/t
t1.txt
DossTest/r3.png
DossTest/r
r3.png
DossTest/r
r1.png
DossTest/r1.png
DossTest/t1.txt
DossTest/t2.txt
$


Comme la science n'est pas infuse, elle se diffuse.
Useless Use of Cat Award
Filenames and Pathnames in Shell: How to do it Correctly
À chaque problème sa solution, à chaque solution son moyen, si pas de moyen, toujours le problème !

Hors ligne

#9 26-04-2022 22:30:19

VBrice
Membre
Inscription : 04-10-2021

Re : [Résolu] IFS=$'\n' qu'est ce que c'est ? Échappement noms de fichiers

Merci pour vos retour, effectivement il y as moyen d'amélioré.

@Tawal: j'ai juste une remarque pour:

t=( "$(find DossTest -type f)" )
for i in "${t[@]}"; do echo "$i"; done


J'ai l'impression que toute la liste des fichiers est contenu dans la 1ere cellule du tableau:
un echo "${t[0]}" renvoie toute la liste affiché, et un echo  "${t[1]}" ne renvoie rien.


Théoriquement, si je suis l'ordre de ta liste,
echo "${t[0]}" devrais renvoyer DossTest/t t2.txt
echo "${t[1]}" devrais renvoyer DossTest/t3.txt
echo "${t[2]}" devrais renvoyer DossTest/t t3.txt

Je dirais donc que actuellement "${t[@]}" est un tableau à une case ? scratchhead.gif
J'ai peut être loupé un truc?

Dernière modification par VBrice (26-04-2022 22:30:36)

Hors ligne

#10 26-04-2022 23:33:42

Tawal
Membre
Distrib. : Debian Stable à jour
Noyau : amd64
(G)UI : Xfce
Inscription : 25-02-2021

Re : [Résolu] IFS=$'\n' qu'est ce que c'est ? Échappement noms de fichiers

Oui, en effet, j'ai commis une erreur. Mea culpa.

En fait, la bonne méthode pour affecter un tableau depuis le retour d'une commande est de passer par mapfile (ou readarray qui est la même primitive).
Et donc, il convient de faire :

$ readarray -t Tab < <(find DossTest -type f)
$
$ for elem in "${Tab[@]}"; do echo "$elem"; done
DossTest/t t2.txt
DossTest/t3.txt
DossTest/t t3.txt
DossTest/r r2.png
DossTest/r2.png
DossTest/t t1.txt
DossTest/r3.png
DossTest/r r3.png
DossTest/r r1.png
DossTest/r1.png
DossTest/t1.txt
DossTest/t2.txt
$
$ echo "${Tab[0]}"
DossTest/t t2.txt
$ echo "${Tab[1]}"
DossTest/t3.txt
$


Désolé

Edit:
Tu devrais (comme moi) utiliser shellcheck qui indique les "erreurs" et les "conventions" usuelles d'un script.
Exemple :
Le script :

#!/bin/bash
a=( $(find DossTest -type f) )
echo ${a[@]}
exit


Le  test shellcheck :

$ shellcheck test.sh

In test.sh line 2:
a=( $(find DossTest -type f) )
    ^----------------------^ SC2207: Prefer mapfile or read -a to split command output (or quote to avoid splitting).


In test.sh line 3:
echo ${a[@]}
     ^-----^ SC2068: Double quote array expansions to avoid re-splitting elements.

For more information:
  https://www.shellcheck.net/wiki/SC2068 -- Double quote array expansions to ...
  https://www.shellcheck.net/wiki/SC2207 -- Prefer mapfile or read -a to spli...

Dernière modification par Tawal (26-04-2022 23:42:59)


Comme la science n'est pas infuse, elle se diffuse.
Useless Use of Cat Award
Filenames and Pathnames in Shell: How to do it Correctly
À chaque problème sa solution, à chaque solution son moyen, si pas de moyen, toujours le problème !

Hors ligne

#11 27-04-2022 15:53:08

VBrice
Membre
Inscription : 04-10-2021

Re : [Résolu] IFS=$'\n' qu'est ce que c'est ? Échappement noms de fichiers

Merci à tous les deux,

readarray est une commande qui à l'aire effectivement très efficace.

Moi qui découvre bash shellcheck vas pas mal m'aider lors des futur scripts smile

Hors ligne

#12 27-04-2022 21:45:03

Tawal
Membre
Distrib. : Debian Stable à jour
Noyau : amd64
(G)UI : Xfce
Inscription : 25-02-2021

Re : [Résolu] IFS=$'\n' qu'est ce que c'est ? Échappement noms de fichiers

Quand tu seras un peu plus aiguisé, n'hésite pas à lire les 2 liens de ma signature.
C'est un peu compliqué et en anglais, mais c'est aussi riche d'enseignements smile

Comme la science n'est pas infuse, elle se diffuse.
Useless Use of Cat Award
Filenames and Pathnames in Shell: How to do it Correctly
À chaque problème sa solution, à chaque solution son moyen, si pas de moyen, toujours le problème !

Hors ligne

#13 28-04-2022 16:03:56

VBrice
Membre
Inscription : 04-10-2021

Re : [Résolu] IFS=$'\n' qu'est ce que c'est ? Échappement noms de fichiers

J'y tacherais smile

Hors ligne

Pied de page des forums