Bien que j'utilise BASH depuis plusieurs années, mon expérience avec les scripts BASH est relativement limitée.
Mon code est comme ci-dessous. Il doit récupérer l'intégralité de la structure du répertoire à partir du répertoire actuel et la répliquer dans $OUTDIR
.
for DIR in `find . -type d -printf "\"%P\"\040"`
do
echo mkdir -p \"${OUTPATH}${DIR}\" # Using echo for debug; working script will simply execute mkdir
echo Created $DIR
done
Le problème est, voici un exemple de ma structure de fichiers:
$ ls
Expect The Impossible-Stellar Kart
Five Iron Frenzy - Cheeses...
Five Score and Seven Years Ago-Relient K
Hello-After Edmund
I Will Go-Starfield
Learning to Breathe-Switchfoot
MMHMM-Relient K
Notez les espaces: -S Et for
prend les paramètres mot par mot, donc la sortie de mon script ressemble à ceci:
Creating directory structure...
mkdir -p "/myfiles/multimedia/samjmusicmp3test/Learning"
Created Learning
mkdir -p "/myfiles/multimedia/samjmusicmp3test/to"
Created to
mkdir -p "/myfiles/multimedia/samjmusicmp3test/Breathe-Switchfoot"
Created Breathe-Switchfoot
Mais j'en ai besoin pour récupérer des noms de fichiers entiers (une ligne à la fois) de la sortie de find
. J'ai également essayé de find
mettre des guillemets autour de chaque nom de fichier. Mais cela n'aide pas.
for DIR in `find . -type d -printf "\"%P\"\040"`
Et sortie avec cette ligne modifiée:
Creating directory structure...
mkdir -p "/myfiles/multimedia/samjmusicmp3test/"""
Created ""
mkdir -p "/myfiles/multimedia/samjmusicmp3test/"Learning"
Created "Learning
mkdir -p "/myfiles/multimedia/samjmusicmp3test/to"
Created to
mkdir -p "/myfiles/multimedia/samjmusicmp3test/Breathe-Switchfoot""
Created Breathe-Switchfoot"
Maintenant, j'ai besoin d'un moyen que je puisse parcourir comme ceci, car je souhaite également exécuter une commande plus compliquée impliquant gstreamer
chaque fichier dans une structure similaire suivante. Comment dois-je faire cela?
Edit: J'ai besoin d'une structure de code qui me permettra d'exécuter plusieurs lignes de code pour chaque répertoire / fichier / boucle. Désolé si je n'ai pas été clair.
Solution: j'ai d'abord essayé:
find . -type d | while read DIR
do
mkdir -p "${OUTPATH}${DIR}"
echo Created $DIR
done
Cela a très bien fonctionné pour la plupart. Cependant, j'ai constaté plus tard que, puisque le canal entraînait l'exécution de la boucle while dans un sous-shell, toutes les variables définies dans la boucle étaient ultérieurement indisponibles, ce qui rendait l'implémentation d'un compteur d'erreurs assez difficile. Ma solution finale (à partir de cette réponse sur SO ):
while read DIR
do
mkdir -p "${OUTPATH}${DIR}"
echo Created $DIR
done < <(find . -type d)
Cela m'a plus tard permis d'incrémenter conditionnellement des variables dans la boucle qui resteraient disponibles plus tard dans le script.
/
J'autoriserais tout sauf les caractères non imprimables. Mais tout est permis sauf/
et\0
vous devez donc les autoriser.Réponses:
Vous devez diriger le
find
dans unewhile
boucle.De plus, vous n'aurez pas besoin de l'utiliser
-printf
dans ce cas.Vous pouvez faire cette preuve contre les fichiers avec des retours à la ligne dans leurs noms, si vous le souhaitez, en utilisant un délimiteur nullbyte (c'est le seul caractère qui ne peut pas apparaître dans un chemin de fichier * nix):
Vous constaterez également que l'utilisation
$()
au lieu de backticks est plus polyvalente et plus facile. Ils peuvent être imbriqués beaucoup plus facilement et les devis peuvent être effectués beaucoup plus facilement. Cet exemple artificiel illustrera ces points:Essayez de le faire avec des contre-coups.
la source
"$dir"
, il est préférable d'utiliser"${dir}"
- il est facile de faire la différence entre $ {dir} nom et $ {dirname}, mais $ dirname peut être interprété dans les deux sens.read
lire une ligne entière${dir}
, donc l'IFS n'a pas d'importance.find … -print0 | xargs -0 …
parce que le délimiteur qu'il utilise correspond exactement au seul caractère qui n'est pas autorisé dans les noms de chemin POSIX: NUL (U + 0000).while
. @Chris Johnsen: C'est vrai, mais même les programmes d'extraction de musique n'ont pas tendance à mettre des sauts de ligne dans leurs noms de fichiers. Et s'ils le font, je veux savoir (c'est-à-dire: quelque chose ne va pas) et m'en débarrasser immédiatement ...Voir cette réponse que j'ai écrite il y a quelques jours pour un exemple de script qui gère les noms de fichiers avec des espaces.
Il existe cependant un moyen un peu plus compliqué (mais plus concis) de réaliser ce que vous essayez de faire:
-print0
indique à find de séparer les arguments par un null; le -0 à xargs lui dit d'attendre des arguments séparés par des nulls. Cela signifie qu'il gère très bien les espaces.-I {}
indique à xargs de remplacer la chaîne{}
par le nom de fichier. Cela implique également qu'un seul nom de fichier doit être utilisé par ligne de commande (xargs remplira normalement autant de fichiers que possible sur la ligne)Le reste devrait être évident.
la source
Le problème que vous rencontrez est que l'instruction for répond à la recherche en tant qu'arguments distincts. Le délimiteur d'espace. Vous devez utiliser la variable IFS de bash pour ne pas diviser l'espace.
Voici un lien qui explique comment procéder.
Définissez votre recherche pour sortir votre délimiteur de champ après le% P et définissez votre IFS de manière appropriée. J'ai choisi le point-virgule car il est très peu probable qu'il se trouve dans vos noms de fichiers.
L'autre alternative est d'appeler mkdir à partir de la recherche directement via
-exec
do, vous pouvez ignorer la boucle for. C'est si vous n'avez pas besoin d'effectuer d'analyse syntaxique supplémentaire.la source
/
POSIX et:
les systèmes de fichiers DOS. Il existe des caractères illégaux pour différents systèmes de fichiers que vous pouvez sélectionner pour l'IFS. Rien de plus compliqué et vous feriez mieux d'utiliser perl.find
renvoie les noms de fichiers avec des chemins incluant une barre oblique. Essayez de changer le point-virgule dans votre script en barre oblique et l'écho imprimera le répertoire et le nom de fichier sur des lignes distinctes.while
opté pour l' option pipe , mais cela semble également tout à fait réalisable. Oui, dans ma structure similaire plus tard, j'avais besoin de faire une analyse plus approfondie. (Le nom du fichier d'entrée serait .ogg, qui serait transmis commefilesrc
dans le pipeline gst, mais un équivalent se terminant par .mp3 basé dans le répertoire de sortie serait généré et également transmis au pipeline en tant quefilesink
, et cela doit bien sûr être fait pour chaque fichier, ainsi que certainsecho
à l'utilisateur.)Si le corps de votre boucle est plus d'une seule commande, il est possible d'utiliser xargs pour piloter un script shell:
Assurez-vous d'inclure le tiret de fin (ou un autre «mot») si le shell est de la variété Bourne / POSIX (il est utilisé pour définir $ 0 dans le script shell). De plus, il faut être prudent avec les guillemets, car le script shell est écrit à l'intérieur d'une chaîne entre guillemets au lieu d'être directement à l'invite.
la source
dans votre question mise à jour, vous avez
ce devrait être
la source
la source
ou pour rendre le tout beaucoup moins compliqué:
cela reproduit la structure de répertoires de SRC en DST.
la source
while
...do
...done
plus tard pour effectuer un traitement similaire à partir de find, ce qui nécessiterait l'exécution de plusieurs lignes de code sur chaque fichier (modification de chaîne, écho, lancement gst, etc. ) etrsync
n'y parviendrait pas. C'est pourquoi j'ai spécifié que j'avais besoin de pouvoir exécuter un ensemble de commandes plus compliqué dans une structure similaire. Mon script utilise cette structure de boucle deux fois, donc pour la question, j'ai posté celle avec moins de crud au milieu.Si vous avez installé GNU Parallel http: // www.gnu.org/software/parallel/, vous pouvez le faire:
Regardez la vidéo d'introduction pour GNU Parallel pour en savoir plus: http://www.youtube.com/watch?v=OpaiGYxkSuQ
la source