Nummerieren Sie eine Liste neu, wenn ein neuer Eintrag eingefügt wird

6

Ich habe eine Textdatei mit nummerierten Einträgen:

1. foo
2. bar 100%
3. kittens
4. eat cake
5. unicorns
6. rainbows

und so weiter bis zu einer großen Anzahl. Nach einer leeren Zeile beginnt ein neuer Block bei 1.

Ich füge einen neuen Eintrag ein und ersetze beispielsweise 4. und muss alle nachfolgenden Einträge im Block neu nummerieren:

1. foo
2. bar 100%
3. kittens
4. sunshine <
5. eat cake
6. unicorns
7. rainbows
Zanna
quelle
2
Sind Blöcke zusammenhängend oder gibt es ein Trennzeichen? (Auch eine leere Zeile?)
Stephen Kitt

Antworten:

5

VIM-Lösungen

Es gibt zwei Lösungen: Eine erfolgt durch Automatisieren des CtrlaTastendrucks über eine Auswahl, die zweite durch Ausführen eines Musteraustauschs mit submatch(0)+1Über der Auswahl. Zuerst die Schlüsselautomatisierung.

Beginnen Sie mit der Erstellung Ihrer Liste:

1. foo
2. bar 100%
3. kittens
4. eat cake
5. unicorns
6. rainbows

Fügen Sie einen Eintrag ein

1. foo
2. bar 100%
3. kittens
4. eat cake
4. sunshine
5. unicorns
6. rainbows

Positionieren Sie den Cursor auf 4. sunshineund aus dem Befehlsmodus. Drücken Sie shift+ vund dann shift+ g. Dies ist eine visuelle Auswahl bis zum Ende der Datei. Sie können den Cursor auch wie gewohnt an das Ende eines Blocks bewegen.

Drücken Sie :, um den Befehlsmodus aufzurufen, und Sie sehen Folgendes : :'<,'> . Geben Sie nun Folgendes ein:

norm Ctrl+ V Ctrl+A

Strg-v und Strg-A ermöglichen es Ihnen, die "genaue" Taste einzugeben, damit sie in ^Ahervorgehoben wird. Dies bedeutet im Grunde for all lines selected, execute in normal mode keypress Ctrl-A, und Strg-A erhöht standardmäßig die Zahl unter dem Cursor. Sie werden sehen, wie sich die Zahlen ändern

Lösung in Aktion:

Vor

Geben Sie hier die Bildbeschreibung ein

Nach

Geben Sie hier die Bildbeschreibung ein


Eine andere Möglichkeit wäre, alles aus der ersten wiederholten Nummer wie zuvor ( Shiftvdann G) auszuwählen und in den Befehlsmodus zu wechseln, um Folgendes auszuführen:

:'<,'>s/\v(^\d+)\./\=(submatch(0)+1).'.'/ 
Sergiy Kolodyazhnyy
quelle
Dies ist genau die einfache denkwürdige Antwort, nach der ich gesucht habe und die ich nicht herausfinden konnte!
Zanna
Ich kann Ctrl-A in vim nicht verwenden / eingeben, da cntrl-A einem anderen Schlüssel für zugeordnet ist tmux. Gibt es eine Möglichkeit, den gleichen Befehl wie ctrl-Avim über den vims-Befehlsmodus zu senden , sodass ich nicht Strg-A eingeben muss?
Alpha_989
@ alpha_989 Nun, laut digitalronin.github.io/2016/06/28/vim-increment-column.html tippt dieser Typ Ctrl-a a. Funktioniert auch an meinem Ende :)
Sergiy Kolodyazhnyy
@ alpha_989 Ich habe eine weitere Lösung im Befehlsmodus hinzugefügt. Das vermeidet die Eingabe von Strg + a insgesamt
Sergiy Kolodyazhnyy
13

Sie können Ihren neuen Eintrag jederzeit mit einer x. newentrySyntax hinzufügen und anschließend alles neu nummerieren:

awk -F . -v OFS=. '{if (NF) $1 = ++n; else n = 0; print}'
  • -F .: setzt das Feldtrennzeichen auf .1
  • -v OFS=.: Gleiches gilt für das Ausgabefeldtrennzeichen ( -F .steht für -v FS=.).
  • {...}: Keine Bedingung, daher wird der darin enthaltene Code {...}für jede Zeile ausgeführt
  • if (NF)Wenn die Anzahl der Felder größer als 0 ist. Mit FSSein .bedeutet dies , dass die aktuelle Zeile mindestens eines enthält .. Wir könnten es auch schaffen, if (length)nach nicht leeren Zeilen zu suchen.
  • $1 = ++n: Setzen Sie das erste Feld auf ein Inkrement n(zuerst 0, dann 1, dann 2 ...).
  • else n = 0: else (wenn NF == 0) setzt n auf 0 zurück.
  • print: Drucken Sie die (möglicherweise geänderte) Zeile.

1 Die Syntax lautet -F <extended-regular-expression>jedoch, wenn <extended-regular-expression>es sich um ein einzelnes Zeichen handelt, das nicht als regulärer Ausdruck (wobei .ein beliebiges Zeichen bedeutet), sondern als dieses Zeichen.

