J'ai un script shell bash qui parcourt tous les répertoires enfants (mais pas les fichiers) d'un certain répertoire. Le problème est que certains des noms de répertoire contiennent des espaces.
Voici le contenu de mon répertoire de test:
$ls -F test
Baltimore/ Cherry Hill/ Edison/ New York City/ Philadelphia/ cities.txt
Et le code qui parcourt les répertoires:
for f in `find test/* -type d`; do
echo $f
done
Voici le résultat:
test / Baltimore test / Cerise Colline test / Edison test / Nouveau York Ville test / Philadelphie
Cherry Hill et New York sont traitées comme 2 ou 3 entrées distinctes.
J'ai essayé de citer les noms de fichiers, comme ceci:
for f in `find test/* -type d | sed -e 's/^/\"/' | sed -e 's/$/\"/'`; do
echo $f
done
mais en vain.
Il doit y avoir un moyen simple de le faire.
Les réponses ci-dessous sont excellentes. Mais pour rendre cela plus compliqué, je ne veux pas toujours utiliser les répertoires répertoriés dans mon répertoire de test. Parfois, je veux passer les noms de répertoire en tant que paramètres de ligne de commande à la place.
J'ai pris la suggestion de Charles de configurer l'IFS et j'ai trouvé ce qui suit:
dirlist="${@}"
(
[[ -z "$dirlist" ]] && dirlist=`find test -mindepth 1 -type d` && IFS=$'\n'
for d in $dirlist; do
echo $d
done
)
et cela fonctionne très bien sauf s'il y a des espaces dans les arguments de la ligne de commande (même si ces arguments sont entre guillemets). Par exemple, appeler le script comme ceci: test.sh "Cherry Hill" "New York City"
produit la sortie suivante:
Cerise Colline Nouveau York Ville
la source
list="$@"
rejette complètement la liste de la valeur d'origine, la réduisant en une chaîne. Veuillez suivre les pratiques de ma réponse exactement telles qu'elles ont été données - une telle affectation n'est encouragée nulle part; si vous souhaitez transmettre une liste d'arguments de ligne de commande à un programme, vous devez les rassembler dans un tableau et développer ce tableau directement.Réponses:
Premièrement, ne le faites pas de cette façon. La meilleure approche consiste à utiliser
find -exec
correctement:L'autre approche sûre consiste à utiliser une liste terminée par NUL, bien que cela nécessite que votre recherche prenne en charge
-print0
:Vous pouvez également remplir un tableau à partir de find et transmettre ce tableau plus tard:
Si votre recherche ne prend pas en charge
-print0
, votre résultat est alors dangereux - ce qui suit ne se comportera pas comme vous le souhaitez si des fichiers contiennent des retours à la ligne dans leurs noms (ce qui, oui, est légal):Si l'on n'utilise pas l'une des méthodes ci-dessus, une troisième approche (moins efficace en termes d'utilisation du temps et de la mémoire, car elle lit toute la sortie du sous-processus avant de procéder au fractionnement de mots) consiste à utiliser une
IFS
variable qui ne ne contient pas le caractère espace. Désactivez le globbing (set -f
) pour empêcher les chaînes contenant des caractères glob tels que[]
,*
ou?
d'être développées:Enfin, pour le cas des paramètres de ligne de commande, vous devriez utiliser des tableaux si votre shell les prend en charge (c'est-à-dire que c'est ksh, bash ou zsh):
maintiendra la séparation. Notez que la citation (et l'utilisation de
$@
plutôt que$*
) est importante. Les tableaux peuvent également être remplis d'autres manières, telles que les expressions glob:la source
Cependant, ne fonctionne pas si le nom de fichier contient des retours à la ligne. Ce qui précède est la seule solution que je connaisse lorsque vous souhaitez réellement avoir le nom du répertoire dans une variable. Si vous souhaitez simplement exécuter une commande, utilisez xargs.
la source
find . -type d | while read file; do ls "$file"; done
Voici une solution simple qui gère les tabulations et / ou les espaces dans le nom de fichier. Si vous devez gérer d'autres caractères étranges dans le nom de fichier, comme des sauts de ligne, choisissez une autre réponse.
Le répertoire de test
Le code pour aller dans les répertoires
Le nom de fichier doit être entre guillemets (
"$f"
) s'il est utilisé comme argument. Sans guillemets, les espaces servent de séparateur d'arguments et plusieurs arguments sont donnés à la commande appelée.Et la sortie:
la source
alias duc='ls -d * | while read D; do du -sh "$D"; done;'
alias duc='du -sh *(/)'
-print0
etIFS='' read -r -d '' f
.C'est extrêmement délicat dans Unix standard, et la plupart des solutions se heurtent à des sauts de ligne ou à un autre caractère. Cependant, si vous utilisez l'ensemble d'outils GNU, vous pouvez exploiter l'
find
option-print0
et l'utiliserxargs
avec l'option correspondante-0
(moins-zéro). Il y a deux caractères qui ne peuvent pas apparaître dans un nom de fichier simple; ce sont des barres obliques et NUL '\ 0'. De toute évidence, une barre oblique apparaît dans les noms de chemin, donc la solution GNU d'utiliser un NUL '\ 0' pour marquer la fin du nom est ingénieuse et infaillible.la source
Pourquoi ne pas simplement mettre
devant la commande for? Cela change le séparateur de champ de <Space> <Tab> <Newline> à juste <Newline>
la source
la source
-d $'\0'
est exactement le même que-d ''
- parce que bash utilise des chaînes terminées par NUL, le premier caractère d'une chaîne vide est un NUL, et pour la même raison, les NUL ne peuvent pas du tout être représentés à l'intérieur des chaînes C.j'utilise
Cela ne suffirait-il pas?
Idée tirée de http://www.cyberciti.biz/tips/handling-filenames-with-spaces-in-bash.html
la source
$(echo ...)
), ne gère pas correctement les noms de fichiers avec des expressions glob, ne gère pas correctement les noms de fichiers qui contiennent$'\b'
ou $ '\ n' caractères, et de plus convertit plusieurs exécutions d'espaces en caractères d'espaces uniques sur le côté sortie en raison d'un devis incorrect.Ne stockez pas les listes sous forme de chaînes; les stocker sous forme de tableaux pour éviter toute cette confusion de délimiteur. Voici un exemple de script qui fonctionnera soit sur tous les sous-répertoires de test, soit sur la liste fournie sur sa ligne de commande:
Essayons maintenant ceci sur un répertoire de test avec une courbe ou deux lancées:
la source
"$@"
tableau, en y ajoutantset -- "$@" "$f"
.Vous pouvez utiliser IFS (séparateur de champ interne) temporairement en utilisant:
la source
ps s'il ne s'agit que d'espace dans l'entrée, alors des guillemets doubles ont bien fonctionné pour moi ...
la source
Pour ajouter à ce que Jonathan a dit: utilisez l'
-print0
option pourfind
en conjonction avec cexargs
qui suit:Cela exécutera la commande
command
avec les arguments appropriés; les répertoires contenant des espaces seront correctement cités (c'est-à-dire qu'ils seront passés comme un argument).la source
Le code ci-dessus convertira les fichiers .mov en .avi. Les fichiers .mov se trouvent dans différents dossiers et les noms de dossier ont des espaces blancs . Mon script ci-dessus convertira les fichiers .mov en fichier .avi dans le même dossier lui-même. Je ne sais pas si cela vous aide les peuples.
Cas:
À votre santé!
la source
echo "$name" | ...
ne fonctionne pas siname
c'est le cas-n
, et comment il se comporte avec des noms avec des séquences d'échappement anti-slash dépend de votre implémentation - POSIX rend le comportement deecho
dans ce cas explicitement indéfini (alors que POSIX étendu par XSI rend l'expansion des séquences d'échappement anti-slash standard , et les systèmes GNU - y compris bash - sansPOSIXLY_CORRECT=1
rompre le standard POSIX en implémentant-e
(alors que la spécification nécessiteecho -e
d'imprimer-e
en sortie).printf '%s\n' "$name" | ...
est plus sûr.Il fallait aussi traiter des espaces dans les chemins. Ce que j'ai finalement fait, c'est d'utiliser une récursivité et
for item in /path/*
:la source
function
mot-clé - cela rend votre code incompatible avec POSIX sh, mais n'a aucun autre but utile. Vous pouvez simplement définir une fonction avecrecursedir() {
, en ajoutant les deux parenthèses et en supprimant le mot-clé de fonction, et cela sera compatible avec tous les shells compatibles POSIX.Convertissez la liste de fichiers en un tableau Bash. Cela utilise l'approche de Matt McClure pour renvoyer un tableau à partir d'une fonction Bash: http://notes-matthewlmcclure.blogspot.com/2009/12/return-array-from-bash-function-v-2.html Le résultat est un moyen pour convertir n'importe quelle entrée multiligne en un tableau Bash.
Cette approche semble fonctionner même lorsque de mauvais caractères sont présents et constitue un moyen général de convertir toute entrée en un tableau Bash. L'inconvénient est que si l'entrée est longue, vous pourriez dépasser les limites de taille de ligne de commande de Bash ou utiliser de grandes quantités de mémoire.
Les approches où la boucle qui travaille finalement sur la liste a également la liste insérée ont l'inconvénient que la lecture de stdin n'est pas facile (comme demander à l'utilisateur une entrée), et la boucle est un nouveau processus, vous vous demandez peut-être pourquoi les variables que vous définissez à l'intérieur de la boucle ne sont pas disponibles une fois la boucle terminée.
Je n'aime pas non plus configurer IFS, cela peut gâcher d'autres codes.
la source
IFS='' read
, sur la même ligne, le paramètre IFS est présent uniquement pour la commande de lecture et ne lui échappe pas. Il n'y a aucune raison de ne pas aimer configurer IFS de cette manière.Eh bien, je vois trop de réponses compliquées. Je ne veux pas passer la sortie de l'utilitaire find ou écrire une boucle, car find a l'option "exec" pour cela.
Mon problème était que je voulais déplacer tous les fichiers avec l'extension dbf vers le dossier actuel et certains d'entre eux contenaient un espace blanc.
Je l'ai abordé ainsi:
Cela me semble bien simple
la source
je viens de découvrir qu'il existe des similitudes entre ma question et la vôtre. Aparentement si vous voulez passer des arguments dans des commandes
pour les imprimer dans l'ordre
notez que $ @ est entouré de guillemets doubles, quelques notes ici
la source
J'avais besoin du même concept pour compresser séquentiellement plusieurs répertoires ou fichiers à partir d'un certain dossier. J'ai résolu en utilisant awk pour ramasser la liste de ls et éviter le problème d'espace vide dans le nom.
Qu'est-ce que tu penses?
la source
la source
Pour moi, cela fonctionne, et c'est à peu près "propre":
la source
Juste eu un problème de variante simple ... Convertissez des fichiers de .flv typés en .mp3 (bâillement).
trouver récursivement tous les fichiers flash de l'utilisateur Macintosh et les transformer en audio (copie, pas de transcodage) ... c'est comme ci-dessus, notant que lire au lieu de juste «pour fichier dans
» s'échappera.
la source
read
aprèsin
est un mot de plus dans la liste sur laquelle vous répétez. Ce que vous avez publié est une version légèrement cassée de ce que le demandeur avait, qui ne fonctionne pas. Vous avez peut-être eu l'intention de publier quelque chose de différent, mais cela est probablement couvert par d'autres réponses ici de toute façon.