Benennen Sie Dateien rekursiv mit find und sed um


84

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?


Übrigens, ich bin mir bewusst, dass es einen Umbenennungsbefehl gibt, aber ich würde wirklich gerne herausfinden, wie dies mit sed gemacht wird, damit ich in Zukunft leistungsfähigere Befehle ausführen kann.
opsb


Antworten:


32

Dies geschieht, weil seddie Zeichenfolge {}als Eingabe empfangen wird. Dies kann überprüft werden mit:

find . -exec echo `echo "{}" | sed 's/./foo/g'` \;

die foofoorekursiv 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 sedPipeline so zu zitieren , dass findsie für jede Datei ausgeführt wird, da findkeine 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:

#!/bin/sh
echo "$1" | sed 's/_test.rb$/_spec.rb/'

(Es mag eine perverse Art der Verwendung sh -cund eine Menge Anführungszeichen geben, um all dies in einem Befehl zu erledigen, aber ich werde es nicht versuchen.)


26
Für diejenigen, die sich über die perverse Verwendung von sh -c hier wundern, ist es: find spec -name "* _test.rb" -exec sh -c 'echo mv "$ 1" "$ (echo" $ 1 "| sed s / test.rb. \ $ / spec.rb /) "'_ {} \;
opsb

1
@opsb wofür zum Teufel ist das _? tolle Lösung - aber ich mag Ramtam Antwort mehr :)
iRaS

Prost! Hat mir viele Kopfschmerzen erspart. Der Vollständigkeit halber leite ich es so zu einem Skript: find. -name "Datei" -exec sh /path/to/script.sh {} \;
Sven M.

124

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:

find . -name *_test.rb | sed -e "p;s/test/spec/" | xargs -n2 mv

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 sie mvpaarweise ( xargs -n2). Beachten Sie, dass in diesem Fall der Pfad selbst keine Zeichenfolge enthalten sollte test.


Vielen Dank dafür!
Nolazybits

8
Leider hat dies Leerraumprobleme. Wenn Sie also Ordner verwenden, deren Namen Leerzeichen enthalten, wird dies bei xargs unterbrochen (bestätigen Sie mit -p für den ausführlichen / interaktiven Modus)
cde

aus irgendeinem Grund unbekannte primäre oder Betreiber bekommen
Masu

habe es verstanden, vergessen, Anführungszeichen um den Namen zu verwenden: find. -name '* _test.rb'
Masu

5
Wenn Sie sich mit Leerzeichen in Pfaden befassen müssen und GNU sed> = 4.2.2 verwenden, können Sie die -zOption zusammen mit Funden -print0und Xargs verwenden -0:find -name '*._test.rb' -print0 | sed -ze "p;s/test/spec/" | xargs -0 -n2 mv
Evan Purkhiser

22

Vielleicht möchten Sie einen anderen Weg in Betracht ziehen

for file in $(find . -name "*_test.rb")
do 
  echo mv $file `echo $file | sed s/_test.rb$/_spec.rb/`
done

Das sieht nach einem guten Weg aus. Ich bin wirklich bestrebt, den einen Liner zu knacken, um mein Wissen mehr als alles andere zu verbessern.
opsb

2
für Datei in $ (find. -name "* _test.rb"); Echo mv $ Datei echo $file | sed s/_test.rb$/_spec.rb/; Fertig ist ein Einzeiler, nicht wahr?
Bretticus

5
Dies funktioniert nicht, wenn Sie Dateinamen mit Leerzeichen haben. forwird 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 .
Onitake

Ich stimme @onitake zu, obwohl ich es vorziehen würde, die -execOption von find zu verwenden.
ShellFish

18

Ich finde das kürzer

find . -name '*_test.rb' -exec bash -c 'echo mv $0 ${0/test.rb/spec.rb}' {} \;

Hallo, ich denke, ' _test.rb "sollte' _test.rb 'sein (doppeltes Anführungszeichen zu einfachem Anführungszeichen). Kann ich fragen, warum Sie den Unterstrich verwenden, um das Argument zu pushen, das Sie $ 1 positionieren möchten, wenn es mir 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 {} \;
agtb

Vielen Dank für Ihre Vorschläge, behoben
csg

Vielen Dank, dass Sie das geklärt haben - ich habe nur kommentiert, weil ich eine Weile darüber nachgedacht habe, was es bedeutet, zu denken, es sei vielleicht eine Trickverwendung von $ _ ('_' ist in Dokumenten ziemlich schwer zu suchen!)
agtb

9

Sie können es ohne sed tun, wenn Sie wollen:

for i in `find -name '*_test.rb'` ; do mv $i ${i%%_test.rb}_spec.rb ; done

${var%%suffix}Streifen suffixvom Wert von var.

oder, um es mit sed zu tun:

