Ich möchte eine Reihe von Verzeichnissen durchgehen und alle Dateien, die auf _test.rb enden, in _spec.rb umbenennen. Es ist etwas, das ich nie ganz herausgefunden habe, wie man mit Bash umgeht, also dachte ich diesmal, ich würde mich etwas anstrengen, um es festzunageln. Ich bin bisher jedoch zu kurz gekommen, meine größte Anstrengung ist:
find spec -name "*_test.rb" -exec echo mv {} `echo {} | sed s/test/spec/` \;
NB: Nach der Ausführung gibt es ein zusätzliches Echo, sodass der Befehl gedruckt und nicht ausgeführt wird, während ich ihn teste.
Wenn ich es ausführe, lautet die Ausgabe für jeden übereinstimmenden Dateinamen:
mv original original
dh die Substitution durch sed ist verloren gegangen. Was ist der Trick?
Antworten:
Dies geschieht, weil
sed
die Zeichenfolge{}
als Eingabe empfangen wird. Dies kann überprüft werden mit:die
foofoo
rekursiv für jede Datei im Verzeichnis druckt . Der Grund für dieses Verhalten ist, dass die Pipeline von der Shell einmal ausgeführt wird, wenn der gesamte Befehl erweitert wird.Es gibt keine Möglichkeit, die
sed
Pipeline so zu zitieren , dassfind
sie für jede Datei ausgeführt wird, dafind
keine Befehle über die Shell ausgeführt werden und keine Vorstellung von Pipelines oder Backquotes besteht. Das GNU findutils-Handbuch erklärt, wie eine ähnliche Aufgabe ausgeführt wird, indem die Pipeline in ein separates Shell-Skript eingefügt wird:(Es mag eine perverse Art der Verwendung
sh -c
und eine Menge Anführungszeichen geben, um all dies in einem Befehl zu erledigen, aber ich werde es nicht versuchen.)quelle
Um es auf eine Weise zu lösen, die dem ursprünglichen Problem am nächsten kommt, würde wahrscheinlich die Option xargs "args per command line" verwendet:
Es findet die Dateien im aktuellen Arbeitsverzeichnis rekursiv, gibt den ursprünglichen Dateinamen (
p
) und dann einen geänderten Namen (s/test/spec/
) wieder und füttert siemv
paarweise (xargs -n2
). Beachten Sie, dass in diesem Fall der Pfad selbst keine Zeichenfolge enthalten solltetest
.quelle
-z
Option zusammen mit Funden-print0
und Xargs verwenden-0
:find -name '*._test.rb' -print0 | sed -ze "p;s/test/spec/" | xargs -0 -n2 mv
Vielleicht möchten Sie einen anderen Weg in Betracht ziehen
quelle
echo $file | sed s/_test.rb$/_spec.rb/
; Fertig ist ein Einzeiler, nicht wahr?for
wird sie in separate Wörter aufteilen. Sie können es zum Laufen bringen, indem Sie die for-Schleife anweisen, nur in Zeilenumbrüchen zu teilen. Beispiele finden Sie unter cyberciti.biz/tips/handling-filenames-with-spaces-in-bash.html .-exec
Option von find zu verwenden.Ich finde das kürzer
quelle
find . -name '*_test.rb' -exec bash -c 'echo mv $0 ${0/test.rb/spec.rb}' {} \;
funktioniert Wie würdefind . -name '*_test.rb' -exec bash -c 'echo mv $1 ${1/test.rb/spec.rb}' iAmArgumentZero {} \;
Sie können es ohne sed tun, wenn Sie wollen:
${var%%suffix}
Streifensuffix
vom Wert vonvar
.oder, um es mit sed zu tun:
quelle
sed
wie in der akzeptierten Antwort).for i in... ; do ... ; done
, welche Befehle über die Shell ausführt und nicht verstehen Graviszeichen.Sie erwähnen, dass Sie
bash
als Shell verwenden. In diesem Fall benötigen Sie nicht wirklichfind
undsed
um die Batch-Umbenennung zu erreichen, nach der Sie suchen ...Angenommen, Sie verwenden
bash
als Shell:... und vorausgesetzt, Sie haben die sogenannte
globstar
Shell-Option aktiviert :... und schließlich vorausgesetzt, Sie haben das
rename
Dienstprogramm installiert (imutil-linux-ng
Paket enthalten)... dann können Sie die Batch-Umbenennung in einem Bash-Einzeiler wie folgt erreichen:
(Die
globstar
Shell-Option stellt sicher, dass bash alle übereinstimmenden*_test.rb
Dateien findet, unabhängig davon, wie tief sie in der Verzeichnishierarchie verschachtelt sind. Verwendenhelp shopt
Sie diese Option, um herauszufinden, wie die Option festgelegt wird.)quelle
Der einfachste Weg :
Der schnellste Weg (vorausgesetzt, Sie haben 4 Prozessoren):
Wenn Sie eine große Anzahl von Dateien verarbeiten müssen, kann es sein, dass die Liste der an xargs weitergeleiteten Dateinamen dazu führt, dass die resultierende Befehlszeile die maximal zulässige Länge überschreitet.
Sie können das Limit Ihres Systems mit überprüfen
getconf ARG_MAX
Auf den meisten Linux-Systemen können Sie verwenden
free -b
odercat /proc/meminfo
ermitteln, mit wie viel RAM Sie arbeiten müssen. Andernfalls verwenden Sietop
oder Ihre Systemaktivitätsüberwachungs-App.Ein sicherer Weg (vorausgesetzt, Sie haben 1000000 Byte RAM zum Arbeiten):
quelle
Folgendes hat bei mir funktioniert, wenn die Dateinamen Leerzeichen enthielten. Im folgenden Beispiel werden alle .dar-Dateien rekursiv in .zip-Dateien umbenannt:
quelle
Dafür brauchst du nicht
sed
. Sie können mit einerwhile
Schleife, die mit dem Ergebnisfind
einer Prozessersetzung gespeist wird, perfekt allein sein .Wenn Sie also einen
find
Ausdruck haben, der die benötigten Dateien auswählt, verwenden Sie die folgende Syntax:Dadurch werden
find
alle Dateien abgelegt und umbenannt, wobei die Zeichenfolge_test.rb
vom Ende entfernt und angehängt wird_spec.rb
.Für diesen Schritt verwenden wir die Shell-Parametererweiterung, bei
${var%string}
der das kürzeste übereinstimmende Muster "Zeichenfolge" entfernt wird$var
.Siehe ein Beispiel:
quelle
while IFS= read -r file; do mv $file ${file%.gz}; done < <(find -type f -name "*.gz")
find .... -exec mv ...
. Seien Sie auch vorsichtig,$file
da es fehlschlägt, wenn es Leerzeichen enthält. Verwenden Sie besser Anführungszeichen"$file"
.wenn Sie Ruby haben (1.9+)
quelle
In der Antwort von ramtam, die mir gefällt, funktioniert der Suchabschnitt in Ordnung, der Rest jedoch nicht, wenn der Pfad Leerzeichen enthält. Ich bin mit sed nicht allzu vertraut, aber ich konnte diese Antwort ändern in:
Ich brauchte wirklich eine solche Änderung, weil in meinem Anwendungsfall der letzte Befehl eher so aussieht
quelle
Ich habe nicht das Herz, alles noch einmal zu machen, aber ich habe dies als Antwort auf Commandline Find Sed Exec geschrieben . Dort wollte der Fragesteller wissen, wie ein ganzer Baum, möglicherweise mit Ausnahme eines oder zweier Verzeichnisse, verschoben und alle Dateien und Verzeichnisse mit der Zeichenfolge "OLD" in "NEW" umbenannt werden .
Neben der Beschreibung des wie mit mühsamer Ausführlichkeit unten kann dieses Verfahren auch eindeutig sein, dass es enthält eingebaute in debuggen. Grundsätzlich werden nur die Befehle kompiliert und in einer Variablen gespeichert, von denen es glaubt, dass sie ausgeführt werden sollen, um die angeforderte Arbeit auszuführen.
Außerdem werden Schleifen explizit so weit wie möglich vermieden . Abgesehen von der
sed
rekursiven Suche nach mehr als einer Übereinstimmung des Musters gibt es meines Wissens keine andere Rekursion.Und zuletzt ist dies vollständig
null
abgegrenzt - es wird kein Zeichen in einem Dateinamen außer dem ausgelöstnull
. Ich denke nicht, dass du das haben solltest.Das ist übrigens WIRKLICH schnell. Aussehen:
HINWEIS: Für die oben genannten
function
Schritte sind wahrscheinlichGNU
Versionen vonsed
und erforderlichfind
, um die Aufrufefind printf
undsed -z -e
und ordnungsgemäß zu verarbeiten:;recursive regex test;t
. Wenn Ihnen diese nicht zur Verfügung stehen, kann die Funktionalität wahrscheinlich mit ein paar geringfügigen Anpassungen dupliziert werden.Dies sollte alles tun, was Sie von Anfang bis Ende mit sehr wenig Aufwand wollten. Ich habe
fork
mitsed
, aber ich war auch einige praktizierendesed
rekursive Verzweigung Techniken ist also, warum ich hier bin. Es ist so, als würde man in einer Friseurschule einen Rabatt-Haarschnitt bekommen, denke ich. Hier ist der Workflow:rm -rf ${UNNECESSARY}
./app
dies unerwünscht sein könnte. Löschen Sie es oder verschieben Sie es vorher an eine andere Stelle, oder Sie können alternativ eine\( -path PATTERN -exec rm -rf \{\} \)
Routine einbauen,find
um dies programmgesteuert zu tun, aber das gehört Ihnen._mvnfind "${@}"
${sh_io}
ist besonders wichtig, da es die Rückgabe von der Funktion speichert.${sed_sep}
kommt in einer knappen Sekunde; Dies ist eine beliebige Zeichenfolge, die verwendet wird, um aufsed
die Rekursion in der Funktion zu verweisen . Wenn${sed_sep}
auf einen Wert gesetzt ist, der möglicherweise in einem Ihrer Pfad- oder Dateinamen gefunden werden kann, auf die reagiert wird ... nun, lassen Sie es einfach nicht zu.mv -n $1 $2
-noclobber
Option fürmv
; Wie geschrieben, wird diese Funktion nicht dort platzieren,${SRC_DIR}
wo${TGT_DIR}
bereits eine vorhanden ist.read -R SED <<HEREDOC
find . -name ${OLD} -printf
find
Prozess. Mitfind
uns nur für etwas suchen , die Umbenennung braucht , weil wir bereits alle der tat Ort-zu-Ort -mv
Operationen mit der ersten Befehl der Funktion. Anstatt eine direkte Aktion mit beispielsweisefind
einemexec
Aufruf auszuführen, verwenden wir sie stattdessen, um die Befehlszeile dynamisch mit aufzubauen-printf
.%dir-depth :tab: 'mv '%path-to-${SRC}' '${sed_sep}'%path-again :null delimiter:'
find
wir die benötigten Dateien gefunden haben, wird sie direkt erstellt und (der größte Teil ) des Befehls ausgedruckt, den wir zum Verarbeiten Ihrer Umbenennung benötigen. Das%dir-depth
am Anfang jeder Zeile angeheftete Element stellt sicher, dass nicht versucht wird, eine Datei oder ein Verzeichnis im Baum mit einem übergeordneten Objekt umzubenennen, das noch umbenannt werden muss.find
verwendet alle Arten von Optimierungstechniken, um Ihren Dateisystembaum zu durchsuchen, und es ist nicht sicher, ob die benötigten Daten in einer betriebssicheren Reihenfolge zurückgegeben werden. Deshalb werden wir als nächstes ...sort -general-numerical -zero-delimited
find
Ausgabe danach,%directory-depth
sodass die Pfade, die in Bezug auf $ {SRC} am nächsten liegen, zuerst bearbeitet werden. Dies vermeidet mögliche Fehler beimmv
Einfügen von Dateien in nicht vorhandene Speicherorte und minimiert die Notwendigkeit einer rekursiven Schleife. ( Tatsächlich kann es schwierig sein, überhaupt eine Schleife zu finden. )sed -ex :rcrs;srch|(save${sep}*til)${OLD}|\saved${SUBSTNEW}|;til ${OLD=0}
%Path
für jede Zeichenfolge gedruckt wird, falls sie mehr als einen $ {OLD} -Wert enthält, der möglicherweise ersetzt werden muss. Alle anderen Lösungen, die ich mir vorgestellt habe, beinhalteten einen zweitensed
Prozess, und obwohl eine kurze Schleife möglicherweise nicht wünschenswert ist, schlägt sie sicherlich das Laichen und Verzweigen eines gesamten Prozesses.sed
wird hier nach $ {sed_sep} gesucht und dann, nachdem es gefunden wurde, es und alle Zeichen, auf die es trifft, gespeichert, bis es $ {OLD} findet, das es dann durch $ {NEW} ersetzt. Es kehrt dann zu $ {sed_sep} zurück und sucht erneut nach $ {OLD}, falls es mehr als einmal in der Zeichenfolge vorkommt. Wenn es nicht gefunden wird, druckt es die geänderte Zeichenfolgestdout
(an die es dann als nächstes wieder abfängt) und beendet die Schleife.mv
Befehlszeichenfolge, die natürlich $ {OLD} enthalten muss, diese enthält und die zweite Hälfte so oft geändert wird, wie zum Löschen der Zeichenfolge erforderlich ist $ {OLD} Name ausmv
dem Zielpfad.sed -ex...-ex search|%dir_depth(save*)${sed_sep}|(only_saved)|out
-exec
Anrufe hier erfolgen ohne Sekundefork
. Wie wir gesehen haben, ändern wir im ersten Schritt denmv
Befehl, der vom Funktionsbefehlfind
's bereitgestellt wird ,-printf
nach Bedarf, um alle Verweise von $ {OLD} auf $ {NEW} ordnungsgemäß zu ändern. Dazu mussten wir jedoch einige verwenden beliebige Referenzpunkte, die nicht in die endgültige Ausgabe aufgenommen werden sollten. Sobaldsed
alles erledigt ist, weisen wir es an, seine Referenzpunkte aus dem Haltepuffer zu löschen, bevor es weitergegeben wird.Und jetzt sind wir wieder da
read
erhält einen Befehl, der folgendermaßen aussieht:Es wird
read
es${msg}
als${sh_io}
was nach Belieben außerhalb der Funktion untersucht werden kann.Cool.
-Mike
quelle
Ich konnte Dateinamen mit Leerzeichen verarbeiten, indem ich den von onitake vorgeschlagenen Beispielen folgte.
Dies wird nicht unterbrochen, wenn der Pfad Leerzeichen oder die Zeichenfolge enthält
test
:quelle
Dies ist ein Beispiel, das in allen Fällen funktionieren sollte. Funktioniert rekursiv, benötigt nur Shell und unterstützt Dateinamen mit Leerzeichen.
quelle
quelle
Ihre Frage scheint sich auf sed zu beziehen, aber um Ihr Ziel der rekursiven Umbenennung zu erreichen, würde ich Folgendes vorschlagen, schamlos aus einer anderen Antwort herausgerissen, die ich hier gegeben habe: rekursive Umbenennung in bash
quelle
sed
, ohne zu entkommen,()
wenn Sie die-r
Option nicht festlegen ?Sicherere Umbenennung mit find utils und sed Typ für reguläre Ausdrücke:
Entfernen Sie die Erweiterung ".txt.txt" wie folgt:
Wenn Sie das + anstelle von verwenden; Um im Batch-Modus zu arbeiten, benennt der obige Befehl nur die erste übereinstimmende Datei um, nicht jedoch die gesamte Liste der übereinstimmenden Dateien nach 'find'.
quelle
Hier ist ein netter Oneliner, der den Trick macht. Sed kann mit diesem Recht nicht umgehen, insbesondere wenn mehrere Variablen von xargs mit -n 2 übergeben werden. Eine Bash-Substitution würde dies leicht handhaben:
Durch Hinzufügen von -type -f werden die Verschiebungsvorgänge nur auf Dateien beschränkt, -print 0 behandelt leere Stellen in Pfaden.
quelle
Ich teile diesen Beitrag, da er ein bisschen mit Fragen zusammenhängt. Entschuldigung, dass Sie keine weiteren Details angegeben haben. Hoffe es hilft jemand anderem. http://www.peteryu.ca/tutorials/shellscripting/batch_rename
quelle
Das ist meine Arbeitslösung:
quelle