Wie füge ich Dateinamen abwechselnde Zeichenfolgen hinzu und nummeriere sie paarweise neu?

7

Mit einem Hochdurchsatzmikroskop erzeugen wir Tausende von Bildern. Nehmen wir an, unser System nennt sie:

ome0001.tif
ome0002.tif
ome0003.tif
ome0004.tif
ome0005.tif
ome0006.tif
ome0007.tif
ome0008.tif
ome0009.tif
ome0010.tif
ome0011.tif
ome0012.tif
...

Wir möchten alternativ c1und c2in Bezug auf den numerischen Wert der Bilder einfügen und dann die ursprüngliche Nummerierung so ändern, dass jede aufeinanderfolgende c1und c2dieselbe inkrementelle Nummer enthält, wobei die numerische Reihenfolge (1, dann 2 ... dann 9, dann 10) berücksichtigt wird ) statt alphanumerischer Reihenfolge (1, dann 10, dann 2 ...).

In meinem Beispiel würde das ergeben:

ome0001c1.tif
ome0001c2.tif
ome0002c1.tif
ome0002c2.tif
ome0003c1.tif
ome0003c2.tif
ome0004c1.tif
ome0004c2.tif
ome0005c1.tif
ome0005c2.tif
ome0006c1.tif
ome0006c2.tif
...

Wir konnten dies nicht über die Terminal-Befehlszeile tun (Biologe spricht ...).

Jeder Vorschlag wäre sehr dankbar!

Philippe P.
quelle
Bitte klären Sie diesen Teil (1, then 2... then 9, then 10)Ihrer Aussage
George Udosen
Für mein Verständnis: ome0001.tif> ome0001c1.tifund ome0002.tif> ome0001c2.tifund so weiter? Neugierig auf den Mikroskoptyp.
Jacob Vlijm
Entschuldigung für die Verwirrung. So beantworten Sie alle Fragen: Der Bereich wird über MicroManager2 gesteuert. Diese seltsame Nummerierung ist auf die Einstellungen für den hohen Durchsatz zurückzuführen, die wir verwenden müssen, um den während der Erfassung verfügbaren Arbeitsspeicher nicht zu überschreiten (16Go)
Philippe P
Die beiden ersten Bilder (ome.0000.tif und ome.0001.tif) sollten in ome.0000c1.tif und ome.0000c2.tif umbenannt werden, die beiden folgenden (ome.0002.tif und ome.0003.tif) in ome.0001c1.tif und ome.0001c2.tif usw. umbenannt werden. Danke fürs Lesen.
Philippe P
Ich bin mir nicht sicher, ob mir das klar ist ... Woher stammt die doppelte Erwähnung für jeden? Sie sind in der Quelle des Beispiels nicht doppelt so hoch.
Jacob Vlijm

Antworten:

11

rename Führt eine Massenumbenennung durch und kann die von Ihnen benötigte Arithmetik ausführen.

Verschiedene GNU / Linux-Distributionen haben unterschiedliche Befehle renamemit unterschiedlicher Syntax und unterschiedlichen Funktionen. In Debian, Ubuntu und einigen anderen Betriebssystemen renameist das Perl-Umbenennungsprogramm prename. Es ist sehr gut für diese Aufgabe geeignet.

Zuerst empfehle ich zu sagen rename, dass Sie nur zeigen sollen , was es tun würde, indem Sie es mit der -nFlagge ausführen:

rename -n 's/\d+/sprintf("%04dc%d", int(($& - 1) \/ 2) + 1, 2 - $& % 2)/e' ome????.tif

Das sollte dir zeigen:

rename(ome0001.tif, ome0001c1.tif)
rename(ome0002.tif, ome0001c2.tif)
rename(ome0003.tif, ome0002c1.tif)
rename(ome0004.tif, ome0002c2.tif)
rename(ome0005.tif, ome0003c1.tif)
rename(ome0006.tif, ome0003c2.tif)
rename(ome0007.tif, ome0004c1.tif)
rename(ome0008.tif, ome0004c2.tif)
rename(ome0009.tif, ome0005c1.tif)
rename(ome0010.tif, ome0005c2.tif)
rename(ome0011.tif, ome0006c1.tif)
rename(ome0012.tif, ome0006c2.tif)

