Comment ajouter un en-tête de licence de manière récursive pour tous les fichiers .h et .cpp dans un répertoire

19

J'essaie d'ajouter un en-tête de licence à tous les fichiers d'en-tête et fichiers source dans un répertoire de projet à l'aide d'une boucle for. Cela ne fonctionne pas, existe-t-il une autre approche sed?

Satyendra
la source

Réponses:

14
for f in **/*.cpp; do
  cat header_file $f > $f.new
  mv $f.new $f
done
Daniel Serodio
la source
1
Il convient de mentionner que vous devez activer globstaren bash pour que cela fonctionne.
Chris Down du
Et que vous avez besoin de bash> 4.0 (par exemple, Mac OS X et RedHat 5 sont toujours sur 3.X)
Matteo
11

Il s'agit plus ou moins d'un long commentaire sur la réponse de Daniel Serodio . J'ai commencé à l'écrire comme un commentaire, mais il est rapidement devenu trop grand ...

Pour qu'un glob bash soit récursif, il faut shopt -s globstar. Vous devez activer globstar, sinon **cela ne fonctionne pas. L'option shell globstar a été introduite dans la version 4 de bash.

Pour éviter de traiter un répertoire tel que my.cpp/, utilisez le test [[ -f $f ]]... Lorsque le test est entre crochets doubles, les variables n'ont pas besoin d'être mises entre guillemets.

Vous pouvez également envisager la possibilité qu'il n'y ait aucun fichier correspondant en utilisant shopt -s nullglob, ce qui permet aux modèles qui ne correspondent à aucun fichier de se développer en une chaîne nulle, plutôt qu'eux-mêmes.

Pour gérer plusieurs modèles, vous pouvez enchaîner les modèles glob: **/*.cpp **/*.h, mais peut - être de préférence, lorsque l'option shell extglob est sur via shopt -s extglob, vous pouvez utiliser ces constructions telles que ce **/*.@(cpp|h)qui évite plusieurs passages sur le système de fichiers; une fois pour chaque motif.

Si vous voulez .filesêtre inclus, utilisez .*.cppetc, ou utilisezshopt -s dotglob

Pour gérer en toute sécurité la modification d'un fichier en cours de piping, utilisez spongefrom package moreutils(cela vous évite de créer votre propre fichier temporaire)


printf "// The License\n\n" > /tmp/$USER-license

shopt -s globstar nullglob extglob
for f in **/*.@(cpp|h) ;do
  [[ -f $f ]] && cat "/tmp/$USER-license" "$f" | sponge "$f"
done
Peter.O
la source
Réponse beaucoup plus complète et informative que l'autre, +1
n0pe
+1, mais [[peut gracieusement gérer un null $f.
enzotib
@enzotib. Merci. Je ne suis pas sûr d'avoir eu cette idée. Cela doit unset x; [ -f $x ] && echo exists
provenir
Étant donné que la version 4 de bash n'est pas encore courante, je remplacerais la **/*.@(cpp|h)par$( find . -name "*.h" -name "*.cpp")
Matteo
3

Merci @fred, @maxmackie, @enzotib.

Pouvez-vous s'il vous plaît vérifier la procédure que j'ai suivie.

#!/bin/sh
# script to copy the headers to all the source files and header files
for f in *.cpp; do
  if (grep Copyright $f);then 
    echo "No need to copy the License Header to $f"
  else
    cat license.txt $f > $f.new
    mv $f.new $f
    echo "License Header copied to $f"
  fi 
done   

sinon l'en-tête de licence sera copié plusieurs fois.

Veuillez me suggérer un modèle pour parcourir tous les en-têtes et sources du répertoire et des sous-répertoires du projet.

Je n'ai pas pu comprendre pleinement ce que @fred a suggéré.

Satyendra
la source
En général, votre code semble correct. Je serais plus précis sur l'identification de l'emplacement exact de "Copyright", par exemple. spécifiez le numéro et la position de la ligne sur cette ligne. Vous pouvez empêcher sed de lire l'intégralité du fichier en quittant la ligne où vous vous attendez à trouver "Copyright" ... par exemple. targln=2; findln=$(sed -rne $targln'{\|// Copyright|=;q}' "$f"); if ((findln==targln));then... mais, bien sûr, au-delà de tout le reste, testez-le soigneusement en premier ... PS. C'est normal pour le cours ici à Unix et Linux de publier de tels extras dans votre question d'origine, pas comme réponse ...
Peter.O
1
Quelques conseils: supprimez les parenthèses autour grep, ajoutez l' -qoption à grep. Ajoutez toujours des guillemets $f.
enzotib
1
Les citations sont vitales, sinon vous serez probablement mordu par le fractionnement des mots. En règle générale, citez chaque expansion sauf si vous savez que le fractionnement de mots ou toute autre interprétation ne sera pas effectué par le shell.
Chris Down du
3

Vous pouvez le faire avec exou edsi vous préférez (vous ne devriez pas le faire avec sedcomme vous l'avez demandé, sedest conçu pour modifier les flux, -iest une mauvaise idée pour diverses raisons):

shopt -s globstar

for _file in **/*.@(cpp|h); do
    ed -s "${_file}" << EOF
0a
/* This file is licensed under the foo license.
   All copyright strictly enforced by the copyright monster. */
.
w
EOF
    done
Chris Down
la source
Pourquoi est-ce sed -iune mauvaise idée?
Daniel Serodio
@DanielSerodio Par défaut, sed -irompt les liens symboliques et les liens physiques, ce qui entraîne un comportement inattendu. Au mieux, il n'est pas intuitif, au pire, il est activement nocif.
Chris Down
2

Si vous en avez un header_fileavec le contenu souhaité:

find -name '*.cpp' -exec bash -c 'cat header_file \{} > \{}.new; mv \{}.new  \{}' \;
find -name '*.h' -exec bash -c 'cat header_file \{} > \{}.new; mv \{}.new  \{}' \;
albfan
la source
1
#!/bin/bash

for i in `find . -name '*.[m|h]'` # or whatever other pattern...
do
  echo $i
  if ! grep -q Copyright $i
  then
    cat copyright.txt $i >$i.new && mv $i.new $i
  fi
done
enter code here
PokerIncome.com
la source