for i in `find -name '*_test.rb'` ; do mv $i `echo $i | sed 's/test/spec/'` ; done

Dies funktioniert nicht ( sedwie in der akzeptierten Antwort).
Ali

@Ali, es funktioniert - ich habe es selbst getestet, als ich die Antwort schrieb. @ larsman Erklärung gilt nicht für for i in... ; do ... ; done, welche Befehle über die Shell ausführt und nicht verstehen Graviszeichen.
Wayne Conrad

9

Sie erwähnen, dass Sie bashals Shell verwenden. In diesem Fall benötigen Sie nicht wirklich findund sedum die Batch-Umbenennung zu erreichen, nach der Sie suchen ...

Angenommen, Sie verwenden bashals Shell:

$ echo $SHELL
/bin/bash
$ _

... und vorausgesetzt, Sie haben die sogenannte globstarShell-Option aktiviert :

$ shopt -p globstar
shopt -s globstar
$ _

... und schließlich vorausgesetzt, Sie haben das renameDienstprogramm installiert (im util-linux-ngPaket enthalten)

$ which rename
/usr/bin/rename
$ _

... dann können Sie die Batch-Umbenennung in einem Bash-Einzeiler wie folgt erreichen:

$ rename _test _spec **/*_test.rb

(Die globstarShell-Option stellt sicher, dass bash alle übereinstimmenden *_test.rbDateien findet, unabhängig davon, wie tief sie in der Verzeichnishierarchie verschachtelt sind. Verwenden help shoptSie diese Option, um herauszufinden, wie die Option festgelegt wird.)


7

Der einfachste Weg :

find . -name "*_test.rb" | xargs rename s/_test/_spec/

Der schnellste Weg (vorausgesetzt, Sie haben 4 Prozessoren):

find . -name "*_test.rb" | xargs -P 4 rename s/_test/_spec/

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 -boder cat /proc/meminfoermitteln, mit wie viel RAM Sie arbeiten müssen. Andernfalls verwenden Sie topoder Ihre Systemaktivitätsüberwachungs-App.

Ein sicherer Weg (vorausgesetzt, Sie haben 1000000 Byte RAM zum Arbeiten):

find . -name "*_test.rb" | xargs -s 1000000 rename s/_test/_spec/

2

Folgendes hat bei mir funktioniert, wenn die Dateinamen Leerzeichen enthielten. Im folgenden Beispiel werden alle .dar-Dateien rekursiv in .zip-Dateien umbenannt:

find . -name "*.dar" -exec bash -c 'mv "$0" "`echo \"$0\" | sed s/.dar/.zip/`"' {} \;

2

Dafür brauchst du nicht sed. Sie können mit einer whileSchleife, die mit dem Ergebnis findeiner Prozessersetzung gespeist wird, perfekt allein sein .

Wenn Sie also einen findAusdruck haben, der die benötigten Dateien auswählt, verwenden Sie die folgende Syntax:

while IFS= read -r file; do
     echo "mv $file ${file%_test.rb}_spec.rb"  # remove "echo" when OK!
done < <(find -name "*_test.rb")

Dadurch werden findalle Dateien abgelegt und umbenannt, wobei die Zeichenfolge _test.rbvom 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.

$ file="HELLOa_test.rbBYE_test.rb"
$ echo "${file%_test.rb}"          # remove _test.rb from the end
HELLOa_test.rbBYE
$ echo "${file%_test.rb}_spec.rb"  # remove _test.rb and append _spec.rb
HELLOa_test.rbBYE_spec.rb

Siehe ein Beispiel:

$ tree
.
├── ab_testArb
├── a_test.rb
├── a_test.rb_test.rb
├── b_test.rb
├── c_test.hello
├── c_test.rb
└── mydir
    └── d_test.rb

$ while IFS= read -r file; do echo "mv $file ${file/_test.rb/_spec.rb}"; done < <(find -name "*_test.rb")
mv ./b_test.rb ./b_spec.rb
mv ./mydir/d_test.rb ./mydir/d_spec.rb
mv ./a_test.rb ./a_spec.rb
mv ./c_test.rb ./c_spec.rb

Vielen Dank! Es hat mir geholfen, die nachfolgende .gz-Datei einfach und rekursiv aus allen Dateinamen zu entfernen. while IFS= read -r file; do mv $file ${file%.gz}; done < <(find -type f -name "*.gz")
Vinay Vissh

1
@ CasualCoder schön das zu lesen :) Hinweis kann man direkt sagen find .... -exec mv .... Seien Sie auch vorsichtig, $fileda es fehlschlägt, wenn es Leerzeichen enthält. Verwenden Sie besser Anführungszeichen "$file".
Fedorqui 'SO hör auf zu schaden'

1

wenn Sie Ruby haben (1.9+)

ruby -e 'Dir["**/*._test.rb"].each{|x|test(?f,x) and File.rename(x,x.gsub(/_test/,"_spec") ) }'