Angenommen, Sie möchten dies tun, führen Sie es ohne die -nFlagge aus (dh entfernen Sie es einfach -n):

rename 's/\d+/sprintf("%04dc%d", int(($& - 1) \/ 2) + 1, 2 - $& % 2)/e' ome????.tif

Dieser Befehl ist etwas hässlich - obwohl immer noch eleganter als die Verwendung einer Schleife in Ihrer Shell - und vielleicht wird jemand mit mehr Perl-Erfahrung als ich eine schönere Lösung veröffentlichen.

Ich empfehle Olis Tutorial Bulk-Umbenennungsdateien in Ubuntu. Die kürzeste Einführung in den Umbenennungsbefehl für eine sanfte Einführung in das Schreiben von renameBefehlen.


So renamefunktioniert dieser spezielle Befehl:

Folgendes s/\d+/sprintf("%04dc%d", int(($& - 1) \/ 2) + 1, 2 - $& % 2)/ebewirkt:

  • Das führende sMittel, um nach zu ersetzendem Text zu suchen.
  • Der reguläre Ausdruck /\d+/entspricht einer oder mehreren ( +) Ziffern ( \d). Dies entspricht Ihre 0001, 0002und so weiter.
  • Der Befehl sprintf("%04dc%d", int(($& - 1) / 2) + 1, 2 - $& % 2)wird erstellt. $&repräsentiert das Match. /Normalerweise wird der Ersetzungstext beendet, es wird jedoch \/ein Literal erstellt /(dies ist eine Unterteilung, wie unten beschrieben).
  • Das nachfolgende /eMittel, um den Ersatztext als Code auszuwerten .
    (Versuchen Sie es mit nur /statt /eam Ende, aber achten Sie darauf, die -nFlagge zu behalten ! )

Somit sind Ihre neuen Dateinamen die Rückgabewerte von sprintf("%04dc%d", int(($& - 1) \/ 2) + 1, 2 - $& % 2). Also, was ist dort los?

  • sprintfGibt formatierten Text zurück. Das erste Argument ist die Formatzeichenfolge, in die Werte eingefügt werden. %04dverbraucht das erste Argument und formatiert es als Ganzzahl mit einer Breite von 4 Zeichen. %4dwürde führende Nullen weglassen, daher %04dwird benötigt. Von niemandem abgedeckt zu werden %, cbedeutet nur einen wörtlichen Buchstaben c. Dann %dverbraucht das zweite Argument und formatiert sie als eine Ganzzahl (mit Standardformatierung).
  • int(($& - 1) / 2) + 11 subtrahiert von der Anzahl von den ursprünglichen Dateinamen extrahierten, teilt es durch 2, kürzt den Bruchteil ( das inttut), dann fügt 1. Die arithmetische sendet 0001und 0002zu 0001, 0003und 0004zu 0002, 0005und 0006nach 0003, und so weiter.
  • 2 - $& % 2Der Rest der Division der aus dem ursprünglichen Dateinamen extrahierten Zahl durch 2 ( %macht das), was 0 ist, wenn es gerade ist, und 1, wenn es ungerade ist. Es subtrahiert dann das von 2. Diese Arithmetik sendet 0001an 1, 0002an 2, 0003an 1, 0004an 2und so weiter.

Schließlich ome????.tifhandelt es sich um einen Glob , den Ihre Shell zu einer Liste aller Dateinamen im aktuellen Verzeichnis erweitert, die mit genau vier beliebigen Zeichen beginnen, mit diesen omeenden .tifund genau vier Zeichen dazwischen haben.

Diese Liste wird an den renameBefehl übergeben, der versucht, -nalle Dateien, deren Namen eine Übereinstimmung mit dem Muster enthalten , umzubenennen (oder mitzuteilen, wie er umbenannt werden soll) \d+.

  • Aus Ihrer Beschreibung geht hervor, dass Sie keine Dateien in diesem Verzeichnis haben, die so benannt sind, aber einige der Zeichen sind keine Ziffern.
  • Wenn ihr aber dann können Sie ersetzen \d+mit \d{4}in den Ausdruck in den oben gezeigten Befehle erscheinen regelmäßig, um sicherzustellen , dass sie nicht umbenannt werden, oder einfach nur die Ausgabe mit produziert prüfen -nsorgfältig, was Sie ohnehin tun sollten.
  • Ich habe geschrieben, \d+anstatt \d{4}zu vermeiden, dass der Befehl komplexer als nötig wird. (Es gibt viele verschiedene Möglichkeiten, es zu schreiben.)
