1
votes

Comment faire correspondre) \ n {en utilisant sed?

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.


1 commentaires

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.


4 Réponses :


2
votes

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

Essayez-le ici!

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 .


5 commentaires

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!



2
votes

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)
{
  ...
}

  • ': 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


4 commentaires

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.



0
votes

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