Je veux supprimer une ligne d'un fichier qui ne contient un caractère particulier qu'une seule fois, si elle est présente plusieurs fois ou n'est pas présente, conservez la ligne dans le fichier.
Par exemple:
DTHGTY
FGTHDC
HYTRHD
HTCCYD
JUTDYC
Ici, le caractère que je veux supprimer est C
ainsi, la commande doit supprimer les lignes FGTHDC
et JUTDYC
parce qu'elles ont C
exactement une fois.
Comment puis-je faire cela en utilisant soit sed
ou awk
?
la source
awk
séparateur de champ!awk 'BEGIN { print "FS={" FS"}","OFS={" OFS "}";} {printf "%d fields : ",NF; for (i=1;i<=NF;i++) {printf "{" $i "} ";}; print "" }'
et le nourrir de quelques lignes, certaines ayant plusieurs spces, et d'autres commençant par des espaces)approche sed :
-i
L'option permet la modification de fichier sur place/^[^C]*C[^C]*$/
- correspond aux lignes quiC
ne contiennent qu'une seule foisd
- supprimer les lignes correspondantesla source
Cela peut être fait avec
sed
:Code:
Résultats:
Comment?
C
via/C.*C/p
C
via/C/d
, cela inclut les lignes déjà imprimées à l'étape 1la source
Cela supprime les lignes avec exactement une occurrence de C.
L'expression régulière
[^C]
correspond à un caractère qui n'est pas C (ou nouvelle ligne), et l'opérateur de répétition (alias étoile de Kleene)*
spécifie zéro ou plusieurs répétitions de l'expression précédente.La sortie par défaut de
grep
(et de la plupart des autres outils orientés texte) est la sortie standard; rediriger vers un nouveau fichier et peut-être le déplacer par-dessus le fichier d'origine si c'est ce que vous voulez. Le même regex peut être utilisé avecsed -i
pour l'édition sur place:(Sur certaines plates-formes, notamment * BSD, y compris macOS, l'
-i
option nécessite un argument, comme-i ''
.)la source
sed -i '/^[^C]*C[^C]*$/d' file
- on dirait qu'il a été posté avant, comment pensez-vous, plagiat?grep
réponse, mais elle s'étend évidemment facilement à lased -i
variante. Je n'ai pas vu votre réponse car je cherchais desgrep
réponses précédentes .-i
avecsed
et au lieu de rediriger vers un nouveau fichier et remplacer l'original que si l'sed
utilitaire est sorti sans erreur.grep -vx '[^C]*C[^C]*'
grep
parce qu'il est plus clair et plus robuste (en particulier,sed
a un code de sortie moins informatif).L'outil POSIX pour les modifications scriptées d'un fichier (plutôt que d'imprimer le contenu modifié en sortie standard) est
ex
.Bien sûr, vous pouvez l' utiliser
sed -i
si votre version de Sed le prend en charge, sachez que ce n'est pas portable si vous écrivez un script destiné à être exécuté sur différents types de systèmes.David Foerster a demandé dans les commentaires:
Réponse: oui.
Pour
printf
vsecho
c'est une question de portabilité; voir Pourquoi printf est-il meilleur que l'écho? Et il est également plus facile de séparer les nouvelles lignes entre les commandes à l'aide deprintf
.Pour
printf ... | ex
vs.ex -c ...
, c'est une question de gestion des erreurs. Pour cette commande spécifique, cela n'aurait pas d'importance, mais en général c'est le cas; par exemple, essayez de mettredans un script. Comparez avec ce qui suit:
Le premier se bloque et attend l'entrée; le second se fermera lorsque EOF sera reçu par la
ex
commande, donc le script continuera. Il existe d'autres solutions de contournement, telles ques///e
, mais elles ne sont pas spécifiées par POSIX. Je préfère utiliser le formulaire portable, illustré ci-dessus.Pour la
g
commande, il doit y avoir une nouvelle ligne à la fin, et je préfère utiliserprintf
pour encapsuler les commandes plutôt que d'incorporer une nouvelle ligne entre guillemets simples.la source
printf
et nonecho
ou quelque chose comme çaex -c COMMAND
?printf
VSecho
(bien que je préfère généralementecho
quand l'argument est codé en dur), mais je ne l'ai pasex
beaucoup utilisé jusqu'à présent.Voici quelques options utilisant perl.
Puisque vous ne correspondez qu'à un seul caractère, vous pouvez utiliser
tr/C//
(une traduction, sans remplacement), pour renvoyer le nombre de correspondances deC
:Plus généralement, si vous souhaitez faire correspondre une chaîne à plusieurs caractères ou une expression régulière, vous pouvez utiliser ceci:
Cela affecte les correspondances de l'expression régulière
/C/g
à une liste@m
et imprime des lignes lorsque la longueur de cette liste n'est pas1
.Le
-i
commutateur peut être ajouté pour modifier "sur place".la source
la source
sed
,t #...
typiquement une branche à l'étiquette appelée#...
dans la plupart des autressed
implémentations.!b
GNU sed car la branche n'aime rien sauf une étiquette ou une nouvelle ligne après.b
,t
,:
,}
(etr file
,w file
...) ne peut pas avoir une commande après eux sur la même ligne. Vous pouvez également utiliser des-e
options distinctes .g
modificateur.Pour tous ceux qui le souhaitent
awk
, je proposeraissautez la ligne si elle correspond au motif, imprimez-la autrement. Vous n'avez pas vraiment besoin
{print}
, vous pouvez utiliser//
et imprimer par défaut, mais je pense que c'est plus clair.Ma première pensée a été d'utiliser
egrep -v
le même modèle, mais cela ne répond pas réellement à la question posée.la source
{next}
? Dites simplementawk '/pattern/ {next} 1'
et toutes les lignes ne correspondant pas au motif seront imprimées. Ou, mieux,awk '!/pattern/'
pour les imprimer directement.!/pattern/
(qui a en quelque sorte glissé mon esprit) mais je préfère de loin voir un explication//{print}
plutôt qu'un cryptique1
. Supposez le moins de compétence et de maîtrise de la part de la personne suivante pour maintenir votre code, tout en évitant de le rendre sérieusement moins efficace ou efficient.