Eliah Kagan
quelle
2
Vielen Dank Eliah! Nicht nur Ihre Lösung (Umbenennen von / \ d + / sprintf ("% 04dc% d", int (($ & - 1) \ / 2) + 1, 2 - $ &% 2) / e 'ome ?? ??. tif) funktionieren perfekt, aber auch sehr schnell. Wir haben 18000 Dateien in wenigen Sekunden umbenannt. Danke auch an Zanna und allgemeiner an diese Community. Wir werden uns Olis Tutorial Bulk-Umbenennungsdateien in Ubuntu ansehen, um hoffentlich von nun an in der Lage zu sein, Dinge selbst umzubenennen. Philippe.
Philippe P
6

Ich habe eine Methode verwendet, um dies in Bash zu tun, basierend auf der Idee, dass wenn die Zahl im Dateinamen gerade ist, wir sie durch zwei teilen und hinzufügen wollen c2, und wenn die Zahl ungerade ist, wollen wir eine hinzufügen und dann durch zwei teilen und hinzufügen c1. Die getrennte Behandlung von Dateien mit ungeraden und geraden Nummern auf diese Weise ist viel länger als die Bash-Methode von Eliah Kagan, und ich stimme zu, dass die Verwendung renamewie in dieser anderen Antwort von Eliah Kagan der kluge Weg ist, aber diese Art von Ansatz kann in einigen Situationen nützlich sein.

Ein kleiner Vorteil gegenüber der Verwendung eines Bereichs wie {0000...0012}ist, dass nur versucht wird, vorhandene Dateien zu bearbeiten, sodass es sich nicht beschwert, wenn die Dateien nicht vorhanden sind. Sie erhalten jedoch immer noch unlogisch nummerierte Dateien, wenn Lücken vorhanden sind. Im zweiten Teil meiner Antwort finden Sie einen Weg, der dieses Problem nicht hat.

In einer Zeile sieht es schrecklich aus:

for f in *; do g="${f%.tif}"; h="${g#ome}"; if [[ $(bc <<< "$h%2") == 0 ]]; then printf -v new "ome%04dc2.tif" "$(bc <<< "$h/2")" ; echo mv -vn -- "$f" "$new"; else printf -v new "ome%04dc1.tif" "$(bc <<< "($h+1)/2")"; echo mv -vn -- "$f" "$new"; fi; done

Hier ist das als Skript:

#!/bin/bash

for f in *; do 
    g="${f%.tif}"
    h="${g#ome}"

    if [[ $(bc <<< "$h%2") == 0 ]]; then 
         printf -v new "ome%04dc2.tif" "$(bc <<< "$h/2")"
         echo mv -vn -- "$f" "$new"
    else
         printf -v new "ome%04dc1.tif" "$(bc <<< "($h+1)/2")"
         echo mv -vn -- "$f" "$new"
    fi
done

Die echoes vor den mvAnweisungen dienen nur zum Testen. Entfernen Sie sie, um die Dateien tatsächlich umzubenennen, wenn Sie sehen, was Sie tun möchten.

Anmerkungen

g="${f%.tif}"     # strip off the extension
h="${g#ome}"      # strip off the letters... now h contains the number

Testen Sie, ob die Zahl gerade ist (dh das Teilen durch 2 ergibt keinen Rest)

if [[ $(bc <<< "$h%2") == 0 ]]; then 

Ich habe verwendet bc, das nicht versucht, Zahlen mit führenden Nullen als Oktalzahlen zu behandeln, obwohl ich die Nullen einfach mit einer anderen Zeichenfolgenerweiterung hätte entfernen können, da ich die Zahlen sowieso mit fester Breite formatieren werde.

Als nächstes konstruieren Sie den neuen Namen für die geradzahligen Dateien:

printf -v new "ome%04dc2.tif" "$(bc <<< "$h/2")"

%04dwird durch die bc <<< "$h/2"im 4-stelligen Format ausgegebene Zahl ersetzt , die mit führenden Nullen aufgefüllt ist (also 0 = 0000, 10 = 0010 usw.).

Benennen Sie die Originaldatei mit dem erstellten neuen Namen um

echo mv -vn -- "$f" "$new"

-vfür ausführlich, -nfür No-Clobber (überschreiben Sie keine Dateien, die bereits den beabsichtigten Namen haben, falls vorhanden) und --um Fehler durch Dateinamen zu vermeiden, die mit beginnen -(aber da der Rest meines Skripts erwartet, dass Ihre Dateien benannt werden, ome[somenumber].tifdenke ich, ich ' Ich füge es nur aus Gewohnheit hinzu.


Die Lücken füllen

Nach einigem Basteln und mehr Hilfe von Eliah Kagan habe ich einen prägnanteren Weg gefunden, um die Namen zu erhöhen, was den Vorteil hat, die Lücken zu füllen. Das Problem bei dieser Methode ist, dass nur eine Zahl inkrementiert, eine einfache Arithmetik für diese Zahl durchgeführt, sie formatiert und in den Dateinamen eingefügt wird. Bash denkt (sozusagen) "ok, hier ist die nächste Datei, ich gebe ihr den nächsten Namen", ohne auf den ursprünglichen Dateinamen zu achten. Dies bedeutet, dass neue Namen erstellt werden, die sich nicht auf die alten Namen beziehen , sodass Sie die Umbenennung nicht logisch rückgängig machen können und die Dateien nur dann in der richtigen Reihenfolge umbenannt werden, wenn ihre Namen bereits so sind, dass sie verarbeitet werden in der richtigen Reihenfolge. Dies ist in Ihrem Beispiel der Fall, das mit Nullen aufgefüllte Zahlen mit fester Breite enthält. Wenn Sie jedoch Dateien mit dem Namen "z.28, 10, 45Würden sie in der Reihenfolge verarbeitet werden 10, 2, 45, 8, das ist wahrscheinlich nicht das, was Sie wollen.

Wenn dieser Ansatz angesichts all dessen für Sie geeignet ist, können Sie dies folgendermaßen tun:

i=0; for f in ome????.tif; do ((i++)); printf -v new "ome%04dc%d.tif" $(((i+1)/2)) $(((i+1)%2+1)); echo mv -vn "$f" "$new"; done 

oder

#!/bin/bash
i=0

for f in ome????.tif; do 
    ((i++))
    printf -v new "ome%04dc%d.tif" $(((i+1)/2)) $(((i+1)%2+1))
    echo mv -vn "$f" "$new"
done 

Anmerkungen

  • i=0 eine Variable initiieren
  • ((i++)) Erhöhen Sie die Variable um eins (dies zählt die Iterationen der Schleife).
  • printf -v new Fügen Sie die folgende Anweisung in die Variable ein new
  • "ome%04dc%d.tif" der neue Dateiname mit den Zahlenformaten, die durch die nachfolgend genannten Zahlen ersetzt werden
  • $(((i+1)/2)) Die Häufigkeit, mit der die Schleife ausgeführt wurde, plus eins, geteilt durch 2

    Dies funktioniert auf der Grundlage, dass Bash nur eine ganzzahlige Division durchführt. Wenn wir also eine ungerade Zahl durch 2 teilen, erhalten wir dieselbe Antwort wie beim Teilen der vorhergehenden geraden Zahl durch 2:

    $ echo $((2/2))
    1
    $ echo $((3/2))
    1
  • $(((i+1)%2+1))Der Rest nach dem Teilen der Häufigkeit, mit der die Schleife ausgeführt wurde, plus eins durch zwei plus eins. Dies bedeutet, wenn die Nummer der Iteration ungerade ist (z. B. der erste Lauf), ist die Ausgabe 1, und wenn die Nummer der Iteration gerade ist (z. B. der zweite Lauf), ist die Ausgabe 2, geben c1oderc2
  • Früher habe ich , i=0weil dann an jedem beliebigen Punkt während des Laufes, der Wert iwird die Anzahl der Male die Schleife ausgeführt wurde , die für die Fehlersuche nützlich sein könnte , wie es auch die Ordnungsnummer der Datei verarbeitet werden wird ( das heißt , wenn i=69wir verarbeiten die 69. Datei). Wir können die Arithmetik jedoch vereinfachen, indem wir mit einem anderen beginnen i, zum Beispiel:

    i=2; for f in ome????.tif; do printf -v new "ome%04dc%d.tif" $((i/2)) $((i%2+1)); echo mv -vn "$f" "$new"; ((i++)); done 

    Es gibt viele Möglichkeiten, dies zu tun :)

  • echo Nur zum Testen - entfernen Sie, wenn Sie das gewünschte Ergebnis sehen.

Hier ist ein Beispiel für diese Methode:

$ ls
ome0002.tif  ome0004.tif  ome0007.tif  ome0009.tif  ome0010.tif  ome0012.tif  ome0019.tif  ome0100.tif  ome2996.tif
$ i=0; for f in ome????.tif; do ((i++)); printf -v new "ome%04dc%d.tif" $(((i+1)/2)) $(((i+1)%2+1)); echo mv -vn "$f" "$new"; done 
mv -vn ome0002.tif ome0001c1.tif
mv -vn ome0004.tif ome0001c2.tif
mv -vn ome0007.tif ome0002c1.tif
mv -vn ome0009.tif ome0002c2.tif
mv -vn ome0010.tif ome0003c1.tif
mv -vn ome0012.tif ome0003c2.tif
mv -vn ome0019.tif ome0004c1.tif
mv -vn ome0100.tif ome0004c2.tif
mv -vn ome2996.tif ome0005c1.tif
Zanna
quelle
5

Sie können dafür eine Shell-Schleife schreiben, wenn Sie wirklich wollen.

Wenn Sie einen Befehl möchten, der auf Systemen funktioniert, die keinen haben renameoder dessen renameBefehl nicht ist prename, oder wenn Sie möchten, dass er von Personen, die Bash, aber nicht Perl kennen, etwas besser verstanden wird, oder aus einem anderen Grund möchten Sie dies als implementieren Eine Schleife in Ihrer Shell, die den mvBefehl aufruft , können Sie. (Ansonsten empfehle ich die renameMethode in meiner anderen Antwort dazu.)

Ubuntu hat Bash 4, bei dem die Klammererweiterung führende Nullen beibehält und daher auf {0001..0012}erweitert 0001 0002 0003 0004 0005 0006 0007 0008 0009 0010 0011 0012. Dies ist nur in Situationen angebracht, in denen Sie tatsächlich alle Dateien in einem Bereich haben. Basierend auf der Problembeschreibung in Ihrer Frage scheint dies der Fall zu sein. Andernfalls würde es immer noch funktionieren, aber Sie würden eine ganze Reihe von Fehlermeldungen für die Lücken erhalten, die es schwierig machen würden, andere Fehler zu bemerken, die tatsächlich wichtig sein könnten. Ersetzen Sie 0012durch Ihre tatsächliche Obergrenze.

Da dieser Befehl bereits echoangezeigt wird mv, werden nur die mvBefehle gedruckt, die ausgeführt werden würden, ohne sie tatsächlich auszuführen: 1

for i in {0001..0012}; do echo mv -n "ome$i.tif" "$(printf 'ome%04dc%d.tif' "$(((10#$i - 1) / 2 + 1))" "$((2 - 10#$i % 2))")"; done

Dies verwendet dieselbe Grundidee wie in meiner renameAntwort , sowohl was die Arithmetik betrifft als auch was die Bedeutung %04dund %ddie Formatzeichenfolgen betrifft. Dies könnte getan werden {1..12}, aber dann wäre es noch komplizierter, da zwei $( )Befehlsersetzungen mit printfanstelle von nur einer erforderlich wären .

Bitte denken Sie daran, dass das -nIn rename -nnicht dasselbe bedeutet wie das -nIn mv -n. Beim Ausführen werden rename -nDateien überhaupt nicht verschoben. Durch das Ausführen werden mv -nDateien verschoben, es sei denn, es müsste eine vorhandene Datei am Ziel überschrieben werden, um dies zu tun. Dies mv -ngibt Ihnen die Sicherheit, die Sie automatisch erhalten rename(es sei denn, Sie führen rename -f). Entfernen Sie denecho folgenden Befehl, damit der oben gezeigte Befehl Dateien tatsächlich verschiebt :

for i in {0001..0012}; do mv -n "ome$i.tif" "$(printf 'ome%04dc%d.tif' "$(((10#$i - 1) / 2 + 1))" "$((2 - 10#$i % 2))")"; done

So funktioniert diese Bash-Schleife:

for i in {0001..0012}führt die Befehle nach dozwölf Mal aus, iwobei jedes Mal ein anderer Wert angenommen wird. Diese Schleife hat zuvor nur einen solchen Befehl done, der das Ende des Schleifenkörpers kennzeichnet. (Wenn die Steuerung dies trifft, donegeht sie konzeptionell zur nächsten Iteration der Schleife über, mit idem nächsten Wert.) Dieser eine Befehl lautet:

mv -n "ome$i.tif" "$(printf 'ome%04dc%d.tif' "$(((10#$i - 1) / 2 + 1))" "$((2 - 10#$i % 2))")"
  • $ierscheint einige Male in der Schleife. Dies ist eine Parametererweiterung und wird durch den aktuellen Wert von ersetzt i.
  • ome$i.tifexpandiert nach einem der ome0001.tif, ome0002.tif, ome0003.tif, usw., je nachdem , welcher Wert ihat. Das Einfügen der führenden Nullen durch Schreiben {0001..0012}anstelle von {1..12}macht dieses Argument mv, das den alten Namen der Datei angibt, einfach zu schreiben.
  • $( )ist Befehlsersetzung . Darin führe ich einen printfBefehl aus, der den gewünschten Text des zweiten Arguments ausgibt mv, der den neuen Namen der Datei angibt. Das Ganze wird in geschlossene " "Anführungszeichen so unerwünschte Erweiterungen - insbesondere Globbing und Wort Spaltung vermieden --are. In Befehl Substitution, $(...)ersetzt wird durch Ausgabe durch die Ausführung des Befehls erzeugt ....

Der Befehl, der den Zieldateinamen ausgibt, lautet wie folgt:

printf 'ome%04dc%d.tif' "$(((10#$i - 1) / 2 + 1))" "$((2 - 10#$i % 2))"
  • %04dund %dhaben die gleiche Bedeutung wie in Perls sprintfFunktion, die von verwendet wurderename .
  • Jedes der beiden Argumente verwendet eine arithmetische Erweiterung , um Berechnungen durchzuführen. Das Ganze $((...))wird durch das Ergebnis der Auswertung des Ausdrucks ersetzt ....
  • 10#$inimmt den Wert von i( $i) und behandelt ihn als Basis-10- Zahl ( 10#). Dies ist hier notwendig, da Bash Zahlen mit führenden 0s als Oktal behandelt . 2 Im Inneren können $(( ))Sie normalerweise nur den Namen einer Variablen schreiben, um damit zu rechnen (dh ianstelle von $i). Dies $iwird jedoch auch unterstützt und 10#$iist einer der wenigen Fälle, in denen er im Inneren benötigt wird $(( )).
  • Die Arithmetik hier ist die gleiche wie die vonrename , außer dass die Division in Bash automatisch eine ganzzahlige Division ist - sie schneidet automatisch den Bruchteil ab -, sodass nichts verwendet werden muss, was der Perl- intFunktion entspricht.

1 Ein Fehler in der Syntaxhervorhebung, die für Bash-Code auf dieser Site verwendet wird, führt derzeit dazu, dass alles nach dem #ausgegraut wird. Ein nicht zitierter Code #startet normalerweise einen Kommentar in Bash, in diesem Fall jedoch nicht . Sie brauchen sich darüber keine Sorgen zu machen - Ihr Bash-Interpreter macht nicht den gleichen Fehler.

2 Perl behandelt Zahlen mit führenden 0s auch als Oktal. Doch mit renameder Anpaßvariable $&ist eigentlich ein String - das ist Textverarbeitung, nachdem alle. Perl erlaubt die Verwendung von Zeichenfolgen, als wären sie Zahlen, und wenn dies der Fall ist, werden führende 0s in der Zeichenfolge nicht dazu führen, dass sie als Oktalzahl behandelt werden! Wenn man den renameWeg mit dieser längeren, schwierigeren und weniger robusten Shell-Loop-Methode vergleicht, fällt eine häufige Beobachtung ein: Perl ist seltsam, aber es erledigt den Job.

Eliah Kagan
quelle