#1 Le 27/07/2010, à 03:41
- grobs
[Bash] cp et les espaces dans les noms de fichiers
Bonjour à toutes et à tous
Cela fait plusieurs heures maintenant (et l'heure tardive de mon poste en est la preuve...) que je me bats avec cp car cet ******* interprète les fichiers avec des espaces comme plusieurs fichiers... "Il suffit de mettre des guillemets !" me direz-vous, et bien voilà mon problème : même comme ça, cela ne fonctionne pas.
Voici mon script (simplifié pour l'occasion) :
for fichier in *
do
cp -PpuR "$fichier" $RepFinal
done
Or, dès que ma boucle for rencontre un fichier avec un espace (par exemple "un fichier.txt"), il me fait une erreur du genre :
cp: impossible d'évaluer «/home/grobs/un»: Aucun fichier ou dossier de ce type
cp: impossible d'évaluer «fichier.txt»: Aucun fichier ou dossier de ce type
Et je trouve ça assez rageant puisque j'ai bien mis des guillemets autour de la variable... J'ai essayé tout un tas de choses horribles ("$fichier", ""$fichier"", \"$fichier\", "\"$fichier\"", '"$fichier"'...) mais rien n'y fait...
Comme je deviens très vite insomniaque lorsqu'un programme me résiste, je serai ravi que vous m'apportiez une explication rationnelle quant à cet énergumène de cp qui ne veut rien entendre oO
Merci d'avance
"Mieux vaut vérifier que les enceintes sont bien branchées avant de recompiler un noyau". (vieux proverbe de Debianneux)
Hors ligne
#2 Le 27/07/2010, à 04:40
- chopinhauer
Re : [Bash] cp et les espaces dans les noms de fichiers
Comme je deviens très vite insomniaque lorsqu'un programme me résiste, je serai ravi que vous m'apportiez une explication rationnelle quant à cet énergumène de cp qui ne veut rien entendre oO
cp n'est surement pas responsable. Regarde du côté shell, t'utilises quoi comme shell?
Pas réussi à reproduire ton problème, même des noms genre 'ha$ $ha" "$ha.txt' passe sans problème.
Pensez à donner un bon titre à vos sujets : cela permettra d'aider d'autres utilisateurs dans votre même situation. Ce n'est pas qu'en donnant des solutions qu'on aide, mais aussi en posant des bonnes questions et… facilement trouvables.
Hors ligne
#3 Le 27/07/2010, à 04:43
- chopinhauer
Re : [Bash] cp et les espaces dans les noms de fichiers
grobs a écrit :Comme je deviens très vite insomniaque lorsqu'un programme me résiste, je serai ravi que vous m'apportiez une explication rationnelle quant à cet énergumène de cp qui ne veut rien entendre oO
cp n'est surement pas responsable. Regarde du côté shell, t'utilises quoi comme shell?
Pas réussi à reproduire ton problème, même des noms genre 'ha$ $ha" "$ha.txt' passent sans problème.
Edit: poste ton script en entier '#!/bin/sh' compris (évidemment s'il y a quelque chose de confidentiel masque-le, me remplace les lettres sans accent par des lettres sans accent et lettre accentuées par des lettres accentuées).
Dernière modification par chopinhauer (Le 27/07/2010, à 04:44)
Pensez à donner un bon titre à vos sujets : cela permettra d'aider d'autres utilisateurs dans votre même situation. Ce n'est pas qu'en donnant des solutions qu'on aide, mais aussi en posant des bonnes questions et… facilement trouvables.
Hors ligne
#4 Le 27/07/2010, à 11:51
- grobs
Re : [Bash] cp et les espaces dans les noms de fichiers
Visiblement il y a des gens pires que moi niveau sommeil ^^
Mon script fait pas loin de 250 lignes alors je vous poste le bout de code qui nous intéresse :
#!/bin/bash
# [...] un gros bout de code
clearLine()
{
NbrIter=$1
for i in $(seq 0 $(($NbrIter-1)))
do
echo -n " "
done
echo -n -e $2
}
nbrFilesInDirRecursive()
{
Directory=$1
nbrFiles=0
for fichier in $Directory/*
do
nbrFiles=$(($nbrFiles+1))
if [[ -d $fichier ]]
then
nbrFiles=$(nbrFilesInDirRecursive $fichier)
fi
done
echo $nbrFiles
}
NbrTotalFichiers=$(nbrFilesInDirRecursive $RepAcopier)
PoidsTotal=$(du -bs $RepAcopier 2> /dev/null | cut -f1)
PoidsMoyenFichier=$(($PoidsTotal/$NbrTotalFichiers))
#TODO enlever ces tests de debug :
echo "NbrTotalFichiers = $NbrTotalFichiers"
echo "PoidsTotal = $PoidsTotal"
echo -e "PoidsMoyenFichier = $PoidsMoyenFichier\n"
CopierRepertoire()
{
PoidsDejaTraite=$2
# Copie des fichiers et dossiers non cachés
for fichier in $1/*
do
if [[ -e $fichier ]] && [[ $fichier != '.' ]] && [[ $fichier != '..' ]]
then
clearLine $(tput cols) "\r"
nbrChars=$(echo -e -n "Copie du dossier \"$fichier\"($PoidsDejaTraite/$PoidsTotal => $PourcentageAvancement%)" | wc -c)
if [[ -d $fichier ]]
then
echo -e -n "\t\t\tCopie du dossier \"$fichier\""
CopierRepertoire $fichier $PoidsDejaTraite
else
echo -e -n "\t\t\tCopie du fichier \"$fichier\""
fi
clearLine $(($(tput cols)-$(($nbrChars+24))))
echo -e -n "($PoidsDejaTraite/$PoidsTotal => $PourcentageAvancement%)\r"
cp -PpuR "$fichier" $RepFinal
PoidsFichierEnCours=$(du -b $fichier | cut -f1)
PoidsDejaTraite=$(($PoidsDejaTraite+$PoidsFichierEnCours))
# On met à jour le pourcentage d'avancement
PourcentageAvancement=$(($PoidsDejaTraite*100/$PoidsTotal))
fi
done
}
CopierRepertoire $RepAcopier 0
Si vous voyez des horreurs, profitez-en pour me le dire
remplace les lettres sans accent par des lettres sans accent et lettre accentuées par des lettres accentuées
Tu es sûr de toi là ? ^^
Dernière modification par grobs (Le 27/07/2010, à 11:55)
"Mieux vaut vérifier que les enceintes sont bien branchées avant de recompiler un noyau". (vieux proverbe de Debianneux)
Hors ligne
#5 Le 27/07/2010, à 12:09
- Vysserk3
Re : [Bash] cp et les espaces dans les noms de fichiers
Salut,
Essaye cette syntaxe:
for fichier in $dossier_source/*
do cp -PpuR "`echo $fichier`" /dossier/destination/
done
Les `` autour de l'echo va d'abord interprêter le résultat de la commande echo, ce qui donnera une chaîne, que l'on encadre par des guillemets pour le cp
Dernière modification par Vysserk3 (Le 27/07/2010, à 12:13)
Hors ligne
#6 Le 27/07/2010, à 12:32
- grobs
Re : [Bash] cp et les espaces dans les noms de fichiers
Toujours la même erreur :
du: impossible d'accéder à «/home/grobs/Backup_config/Bookmarks»: Aucun fichier ou dossier de ce type (129043/203995284958 => 0%)
du: impossible d'accéder à «2009-10-28.json»: Aucun fichier ou dossier de ce type
/home/grobs/Mes_programmes/Shell/backupssh: line 136: 129043+: erreur de syntaxe : opérande attendue (error token is "+")
Le fichier s'appelle "Bookmarks 2009-10-28.json" et la ligne 136 c'est celle-ci :
PoidsDejaTraite=$(($PoidsDejaTraite+$PoidsFichierEnCours))
Apparemment, $PoidsFichierEnCours est vide mais il ne l'est pas tout le temps, j'ai l'impression qu'il ne l'est que là, soit à cause de l'erreur dûe à l'espace, soit à cause d'une erreur d'algo dans la récursivité car ce fichier est dans un sous répertoire (donc traîté dans un fils de la fonction CopierRepertoire().
Dernière modification par grobs (Le 27/07/2010, à 12:32)
"Mieux vaut vérifier que les enceintes sont bien branchées avant de recompiler un noyau". (vieux proverbe de Debianneux)
Hors ligne
#7 Le 27/07/2010, à 12:41
- Vysserk3
Re : [Bash] cp et les espaces dans les noms de fichiers
Tu as bien remplacé partout où apparaissait la syntaxe incorrecte par ce que j'ai proposé, à savoir pas seulement pour le cp mais aussi pour le du.
Mais là apparemment, c'est un autre problème, peut être d'algo
Hors ligne
#8 Le 27/07/2010, à 13:08
- grobs
Re : [Bash] cp et les espaces dans les noms de fichiers
Bon, j'ai résolu le problème grâce à toi, j'avais juste oublié de mettre les guillements sur le $fichier de la commande "du"...
Par contre, j'ai un autre souci : lorsque le dossier que je veux copier est vide, j'ai cette erreur :
cp: impossible d'évaluer «/home/grobs/Backup_config/etc/cron.hourly/*»: Aucun fichier ou dossier de ce type
Je ne comprends pas pourquoi il essaie de faire ça puisque je vérifie au début si le fichier existe bien et s'il n'est pas "."
Une idée ?
Merci pour tout en tout cas
Dernière modification par grobs (Le 27/07/2010, à 13:31)
"Mieux vaut vérifier que les enceintes sont bien branchées avant de recompiler un noyau". (vieux proverbe de Debianneux)
Hors ligne
#9 Le 27/07/2010, à 13:12
- Vysserk3
Re : [Bash] cp et les espaces dans les noms de fichiers
Tu compares le $fichier qui est un chemin absolu de fichier avec '.' donc ca ne peut pas aller. Essaye de préfixer '.' par le nom du dossier courant, ici c'est $1/ je crois
Hors ligne
#10 Le 27/07/2010, à 13:31
- grobs
Re : [Bash] cp et les espaces dans les noms de fichiers
Même problème avec ce test :
if [[ -e "$fichier" ]] && [[ "$fichier" != "$1/." ]] && [[ "$fichier" != "$1/.." ]]
Dernière modification par grobs (Le 27/07/2010, à 13:32)
"Mieux vaut vérifier que les enceintes sont bien branchées avant de recompiler un noyau". (vieux proverbe de Debianneux)
Hors ligne
#11 Le 27/07/2010, à 13:42
- Vysserk3
Re : [Bash] cp et les espaces dans les noms de fichiers
Apparemment, quand on fait :
for file in *; do echo $file; done
et que le dossier est vide, il affiche * (en bash en tout cas)
Donc essaye de tester juste si c'est différent de * :
avec
if [ $file != * ]
Hors ligne
#12 Le 27/07/2010, à 13:56
- grobs
Re : [Bash] cp et les espaces dans les noms de fichiers
Apparemment oui... Mais le truc bizarre c'est que j'ai testé avec ça et ça fait pareil oO :
if [[ -e "$fichier" ]] && [[ "$fichier" != "$1/." ]] && [[ "$fichier" != "$1/.." ]] && [[ "$fichier" != "$1/*" ]]
Pourtant, j'ai affiché les deux chaînes et ce sont exactement les mêmes !
TEST : $fichier = /home/grobs/Backup_config/etc/cron.hourly/*
TEST : $1/* = /home/grobs/Backup_config/etc/cron.hourly/*
Je pensais faire un truc du genre sinon mais ça ne fonctionne pas :
if ( [[ -d "$fichier" ]] && [[ -n $(ls "$fichier" | head -n1) ]] ) || ( [[ -e "$fichier" ]] && [[ "$fichier" != "$1/." ]] && [[ "$fichier" != "$1/.." ]] )
la commande cp exécutée est la suivante lors de l'erreur :
cp -PpuR "/home/grobs/Backup_config/etc/cron.hourly/*" "/media/WesternDigital1/home/grobs/Backup-mardi-27-juillet-2010--14h03
Dernière modification par grobs (Le 27/07/2010, à 14:04)
"Mieux vaut vérifier que les enceintes sont bien branchées avant de recompiler un noyau". (vieux proverbe de Debianneux)
Hors ligne
#13 Le 27/07/2010, à 14:02
- iuchiban
Re : [Bash] cp et les espaces dans les noms de fichiers
Cherche plus, je viens de trouver :
Les boucles for découpent suivant les espaces.
J'ai eu le même problème pour lire un fichier texte ligne par ligne.
Dans ce dernier cas, je passe maintenant par une boucle de type
while read ligne
do
[Commandes]
done < fichier
Donc je pense que dans tons cas, il faudrait faire un truc du style :
CopierRepertoire()
{
PoidsDejaTraite=$2
# Copie des fichiers et dossiers non cachés
find $1 -type f > Liste_fichier
while read fichier
do
clearLine $(tput cols) "\r"
nbrChars=$(echo -e -n "Copie du dossier \"$fichier\"($PoidsDejaTraite/$PoidsTotal => $PourcentageAvancement%)" | wc -c)
if [[ -d $fichier ]]
then
echo -e -n "\t\t\tCopie du dossier \"$fichier\""
CopierRepertoire $fichier $PoidsDejaTraite
else
echo -e -n "\t\t\tCopie du fichier \"$fichier\""
fi
clearLine $(($(tput cols)-$(($nbrChars+24))))
echo -e -n "($PoidsDejaTraite/$PoidsTotal => $PourcentageAvancement%)\r"
cp -PpuR "$fichier" $RepFinal
PoidsFichierEnCours=$(du -b $fichier | cut -f1)
PoidsDejaTraite=$(($PoidsDejaTraite+$PoidsFichierEnCours))
# On met à jour le pourcentage d'avancement
PourcentageAvancement=$(($PoidsDejaTraite*100/$PoidsTotal))
done < Liste_fichier
}
C'est depuis que Chuck Norris a laissé la vie sauve à un manchot que l'on dit que Linux est libre.
Chuck Norris n'a pas besoin d'éditer son premier message pour ajouter [Résolu]. Chuck Norris est toujours [Résolu], quoi qu'il arrive.
Hors ligne
#14 Le 27/07/2010, à 14:07
- Vysserk3
Re : [Bash] cp et les espaces dans les noms de fichiers
Quelles deux chaînes ? Le fait de rajouter une condition toujours vraie à une série de and, ne change rien.
Là normalement, "$fichier" vaut * et tu le compares à /dossier/* , le test est toujours vraie...
edit : en fait j'ai écrit trop lentement...bon alors $fichier, c'est bizarre qu'il contienne le nom du dossier parent, non ?
Dernière modification par Vysserk3 (Le 27/07/2010, à 14:09)
Hors ligne
#15 Le 27/07/2010, à 14:28
- grobs
Re : [Bash] cp et les espaces dans les noms de fichiers
Je pourrai donner un autre nom à ma variable (genre $MonSuperFichierEtSonChemin) mais je doute que cela change quelque chose.
Ma condition n'est pas toujours vraie !!
Dernière modification par grobs (Le 27/07/2010, à 14:30)
"Mieux vaut vérifier que les enceintes sont bien branchées avant de recompiler un noyau". (vieux proverbe de Debianneux)
Hors ligne
#16 Le 27/07/2010, à 14:42
- Vysserk3
Re : [Bash] cp et les espaces dans les noms de fichiers
Oui désolé, c'est moi qui testais mal localement, je faisais pas for file in $dir/*, mais for file in *...
Hors ligne
#17 Le 27/07/2010, à 14:52
- Vysserk3
Re : [Bash] cp et les espaces dans les noms de fichiers
Bon je teste chez moi et ca marche :
for file in $rep/*
do if [ $file != "$rep/*" ]
then echo "vrai"
else echo "faux"
fi
done
C'est à dire qu'il m'affiche faux car il y a bien égalité...
Le $rep, c'est un dossier vide.
Hors ligne
#18 Le 27/07/2010, à 14:57
- iuchiban
Re : [Bash] cp et les espaces dans les noms de fichiers
et pourquoi pas utiliser ma méthoe :
d'abord un find -type f pour avoir la liste des fichiers (donc pas de *, pas de répertoires tous seuls) puis une boucle while read ligne pour gérer les fichiers contenant des espaces ??
C'est depuis que Chuck Norris a laissé la vie sauve à un manchot que l'on dit que Linux est libre.
Chuck Norris n'a pas besoin d'éditer son premier message pour ajouter [Résolu]. Chuck Norris est toujours [Résolu], quoi qu'il arrive.
Hors ligne
#19 Le 27/07/2010, à 14:58
- Vysserk3
Re : [Bash] cp et les espaces dans les noms de fichiers
Sinon pour tester si un répertoire est vide ou pas, il y a plus simple quand même (et je te conseille de t'en faire une fonction séparée, ca peut servir ailleurs) :
Cf http://www.cyberciti.biz/faq/linux-unix-shell-check-if-directory-empty/
Hors ligne
#20 Le 27/07/2010, à 15:42
- chopinhauer
Re : [Bash] cp et les espaces dans les noms de fichiers
Visiblement il y a des gens pires que moi niveau sommeil ^^
J'aurais pu être au Canada, hein! Au fait ce n'est pas le cas , mais en revanche je me lève tard.
CopierRepertoire()
{
PoidsDejaTraite=$2# Copie des fichiers et dossiers non cachés
for fichier in $1/*
do
if [[ -e $fichier ]] && [[ $fichier != '.' ]] && [[ $fichier != '..' ]]
then
clearLine $(tput cols) "\r"
nbrChars=$(echo -e -n "Copie du dossier \"$fichier\"($PoidsDejaTraite/$PoidsTotal => $PourcentageAvancement%)" | wc -c)if [[ -d $fichier ]]
then
echo -e -n "\t\t\tCopie du dossier \"$fichier\""
CopierRepertoire $fichier $PoidsDejaTraite
else
echo -e -n "\t\t\tCopie du fichier \"$fichier\""
ficlearLine $(($(tput cols)-$(($nbrChars+24))))
echo -e -n "($PoidsDejaTraite/$PoidsTotal => $PourcentageAvancement%)\r"
cp -PpuR "$fichier" $RepFinalPoidsFichierEnCours=$(du -b $fichier | cut -f1)
PoidsDejaTraite=$(($PoidsDejaTraite+$PoidsFichierEnCours))
# On met à jour le pourcentage d'avancement
PourcentageAvancement=$(($PoidsDejaTraite*100/$PoidsTotal))
fi
done
}
Il faut que tu protège tes répertoires aussi. Genre for fichier in "$1"/*, etc. L'expansion des paramètre se fait avant l'expansion des globs.
Bash n'inclut pas les fichiers qui commencent par '.' dans l'expansion de *. Il faut activer l'option dotglob et de toute manière . et .. ne seront pas dans la liste.
Des guillemets autours de $fichier sont suffisant pour cp au moins celui de bash.
chopinhauer a écrit :remplace les lettres sans accent par des lettres sans accent et lettre accentuées par des lettres accentuées
Tu es sûr de toi là ? ^^
Si il te fallait encore des mots de passe, mieux changer le moins possible. Genre si fichier est une variable $fichiermdp n'est pas pareil que $fichier/mdp.
Apparemment, quand on fait :
for file in *; do echo $file; done
et que le dossier est vide, il affiche * (en bash en tout cas)
C'est une option de bash gérée par les options nullglob, failglob, dotglob, pas une constante.
Cherche plus, je viens de trouver :
Les boucles for découpent suivant les espaces.
Vrai: bash découpe les mots selon les espaces (en tout cas selon la variable IFS).
Faux: l'expansion des pathname est la dernière, juste après le découpage en mots.
Pensez à donner un bon titre à vos sujets : cela permettra d'aider d'autres utilisateurs dans votre même situation. Ce n'est pas qu'en donnant des solutions qu'on aide, mais aussi en posant des bonnes questions et… facilement trouvables.
Hors ligne
#21 Le 27/07/2010, à 15:42
- chaoswizard
Re : [Bash] cp et les espaces dans les noms de fichiers
Sinon, il y a toujours la vieille technique de changer le séparateur de champs en début de script.
IFS=$'\n'
Ubuntu ==> Debian ==> Archlinux
Hors ligne
#22 Le 27/07/2010, à 16:30
- grobs
Re : [Bash] cp et les espaces dans les noms de fichiers
Bon je teste chez moi et ca marche :
for file in $rep/* do if [ $file != "$rep/*" ] then echo "vrai" else echo "faux" fi done
C'est à dire qu'il m'affiche faux car il y a bien égalité...
Le $rep, c'est un dossier vide.
J'ai compris mon erreur : je faisais [[ $file != "$rep/*" ]] au lieu de [ $file != "$rep/*" ]
D'ailleurs, qu'est-ce que ça change exactement ?
Mon script étant un gros script de maintenance pour Ubuntu 10.04, je compte le rendre entièrement générique. Et dès qu'il le sera, je le poste dans un nouveau topic pour en faire profiter tout le monde
Merci à vous 4 !
Ubuntu ne serait pas ce qu'il est sans sa fabuleuse communauté !
Dernière modification par grobs (Le 27/07/2010, à 16:31)
"Mieux vaut vérifier que les enceintes sont bien branchées avant de recompiler un noyau". (vieux proverbe de Debianneux)
Hors ligne
#23 Le 27/07/2010, à 17:58
- chopinhauer
Re : [Bash] cp et les espaces dans les noms de fichiers
J'ai compris mon erreur : je faisais [[ $file != "$rep/*" ]] au lieu de [ $file != "$rep/*" ]
D'ailleurs, qu'est-ce que ça change exactement ?
De la man de bash à propos de [[:
When the == and != operators are used, the string to the right
of the operator is considered a pattern and matched according to
the rules described below under Pattern Matching.
Mon script étant un gros script de maintenance pour Ubuntu 10.04, je compte le rendre entièrement générique. Et dès qu'il le sera, je le poste dans un nouveau topic pour en faire profiter tout le monde.
Dans ce cas fais attention aux shopts ou essaie d'enlever les bashismes.
Sinon, il y a toujours la vieille technique de changer le séparateur de champs en début de script.
Quand les utilisateurs utilisent des noms de fichier sales on utilise des techniques encore plus sales.:D
Pensez à donner un bon titre à vos sujets : cela permettra d'aider d'autres utilisateurs dans votre même situation. Ce n'est pas qu'en donnant des solutions qu'on aide, mais aussi en posant des bonnes questions et… facilement trouvables.
Hors ligne
#24 Le 27/07/2010, à 18:26
- Totor
Re : [Bash] cp et les espaces dans les noms de fichiers
Bonjour,
pour info :
# effacer toute la ligne active sans changer la position du curseur
echo -e "\033[2K"
# effacer toute la ligne active et revenir au début
echo -e "\033[2K\r"
par contre, plutôt que de faire du récursif :
#!/bin/bash
CopierRepertoire()
{
dossiersource="$1"
dossierdestination="$2"
poidstotal=0
cd "${dossiersource}"
while read
do
infos=( $(du -k --apparent-size "${REPLY}") )
((poidstotal+=infos[0]))
fichiers+=( "${infos[0]};${REPLY}" )
done < <(find -type f -printf "%P\n")
for info in "${fichiers[@]}"
do
poids="${info%%;*}"
fichier="${info#*;}"
# tes calculs et affichages
cp -Ppu "${fichier}" "${dossierdestination}"
done
}
il est possible qu'il y ait des bugs, je n'ai pas testé (pas le courage) et j'ai fait ça à la mano
-- Lucid Lynx --
Hors ligne
#25 Le 27/07/2010, à 20:47
- grobs
Re : [Bash] cp et les espaces dans les noms de fichiers
@chopinhauer : Qu'appelles-tu des "shopts" ? Je ne maîtrise pas assez le langage shell pour savoir quels sont les techniques "à la bash" et celles qui ne le sont pas, je posterai tout ça dans un nouveau topic pour en discuter avec vous
@Totor : En effet, j'y avais pensé mais est-ce que les séquences d'échappement sont vraiment génériques ? Pour ce qui est de ton bout de code, je vais essayer, merci d'avoir pris le temps de le faire
Dernière modification par grobs (Le 27/07/2010, à 21:21)
"Mieux vaut vérifier que les enceintes sont bien branchées avant de recompiler un noyau". (vieux proverbe de Debianneux)
Hors ligne