Stéphane Chazelas
quelle
Das macht den Job für mich :) Ich würde mich über eine Erklärung freuen, da ich Anfänger bin mitawk
Zanna
@ Zanna, siehe bearbeiten.
Stéphane Chazelas
6

Maximaler Overkill (und Komplexität! Und Fehler!) Können über das Perl-Modul Text :: Autoformat erzielt werden.

% < input                                                                    
1. foo
2. bar 100%
3. kittens
4. it is getting dark. there may be a grue
4. no seriously, it's getting dark
4. really, you should find a light or something.
4. are you even paying attention? helloooo
4. eat cake
5. unicorns
6. rainbows
% perl -MText::Autoformat -0777 -ple '$_=autoformat $_, { all => 1 }' < input
 1. foo
 2. bar 100%
 3. kittens
 4. it is getting dark. there may be a grue
 5. no seriously, it's getting dark
 6. really, you should find a light or something.
 7. are you even paying attention? helloooo
 8. eat cake
 9. unicorns
10. rainbows
% 

Die tatsächlichen Ergebnisse hängen von der Eingabe, der gewünschten Ausgabe, den übergebenen Optionen usw. ab.

Thrig
quelle
3

Mit einfachen Shell-Tools kann dies folgendermaßen erfolgen - es kann sogar noch einfacher sein:

paste -d . <(seq $(wc -l < new.list)) <(cut -d . -f 2- < new.list) 

Ich erkläre von außen nach innen:

paste -d . file1 file2 

Erstellt Ausgabezeilen aus den Dateien als Spalten. -d .setzt das Trennzeichen auf ., was hinter den neuen Zahlen endet.

commandA <(commandB)

Startet commandBund präsentiert die Ausgabe so, als ob sie eine Datei enthält, aus der gelesen werden soll commandA, wobei ein Dateiname als Argument erwartet wird. (Siehe Befehlsersetzung.)

Das zweite Argument paste, <(cut -d . -f 2- < new.list)gibt die zweite Spalte unverändert, jede Zeile aus dem zweiten Bereich auf, die Mittel nach dem Starten ..

Das erste Argument von pasteerstellt die neuen fortlaufenden Zeilennummern: Es zählt zuerst die Zeilen mit "wordcount lines": $(wc -l < new.list)und verwendet diese Nummer, um eine Folge von Zahlen von 1 bis zur Anzahl mit zu erstellen seq 7.

Volker Siegel
quelle
Oh, was die leeren Zeilen betrifft, stimmt das - ich habe mich auf den Testfall konzentriert. Aber in Bezug auf die .- es wird davon ausgegangen, dass es die .nach der Nummer gibt; Später hat .eine Linie keine Auswirkung, da die Linie dann nur am ersten Trennzeichen vor Feld 2 ( -f 2-dh Feld 2 und folgende) geschnitten wird - und keine weiteren Trennzeichen berührt, die nicht zum Schneiden verwendet werden. Es behandelt sie als Port des Feldes. In Bezug auf die durch Zeilenumbrüche getrennten Abschnitte csplit(auch von coreutils) scheint dies richtig zu sein; Bei leeren Zeilen grob aufteilen, oben beschrieben anwenden, Katzenergebnisse zusammen. Vielen Dank für den Hinweis!
Volker Siegel
@don_crissti Ich sehe - das Beispiel des OP ist nicht klar - Ich habe mir das Beispiel von @tring angesehen und wurde durch die aufeinander folgenden 4., 4. 4.Zeilen irregeführt . Mal sehen ...
Volker Siegel
+1 sehr schön für den ersten Block (wie im Beispiel) ... Ich muss damit spielen pasteund seqnoch mehr ... Ich kann es leicht in einem Editor machen, der die Zeilen zählt, aber ich möchte einen Weg, der nicht bricht, wenn Die Nummer, die ich einfügen möchte, ist NICHT die Zeilennummer> _ <
Zanna
1
Wenn Sie vim verwenden, können Sie den Befehl von vim in ausgewählten Zeilen ausführen - wenn es nur wenige sind. (Ein Skript muss damit umgehen, dass die Eingabe zweimal gelesen wird, wobei eine tmp-Datei oder so in einem Skript verwendet wird.) (Ich bin mir ziemlich sicher, dass dies ohne tmp-Datei möglich ist, aber das verwendet wirklich undurchsichtige Shell-Funktionen, vielleicht ...)
Volker Siegel
1
Es kann einfacher sein, dies direkt in vim zu tun, wenn Sie dies verwenden. Die rechteckige visuelle Blockauswahl wäre zum Beispiel nützlich: Natürlich gibt es auf vi.stackexchange.com gute Ratschläge zu vim-Fragen !
Volker Siegel
2

Zahlen entfernen: cut -d" " -f2- < list.txt > temp.txt

Zeile in temp.txt einfügen

Zahlen machen: cat -n temp.txt| sed -e 's/^[ ]*\([0-9]*\)[ \t]*\(.*\)/\1. \2/' > list.txt

Ipor Sircer
quelle
@don_crissti warum?
Ipor Sircer