1

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:

find . -name "*_test.rb" | perl -pe 's/^((.*_)test.rb)$/"\1" "\2spec.rb"/' | xargs -n2 mv

Ich brauchte wirklich eine solche Änderung, weil in meinem Anwendungsfall der letzte Befehl eher so aussieht

find . -name "olddir" | perl -pe 's/^((.*)olddir)$/"\1" "\2new directory"/' | xargs -n2 mv

1

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 sedrekursiven Suche nach mehr als einer Übereinstimmung des Musters gibt es meines Wissens keine andere Rekursion.

Und zuletzt ist dies vollständig nullabgegrenzt - es wird kein Zeichen in einem Dateinamen außer dem ausgelöst null. Ich denke nicht, dass du das haben solltest.

Das ist übrigens WIRKLICH schnell. Aussehen:

% _mvnfind() { mv -n "${1}" "${2}" && cd "${2}"
> read -r SED <<SED
> :;s|${3}\(.*/[^/]*${5}\)|${4}\1|;t;:;s|\(${5}.*\)${3}|\1${4}|;t;s|^[0-9]*[\t]\(mv.*\)${5}|\1|p
> SED
> find . -name "*${3}*" -printf "%d\tmv %P ${5} %P\000" |
> sort -zg | sed -nz ${SED} | read -r ${6}
> echo <<EOF
> Prepared commands saved in variable: ${6}
> To view do: printf ${6} | tr "\000" "\n"
> To run do: sh <<EORUN
> $(printf ${6} | tr "\000" "\n")
> EORUN
> EOF
> }
% rm -rf "${UNNECESSARY:=/any/dirs/you/dont/want/moved}"
% time ( _mvnfind ${SRC=./test_tree} ${TGT=./mv_tree} \
> ${OLD=google} ${NEW=replacement_word} ${sed_sep=SsEeDd} \
> ${sh_io:=sh_io} ; printf %b\\000 "${sh_io}" | tr "\000" "\n" \
> | wc - ; echo ${sh_io} | tr "\000" "\n" |  tail -n 2 )

   <actual process time used:>
    0.06s user 0.03s system 106% cpu 0.090 total

   <output from wc:>

    Lines  Words  Bytes
    115     362   20691 -

    <output from tail:>

    mv .config/replacement_word-chrome-beta/Default/.../googlestars \
    .config/replacement_word-chrome-beta/Default/.../replacement_wordstars        

HINWEIS: Für die oben genannten functionSchritte sind wahrscheinlich GNUVersionen von sedund erforderlich find, um die Aufrufe find printfund sed -z -eund 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 forkmit sed, aber ich war auch einige praktizierende sedrekursive 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}
    • Ich habe absichtlich jeden Funktionsaufruf ausgelassen, der Daten jeglicher Art löschen oder zerstören könnte. Sie erwähnen, dass ./appdies 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, findum dies programmgesteuert zu tun, aber das gehört Ihnen.
  • _mvnfind "${@}"
    • Deklarieren Sie die Argumente und rufen Sie die Worker-Funktion auf. ${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 auf seddie 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
    • Der ganze Baum wird von Anfang an bewegt. Es wird viel Kopfschmerzen sparen; glaube mir. Der Rest von dem, was Sie tun möchten - das Umbenennen - ist einfach eine Frage der Metadaten des Dateisystems. Wenn Sie dies beispielsweise von einem Laufwerk auf ein anderes oder über Dateisystemgrenzen hinweg verschieben, ist es besser, dies sofort mit einem Befehl zu tun. Es ist auch sicherer. Beachten Sie die -noclobberOption für mv; Wie geschrieben, wird diese Funktion nicht dort platzieren, ${SRC_DIR}wo ${TGT_DIR}bereits eine vorhanden ist.
  • read -R SED <<HEREDOC
    • Ich habe alle Befehle von sed hier gefunden, um Ärger zu vermeiden, und sie in eine Variable eingelesen, um sie unten an sed weiterzuleiten. Erklärung unten.
  • find . -name ${OLD} -printf
    • Wir beginnen den findProzess. Mit finduns nur für etwas suchen , die Umbenennung braucht , weil wir bereits alle der tat Ort-zu-Ort - mvOperationen mit der ersten Befehl der Funktion. Anstatt eine direkte Aktion mit beispielsweise findeinem execAufruf 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:'
    • Nachdem findwir 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-deptham 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. findverwendet 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
    • Wir sortieren die gesamte findAusgabe danach, %directory-depthsodass die Pfade, die in Bezug auf $ {SRC} am nächsten liegen, zuerst bearbeitet werden. Dies vermeidet mögliche Fehler beim mvEinfü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}
    • Ich denke, dies ist die einzige Schleife im gesamten Skript, und sie durchläuft nur die zweite Schleife, die %Pathfü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 zweiten sedProzess, und obwohl eine kurze Schleife möglicherweise nicht wünschenswert ist, schlägt sie sicherlich das Laichen und Verzweigen eines gesamten Prozesses.
    • Im Grunde genommen sedwird 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 Zeichenfolge stdout(an die es dann als nächstes wieder abfängt) und beendet die Schleife.
    • Dadurch wird vermieden, dass die gesamte Zeichenfolge analysiert werden muss, und es wird sichergestellt, dass die erste Hälfte der mvBefehlszeichenfolge, 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 aus mvdem Zielpfad.
  • sed -ex...-ex search|%dir_depth(save*)${sed_sep}|(only_saved)|out
    • Die beiden -execAnrufe hier erfolgen ohne Sekunde fork. Wie wir gesehen haben, ändern wir im ersten Schritt den mvBefehl, der vom Funktionsbefehl find's bereitgestellt wird , -printfnach 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. Sobald sedalles 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:

