J'ai besoin d'écrire un script shell qui entrera une chaîne dans mes fichiers .c après un modèle assez compliqué à faire correspondre.
Le modèle est: ) \ n {
,
sans tabulations / espaces entre le \ n
et le {
. C'est-à-dire que je veux faire correspondre avec un {
situé dans la première colonne de mon fichier (ceci afin de correspondre UNIQUEMENT avec ) \ n {
suivant les déclarations de fonction en C , pas celles qui suivent des boucles ou des conditions).
void some_function_declaration (char var1, char var2)
{
J'ai lu des manuels, des forums et je ne parviens toujours pas à trouver la bonne façon d'écrire le modèle correspondant ou de trouver le regex correspondant. Le résultat correspondant serait:
void some_function_declaration (char var1, char var2)
{time_exe (__ func__, cl (horloge ()));
reste de la fonction ...
Ce qui suit est ce que j'ai trouvé jusqu'ici et ne fonctionne pas.
Essayez 1
sed -i '' '/) $ \ n {/ a time_exe (__ func__, cl (clock ()));' >> list_func2.c
Essayez 2
sed -i '' '/) \ n {/ a time_exe (__ func__, cl (clock ()));' >> list_func2.c
Essayez 3
sed -i '' '/) / \ n {/ a time_exe (__ func__, cl (clock ()));' >> list_func2.c
Je serais très heureux d'entendre vos recommandations à ce sujet.
4 Réponses :
Vous devriez éviter d'utiliser sed
qui est un outil basé sur des lignes et qui ne gérera pas bien ce genre de tâche.
Si vous insistez pour utiliser sed
et utilisez GNU sed
, vous pouvez cependant utiliser l'option -z
/ --null-data
qui lira tout le fichier en un seul passage ( lire des enregistrements séparés par octets NUL plutôt que des enregistrements séparés par saut de ligne) et vous permettra d'utiliser le modèle ) \ n {
comme vous vous en doutez:
sed '/)$/{N;s/)\n{/) {\n\ttime_exe(__func__, cl(clock()));/;t;P;D}'
Comme cela nécessite de charger tout le fichier en mémoire, attendez-vous à des performances terribles pour des fichiers volumineux.
Si vous aimez le charabia impossible à entretenir, vous pouvez résoudre ce problème en utilisant sed
commandes P
, t
et D
moins connues:
$ { echo "line1)"; echo "{line2"; } | sed -z 's/)\n{/X/g' line1Xline2
Cela fonctionne en chargeant une ligne supplémentaire dans l'espace du motif ( N code>) lorsqu'une ligne se terminant par
)
est rencontrée, en essayant de vous abonner titrer contre le motif à deux lignes, et imprimer ( P
) et supprimer ( D
) la première ligne de l'espace du motif si le motif ne correspond pas (sinon t
passe à l'itération suivante), laissant la deuxième ligne dans l'espace de motif à utiliser comme première ligne de l'itération suivante.
Utilisation de / motif de première ligne / { N; s / motif entier / remplacement /}
est souvent assez bon, mais cela peut échouer car N
consommera une ligne sur laquelle vous ne testerez pas le motif de première ligne. Ceci est illustré ici .
Auriez-vous recommandé d'utiliser awk à la place? Le -z n'est pas reconnu sur ma distribution, UNIX Mac OS. Merci pour votre réponse perspicace et détaillée.
Je ne pense pas que awk
serait génial, il est également basé sur des lignes (ou du moins sur des enregistrements) et n'aide pas beaucoup avec l'utilisation des regex. Je recommanderais d'utiliser un autre outil de recherche / remplacement de regex, je pense que la suggestion de Glenn Jackman d'utiliser perl
est bonne si elle est disponible et que les fichiers ne sont pas énormes. Sinon, peut-être que votre éditeur de texte ou l'IDE que vous utilisez inclurait un moteur d'expression régulière PCRE (par exemple, je pourrais utiliser Notepad ++ sous Windows), et peut-être que des outils d'expression régulière spécialisés pourraient être optimisés pour les fichiers volumineux.
Si je me souviens bien, sed (le bon vieux sed par défaut, pas GNU) a une notion d'espace de motif et d'espace de stockage, et commande de les échanger. Cela signifie qu'il devrait être possible de traiter les lignes par roulement de paires, au détriment d'un script sed illisible et non maintenable. J'avais l'habitude de commettre ce genre de choses quand Python n'était pas disponible.
@SergeBallesta à droite, et je pense que l'utilisation de D
devrait le rendre possible sans même avoir à utiliser l'espace d'attente. Je vais voir si je peux produire une de ces bonnes vieilles lignes de charabia pire que perl;)
@SergeBallesta ce n'est même pas aussi terrible que je le pensais!
Je suis d'accord avec @Aaron, mais si vous voulez toujours exactement sed
, regardez ceci:
sed -e ':a' -e 'N' -e'$!ba' -e 's/)\n{/)\n{ ... ;/g'
Et appliquez sed
:
$ cat /tmp/del.txt | sed ':a;N;$!ba;s/)\n{/)\{\ntime_exe(__func__, cl(clock()));/g' void some_function_declaration(char var1, char var2){ time_exe(__func__, cl(clock())); ... } void enother_function_declaration(char var1, char var2){ time_exe(__func__, cl(clock())); ... }
Je pense qu'il semble que vous vouliez
UPD
Laissez-moi vous expliquer ..
Une autre syntaxe compatible multiplateforme est:
$ cat /tmp/del.txt void some_function_declaration(char var1, char var2) { ... } void enother_function_declaration(char var1, char var2) { ... }
où
': a'
- crée une étiquette de branche (nommée a
) qui peut être renvoyée plus tard 'N'
- ajoute la ligne suivante à la ligne courante (avec \ n
entre) '$! ba'
- passe à l'étiquette a
si la ligne suivante est la dernière ligne 's /) \ n {/) \ n {...; / g'
- effectue une substitution globale sur une seule ligne, composée de toutes les lignes et \ n
s
Ah, j'ai pensé un instant que je m'étais trompé sur le comportement de N
, mais vous êtes juste en train de réimplémenter -z
, en lisant tout le fichier avant d'appliquer la substitution. Néanmoins, c'est utile si OP utilise une implémentation qui n'a pas -z
Honnêtement, je ne connais pas d'autres solutions que la lecture du fichier entier lorsque je souhaite effectuer de nouvelles lignes avec sed.
Parfois, vous êtes capable de tester une première ligne et de ne consommer que les (quelques) lignes suivantes si vous correspondez au début du modèle, mais comme je l'ai expliqué dans le dernier § de ma réponse, ce serait probablement une mauvaise idée dans ce cas Cas. Je pensais que vous aviez opté pour cette solution et j'ai été surpris de la tester avec succès jusqu'à ce que j'ai correctement lu et compris votre commande sed
.
Merci pour votre réponse détaillée. Cela m'a certainement aidé à comprendre plus de principes fondamentaux sur sed. J'espère que cela aidera également d'autres personnes.
Cela pourrait fonctionner pour vous (GNU sed):
sed ':a;/)$/!b;n;s/^{/& time_exe(__func__, cl(clock()));/;Ta' file
Si la ligne actuelle ne se termine pas par )
, interrompez tout traitement ultérieur par sed. Sinon, imprimez la ligne actuelle et lisez la suivante. Si cette ligne ne commence pas par une vérification {
depuis le début, sinon changez-la au format souhaité.
Le format souhaité peut également être ajouté ou inséré, voir ci-dessous:
sed ':a;/)$/!b;n;/^{/!ba;a{ time_exe(__func__, cl(clock()));' file
Ou,
sed ':a;/)$/!b;n;/^{/!ba;c{ time_exe(__func__, cl(clock()));' file
Cela échoue lorsque le modèle est précédé d'une ligne qui se termine par )
, vérifiez ce test a > ou le dernier § de ma réponse pour une explication détaillée.
Merci pour votre réponse et vos commentaires précis. J'aurais dû être plus explicite sur le fait que l'utilisation de sed n'était pas obligatoire parce que je n'ai pas GNU sed. J'ai aussi essayé votre réponse et cela n'a pas fonctionné dans tous les cas. Ajoutait parfois n ...
après ma déclaration de fonction. J'espère que cela aidera d'autres personnes.
@Aaron vous avez raison, a ajouté un chèque pour une telle condition qui refait le match initial. Merci
@aboitier Dans ces cas, les solutions fournies ne dépendent pas de GNU sed. Lorsque je donne une réponse, j'utilise le sed qui est présent sur ma machine et afin d'éviter d'éventuels malentendus (surtout lorsque les réponses doivent être améliorées à cause de réflexions ultérieures) je la documente avec GNU sed
.
Perl peut être bon pour cela:
$ perl -0777 -pe 's {\)\n\{} {$& some other stuff;\n}g' file.c void some_function_declaration(char var1, char var2) { some other stuff; if (a) { b; } } void func2() { some other stuff; // }
$ cat file.c void some_function_declaration(char var1, char var2) { if (a) { b; } } void func2() { // }
Même mise en garde que Réponse d'Aaron en raison de la lecture du fichier entier en mémoire.
Merci, cela fait le travail. Mes fichiers ne sont pas trop volumineux, cela ne devrait donc pas poser de problème.
Vous ne pouvez pas faire cela avec un simple motif d'expression régulière sed car sed est orienté ligne - le fichier est divisé sur les nouvelles lignes donc, sans faire de programmation sed , vous ne verrez jamais de nouvelle ligne espace de motif sed. Et, à moins que vous ne soyez prêt à le supporter, la programmation sed peut être douloureuse.