Je veux remplacer uniquement les premières k
instances d'un mot.
Comment puis-je faire ceci?
Par exemple. Le fichier Say foo.txt
contient 100 occurrences d'occurrences du mot «linux».
Je dois remplacer seulement les 50 premières occurrences.
text-processing
sed
awk
narendra-choudhary
la source
la source
Réponses:
La première section ci-dessous décrit l'utilisation
sed
de la modification des premières occurrences k sur une ligne. La deuxième section étend cette approche pour modifier uniquement les premières occurrences k d'un fichier, quelle que soit la ligne sur laquelle elles apparaissent.Solution orientée ligne
Avec sed standard, il existe une commande pour remplacer la k-ème occurrence d'un mot sur une ligne. Si
k
est 3, par exemple:Ou, on peut remplacer toutes les occurrences par:
Ni l'un ni l'autre n'est ce que vous voulez.
GNU
sed
propose une extension qui changera la k-ème occurrence et tout cela après. Si k est 3, par exemple:Ceux-ci peuvent être combinés pour faire ce que vous voulez. Pour modifier les 3 premières occurrences:
où
\n
est utile ici car nous pouvons être sûrs qu'il ne se produit jamais sur une ligne.Explication:
Nous utilisons trois
sed
commandes de substitution:s/\<old\>/\n/g4
C'est l'extension GNU pour remplacer la quatrième et toutes les occurrences suivantes de
old
with\n
.La fonction d'expression régulière étendue
\<
est utilisée pour faire correspondre le début d'un mot et\>
pour correspondre à la fin d'un mot. Cela garantit que seuls les mots complets correspondent. L'expression regex étendue nécessite l'-E
option desed
.s/\<old\>/new/g
Seules les trois premières occurrences de
old
restent et cela les remplace toutes parnew
.s/\n/old/g
La quatrième et toutes les occurrences restantes de
old
ont été remplacées par\n
dans la première étape. Cela les ramène à leur état d'origine.Solution non GNU
Si GNU sed n'est pas disponible et que vous souhaitez modifier les 3 premières occurrences de
old
ennew
, utilisez troiss
commandes:Cela fonctionne bien quand
k
est un petit nombre mais évolue mal à grandk
.Étant donné que certains seds non GNU ne prennent pas en charge la combinaison de commandes avec des points-virgules, chaque commande ici est introduite avec sa propre
-e
option. Il peut également être nécessaire de vérifier que votresed
prend en charge les symboles de limite de mot,\<
et\>
.Solution orientée fichier
Nous pouvons dire à sed de lire l'intégralité du fichier puis d'effectuer les substitutions. Par exemple, pour remplacer les trois premières occurrences de l'
old
utilisation d'un sed de style BSD:Les commandes sed
H;1h;$!d;x
lisent l'intégralité du fichier.Étant donné que ce qui précède n'utilise aucune extension GNU, il devrait fonctionner sur BSD (OSX) sed. Notez, pensait, que cette approche nécessite un
sed
qui peut gérer les longues lignes. GNUsed
devrait aller bien. Ceux qui utilisent une version non GNU desed
devraient tester sa capacité à gérer les longues lignes.Avec un sed GNU, nous pouvons continuer à utiliser l'
g
astuce décrite ci-dessus, mais avec\n
remplacé par\x00
, pour remplacer les trois premières occurrences:Cette approche évolue bien et
k
devient grande. Cela suppose, cependant, que ce\x00
n'est pas dans votre chaîne d'origine. Puisqu'il est impossible de mettre le caractère\x00
dans une chaîne bash, c'est généralement une hypothèse sûre.la source
tr '\n' '|' < input_file | sed …
. Mais, bien sûr, cela convertit l'intégralité de l'entrée en une seule ligne, et certains seds non GNU ne peuvent pas gérer des lignes arbitrairement longues. (2) Vous dites: «… ci-dessus, la chaîne entre guillemets'|'
doit être remplacée par n'importe quel caractère, ou chaîne de caractères,…» Mais vous ne pouvez pas utilisertr
pour remplacer un caractère par une chaîne (de longueur> 1). (3) Dans votre dernier exemple, vous dites-e 's/\<old\>/new/' -e 's/\<old\>/w/' | tr '\000' '\n'\>/new
. Cela semble être une faute de frappe pour-e 's/\<old\>/new/' -e 's/\<old\>/new/' -e 's/\<old\>/new/' | tr '\000' '\n'
.Utiliser Awk
Les commandes awk peuvent être utilisées pour remplacer les N premières occurrences du mot par le remplacement.
Les commandes ne remplaceront que si le mot est une correspondance complète.
Dans les exemples ci-dessous, je remplace les premières
27
occurrences deold
parnew
Utilisation de sous
Remplacement manuel du champ
Effectuer une vérification avant
RÉSULTATS
Par exemple
à
la source
$i
bit, il a été modifié, merci :)Supposons que vous souhaitiez remplacer uniquement les trois premières instances d'une chaîne ...
note: ce qui précède ne fonctionnera probablement pas avec des commentaires intégrés
... ou dans mon cas d'exemple, d'un '1' ...
SORTIE:
Là, j'utilise deux techniques notables. En premier lieu, chaque occurrence de
1
sur une ligne est remplacée par\n1
. De cette façon, comme je fais les remplacements récursifs ensuite, je peux être sûr de ne pas remplacer l'occurrence deux fois si ma chaîne de remplacement contient ma chaîne de remplacement. Par exemple, si je remplacehe
parhey
cela fonctionnera.Je fais ça comme:
Deuxièmement, je compte les remplacements en ajoutant un caractère à l'
h
ancien espace pour chaque occurrence. Une fois que j'aurai atteint trois, il ne se passera plus. Si vous appliquez cela à vos données et changez le\{3\}
nombre total de remplacements que vous désirez et les/\n1/
adresses en tout ce que vous voulez remplacer, vous ne devez remplacer que le nombre que vous souhaitez.Je n'ai fait que toutes les
-e
choses pour la lisibilité. POSIX Il pourrait être écrit comme ceci:Et avec GNU
sed
:Souvenez-vous également qu'il
sed
est orienté ligne - il ne lit pas dans tout le fichier et essaie ensuite de le boucler comme c'est souvent le cas dans d'autres éditeurs.sed
est simple et efficace. Cela dit, il est souvent pratique de faire quelque chose comme ceci:Voici une petite fonction shell qui la regroupe en une commande simplement exécutée:
Donc avec ça je peux faire:
...et obtenir...
...ou...
...obtenir...
... ou, pour correspondre à votre exemple (sur un ordre de grandeur plus petit) :
la source
Une courte alternative en Perl:
Modifiez la valeur de `$ n $ à votre guise.
Comment ça marche:
new
àold
(s/old/new/
) et chaque fois qu'il le peut, il incrémente la variable$i
(++$i
).1 while ...
) tant qu'il a effectué moins de$n
substitutions au total et qu'il peut effectuer au moins une substitution sur cette ligne.la source
Utilisez une boucle shell et
ex
!Oui, c'est un peu maladroit.
;)
Remarque: cela peut échouer s'il y a moins de 50 instances de
old
dans le fichier. (Je ne l'ai pas testé.) Si c'est le cas, le fichier ne sera pas modifié.Mieux encore, utilisez Vim.
Explication:
la source
Une solution simple, mais pas très rapide, consiste à parcourir les commandes décrites dans /programming/148451/how-to-use-sed-to-replace-only-the-first-occurrence-in-a -fichier
Cette commande sed particulière ne fonctionne probablement que pour GNU sed et si newword ne fait pas partie de oldword . Pour les sed non GNU, voyez ici comment remplacer uniquement le premier pattern d'un fichier.
la source
Avec GNU,
awk
vous pouvez définir le séparateur d'enregistrementRS
sur le mot à remplacer délimité par des limites de mot. Ensuite, il s'agit de définir le séparateur d'enregistrement sur la sortie sur le mot de remplacement pour les premiersk
enregistrements tout en conservant le séparateur d'enregistrement d'origine pour le resteOU
la source