% mv /path2/$SRC/$OLD_DIR/$OLD_FILE /same/path_w/$NEW_DIR/$NEW_FILE \000

Es wird reades ${msg}als ${sh_io}was nach Belieben außerhalb der Funktion untersucht werden kann.

Cool.

-Mike


1

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:

find . -name "*_test.rb" -print0 | while read -d $'\0' file
do
    echo mv "$file" "$(echo $file | sed s/test/spec/)"
done

1

Dies ist ein Beispiel, das in allen Fällen funktionieren sollte. Funktioniert rekursiv, benötigt nur Shell und unterstützt Dateinamen mit Leerzeichen.

find spec -name "*_test.rb" -print0 | while read -d $'\0' file; do mv "$file" "`echo $file | sed s/test/spec/`"; done

0
$ find spec -name "*_test.rb"
spec/dir2/a_test.rb
spec/dir1/a_test.rb

$ find spec -name "*_test.rb" | xargs -n 1 /usr/bin/perl -e '($new=$ARGV[0]) =~ s/test/spec/; system(qq(mv),qq(-v), $ARGV[0], $new);'
`spec/dir2/a_test.rb' -> `spec/dir2/a_spec.rb'
`spec/dir1/a_test.rb' -> `spec/dir1/a_spec.rb'

$ find spec -name "*_spec.rb"
spec/dir2/b_spec.rb
spec/dir2/a_spec.rb
spec/dir1/a_spec.rb
spec/dir1/c_spec.rb

Ah..Ich kenne keine andere Möglichkeit, sed zu verwenden, als die Logik in ein Shell-Skript zu setzen und dies in exec aufzurufen. sah nicht die Anforderung, sed anfangs zu verwenden
Damodharan R

0

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

#!/bin/bash
IFS=$'\n'
function RecurseDirs
{
for f in "$@"
do
  newf=echo "${f}" | sed -e 's/^(.*_)test.rb$/\1spec.rb/g'
    echo "${f}" "${newf}"
    mv "${f}" "${newf}"
    f="${newf}"
  if [[ -d "${f}" ]]; then
    cd "${f}"
    RecurseDirs $(ls -1 ".")
  fi
done
cd ..
}
RecurseDirs .

Wie funktioniert das sed, ohne zu entkommen, ()wenn Sie die -rOption nicht festlegen ?
Mikeserv

0

Sicherere Umbenennung mit find utils und sed Typ für reguläre Ausdrücke:

  mkdir ~/practice

  cd ~/practice

  touch classic.txt.txt

  touch folk.txt.txt

Entfernen Sie die Erweiterung ".txt.txt" wie folgt:

  cd ~/practice

  find . -name "*txt" -execdir sh -c 'mv "$0" `echo "$0" | sed -r 's/\.[[:alnum:]]+\.[[:alnum:]]+$//'`' {} \;

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'.

  find . -name "*txt" -execdir sh -c 'mv "$0" `echo "$0" | sed -r 's/\.[[:alnum:]]+\.[[:alnum:]]+$//'`' {} +

0

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:

find ./spec -type f -name "*_test.rb" -print0 | xargs -0 -I {} sh -c 'export file={}; mv $file ${file/_test.rb/_spec.rb}'

Durch Hinzufügen von -type -f werden die Verschiebungsvorgänge nur auf Dateien beschränkt, -print 0 behandelt leere Stellen in Pfaden.



0

Das ist meine Arbeitslösung:

for FILE in {{FILE_PATTERN}}; do echo ${FILE} | mv ${FILE} $(sed 's/{{SOURCE_PATTERN}}/{{TARGET_PATTERN}}/g'); done
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.