Je n'ai pas le cœur de tout recommencer, mais j'ai écrit ceci en réponse à Commandline Find Sed Exec . Là, le demandeur voulait savoir comment déplacer une arborescence entière, en excluant éventuellement un répertoire ou deux, et renommer tous les fichiers et répertoires contenant la chaîne "OLD" pour qu'ils contiennent à la place "NEW" .
En plus de décrire le comment avec une verbosité minutieuse ci-dessous, cette méthode peut également être unique en ce qu'elle intègre le débogage intégré. Il ne fait fondamentalement rien du tout tel qu'il est écrit, sauf compiler et enregistrer dans une variable toutes les commandes qu'il pense devoir faire pour effectuer le travail demandé.
Il évite également explicitement les boucles autant que possible. Outre la sed
recherche récursive de plus d'une correspondance du motif, il n'y a pas d'autre récursivité à ma connaissance.
Et enfin, il est entièrement null
délimité - il ne se déclenche sur aucun caractère dans aucun nom de fichier à l'exception du null
. Je ne pense pas que tu devrais avoir ça.
Au fait, c'est VRAIMENT rapide. Regardez:
% _mvnfind() { mv -n "${1}" "${2}" && cd "${2}"
> read -r SED <<SED
> :;s|${3}\(.*/[^/]*${5}\)|${4}\1|;t;:;s|\(${5}.*\)${3}|\1${4}|;t;s|^[0-9]*[\t]\(mv.*\)${5}|\1|p
> SED
> find . -name "*${3}*" -printf "%d\tmv %P ${5} %P\000" |
> sort -zg | sed -nz ${SED} | read -r ${6}
> echo <<EOF
> Prepared commands saved in variable: ${6}
> To view do: printf ${6} | tr "\000" "\n"
> To run do: sh <<EORUN
> $(printf ${6} | tr "\000" "\n")
> EORUN
> EOF
> }
% rm -rf "${UNNECESSARY:=/any/dirs/you/dont/want/moved}"
% time ( _mvnfind ${SRC=./test_tree} ${TGT=./mv_tree} \
> ${OLD=google} ${NEW=replacement_word} ${sed_sep=SsEeDd} \
> ${sh_io:=sh_io} ; printf %b\\000 "${sh_io}" | tr "\000" "\n" \
> | wc - ; echo ${sh_io} | tr "\000" "\n" | tail -n 2 )
<actual process time used:>
0.06s user 0.03s system 106% cpu 0.090 total
<output from wc:>
Lines Words Bytes
115 362 20691 -
<output from tail:>
mv .config/replacement_word-chrome-beta/Default/.../googlestars \
.config/replacement_word-chrome-beta/Default/.../replacement_wordstars
REMARQUE: ce qui précède function
nécessitera probablement des GNU
versions de sed
et find
pour gérer correctement lefind printf
et sed -z -e
et:;recursive regex test;t
appels . Si ceux-ci ne sont pas disponibles pour vous, la fonctionnalité peut probablement être dupliquée avec quelques ajustements mineurs.
Cela devrait faire tout ce que vous vouliez du début à la fin avec très peu de complications. Je l'ai fait fork
avec sed
, mais je pratiquais aussi sed
des techniques de branchement récursives, c'est pourquoi je suis ici. C'est un peu comme obtenir une coupe de cheveux à prix réduit dans une école de coiffeur, je suppose. Voici le flux de travail:
rm -rf ${UNNECESSARY}
- J'ai volontairement laissé de côté tout appel fonctionnel qui pourrait supprimer ou détruire des données de toute nature. Vous dites que cela
./app
pourrait être indésirable. Supprimez-le ou déplacez-le ailleurs au préalable, ou, alternativement, vous pouvez créer une \( -path PATTERN -exec rm -rf \{\} \)
routine pour find
le faire par programme, mais celui-ci est tout à vous.
_mvnfind "${@}"
- Déclarez ses arguments et appelez la fonction de travail.
${sh_io}
est particulièrement important en ce qu'il enregistre le retour de la fonction. ${sed_sep}
vient dans une seconde près; c'est une chaîne arbitraire utilisée pour référencer sed
la récursion de la fonction. Si ${sed_sep}
est défini sur une valeur qui pourrait potentiellement être trouvée dans l'un de vos noms de chemin ou de fichier sur lesquels vous avez agi ... eh bien, ne le laissez pas être.
mv -n $1 $2
- L'arbre entier est déplacé depuis le début. Cela évitera beaucoup de maux de tête; Crois moi. Le reste de ce que vous voulez faire - le changement de nom - est simplement une question de métadonnées du système de fichiers. Si vous étiez, par exemple, en train de déplacer cela d'un lecteur à un autre, ou à travers les limites du système de fichiers de tout type, vous feriez mieux de le faire immédiatement avec une seule commande. C'est aussi plus sûr. Notez l'
-noclobber
option définie pour mv
; comme écrit, cette fonction ne mettra pas ${SRC_DIR}
là où un ${TGT_DIR}
existe déjà.
read -R SED <<HEREDOC
- J'ai localisé toutes les commandes de sed ici pour économiser sur les tracas et les lire dans une variable pour alimenter sed ci-dessous. Explication ci-dessous.
find . -name ${OLD} -printf
- Nous commençons le
find
processus. Avec find
nous ne recherchons que tout ce qui doit être renommé car nous avons déjà effectué toutes les opérations de placement à place mv
avec la première commande de la fonction. Plutôt que de prendre une action directe avec find
, comme un exec
appel, par exemple, nous l'utilisons à la place pour construire dynamiquement la ligne de commande avec -printf
.
%dir-depth :tab: 'mv '%path-to-${SRC}' '${sed_sep}'%path-again :null delimiter:'
- Après avoir
find
localisé les fichiers dont nous avons besoin, il construit et imprime directement (la plupart ) de la commande dont nous aurons besoin pour traiter votre changement de nom. Le %dir-depth
clouage au début de chaque ligne aidera à nous assurer que nous n'essayons pas de renommer un fichier ou un répertoire dans l'arborescence avec un objet parent qui n'a pas encore été renommé. find
utilise toutes sortes de techniques d'optimisation pour parcourir l'arborescence de votre système de fichiers et il n'est pas certain qu'il renverra les données dont nous avons besoin dans un ordre sûr pour les opérations. C'est pourquoi nous ...
sort -general-numerical -zero-delimited
- Nous trions toute
find
la sortie de %directory-depth
afin que les chemins les plus proches en relation avec $ {SRC} soient travaillés en premier. Cela évite les erreurs possibles impliquant des mv
fichiers dans des emplacements inexistants et minimise le besoin de bouclage récursif. ( en fait, vous pourriez avoir du mal à trouver une boucle )
sed -ex :rcrs;srch|(save${sep}*til)${OLD}|\saved${SUBSTNEW}|;til ${OLD=0}
- Je pense que c'est la seule boucle dans tout le script, et elle ne boucle que sur la seconde
%Path
imprimée pour chaque chaîne au cas où elle contiendrait plus d'une valeur $ {OLD} qui pourrait avoir besoin d'être remplacée. Toutes les autres solutions que j'ai imaginées impliquaient un deuxième sed
processus, et même si une boucle courte n'est peut-être pas souhaitable, elle bat certainement la génération et la fourche d'un processus entier.
- Donc fondamentalement quoi
sed
fait ici est de rechercher $ {sed_sep}, puis, après l'avoir trouvé, le sauvegarde et tous les caractères qu'il rencontre jusqu'à ce qu'il trouve $ {OLD}, qu'il remplace ensuite par $ {NEW}. Il revient ensuite à $ {sed_sep} et cherche à nouveau $ {OLD}, au cas où cela se produirait plus d'une fois dans la chaîne. S'il n'est pas trouvé, il imprime la chaîne modifiée dansstdout
(qu'il attrape ensuite à nouveau) et termine la boucle.
- Cela évite d'avoir à analyser la chaîne entière et garantit que la première moitié de la
mv
chaîne de commande, qui doit bien sûr inclure $ {OLD}, l'inclut, et la seconde moitié est modifiée autant de fois que nécessaire pour effacer le $ {OLD} nom du mv
chemin de destination de.
sed -ex...-ex search|%dir_depth(save*)${sed_sep}|(only_saved)|out
- Les deux
-exec
appels se produisent ici sans une seconde fork
. Dans le premier, comme nous l'avons vu, nous modifions la mv
commande fournie par find
la -printf
commande de fonction de s comme nécessaire pour modifier correctement toutes les références de $ {OLD} à $ {NEW}, mais pour ce faire, nous devions utiliser des points de référence arbitraires qui ne devraient pas être inclus dans le résultat final. Donc, une fois que sed
tout ce qu'il a à faire est terminé, nous lui demandons d'effacer ses points de référence du tampon de maintien avant de le transmettre.
ET MAINTENANT NOUS SOMMES DE RETOUR
read
recevra une commande qui ressemble à ceci:
% mv /path2/$SRC/$OLD_DIR/$OLD_FILE /same/path_w/$NEW_DIR/$NEW_FILE \000
Il sera read
en ${msg}
tant que${sh_io}
qui peut être examinée à volonté en dehors de la fonction.
Cool.
-Mike