Rekurencyjnie zmieniaj nazwy plików za pomocą funkcji find i sed

85

Chcę przejrzeć kilka katalogów i zmienić nazwy wszystkich plików kończących się na _test.rb, aby zamiast tego kończyły się na _spec.rb. To jest coś, czego nigdy nie wymyśliłem, jak zrobić z bashem, więc tym razem pomyślałem, że włożyłem trochę wysiłku, aby to przybić. Jednak do tej pory nie wychodzę, mój najlepszy wysiłek to:

find spec -name "*_test.rb" -exec echo mv {} `echo {} | sed s/test/spec/` \;

Uwaga: po exec jest dodatkowe echo, więc polecenie jest drukowane zamiast uruchamiania podczas testowania.

Kiedy go uruchomię, dane wyjściowe dla każdej pasującej nazwy pliku to:

mv original original

tj. zamiana seda została utracona. Co to za sztuczka?

opsb
źródło
Przy okazji, zdaję sobie sprawę, że istnieje polecenie zmiany nazwy, ale naprawdę chciałbym dowiedzieć się, jak to zrobić za pomocą seda, aby móc wykonywać bardziej wydajne polecenia w przyszłości.
opsb
2
Proszę nie przesyłać .
Wstrzymano do odwołania.

Odpowiedzi:

32

Dzieje się tak, ponieważ sedotrzymuje ciąg {}jako dane wejściowe, co można zweryfikować za pomocą:

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

który wypisuje foofoorekursywnie dla każdego pliku w katalogu. Przyczyną takiego zachowania jest to, że potok jest wykonywany raz przez powłokę, gdy rozszerza całe polecenie.

Nie ma możliwości cytowania sedpotoku w sposób, findktóry wykona go dla każdego pliku, ponieważ findnie wykonuje poleceń przez powłokę i nie ma pojęcia o potokach ani cudzysłowach. Podręcznik GNU findutils wyjaśnia, jak wykonać podobne zadanie, umieszczając potok w osobnym skrypcie powłoki:

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

(Może istnieć jakiś przewrotny sposób użycia sh -ci mnóstwo cytatów, aby zrobić to wszystko za pomocą jednego polecenia, ale nie zamierzam próbować).

Fred Foo
źródło
27
Dla tych, którzy zastanawiają się nad przewrotnym użyciem sh -c tutaj jest to: find spec -name "* _test.rb" -exec sh -c 'echo mv "$ 1" "$ (echo" $ 1 "| sed s / test.rb \ $ / spec.rb /) "'_ {} \;
opsb
1
@opsb, po co to _ do cholery? świetne rozwiązanie - ale wolę ramtam odpowiedzieć bardziej :)
iRaS
Twoje zdrowie! Uratował mi wiele bólów głowy. Ze względu na kompletność w następujący sposób przekierowuję to do skryptu: znajdź. -name "plik" -exec sh /path/to/script.sh {} \;
Sven M.,
128

Aby rozwiązać ten problem w sposób najbardziej zbliżony do pierwotnego, prawdopodobnie użyłbym opcji xargs "args per command line":

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

Znajduje pliki w bieżącym katalogu roboczym rekurencyjnie, powtarza oryginalną nazwę pliku ( p), a następnie zmodyfikowaną nazwę ( s/test/spec/) i przekazuje to wszystko mvparami ( xargs -n2). Uważaj, w tym przypadku sama ścieżka nie powinna zawierać łańcucha test.

ramtam
źródło
9
Niestety powoduje to problemy z białymi znakami. Tak więc użycie z folderami, które mają spacje w nazwie, spowoduje uszkodzenie na xargs (potwierdź za pomocą -p dla trybu gadatliwego / interaktywnego)
cde
1
Dokładnie tego szukałem. Szkoda dla problemu białych znaków (chociaż tego nie testowałem). Ale jak na moje obecne potrzeby jest idealny. Sugerowałbym przetestowanie go najpierw z "echo" zamiast "mv" jako parametrem w "xargs".
Michele Dall'Agata
5
Jeśli potrzebujesz poradzić sobie z białymi znakami w ścieżkach i używasz GNU sed> = 4.2.2, możesz użyć tej -zopcji razem z -print0wynikami wyszukiwania i xargs -0:find -name '*._test.rb' -print0 | sed -ze "p;s/test/spec/" | xargs -0 -n2 mv
Evan Purkhiser
Najlepsze rozwiązanie. O wiele szybciej niż find -exec. Dziękuję
Miguel A. Baldi Hörlle
To nie zadziała, jeśli testw jednej ścieżce znajduje się wiele folderów. sedzmieni nazwę tylko pierwszego, a mvpolecenie zakończy się niepowodzeniem w przypadku No such file or directorybłędu.
Casey
22

możesz rozważyć inny sposób, na przykład

for file in $(find . -name "*_test.rb")
do 
  echo mv $file `echo $file | sed s/_test.rb$/_spec.rb/`
done
ajreal
źródło
Wygląda na to, że to dobry sposób. Naprawdę chcę złamać jedną linijkę, aby poprawić swoją wiedzę bardziej niż cokolwiek innego.
opsb
2
dla pliku w $ (znajdź. -nazwa "* _test.rb"); zrobić echo mv $ plik echo $file | sed s/_test.rb$/_spec.rb/; skończone to jednolinijkowe, prawda?
Bretticus,
5
To nie zadziała, jeśli masz nazwy plików ze spacjami. forpodzieli je na osobne słowa. Możesz sprawić, by działało, instruując pętlę for, aby dzieliła się tylko w nowych wierszach. Przykłady można znaleźć pod adresem cyberciti.biz/tips/handling-filenames-with-spaces-in-bash.html .
onitake
Zgadzam się z @onitake, chociaż wolałbym skorzystać z -execopcji find.
ShellFish
18

Uważam, że ten jest krótszy

find . -name '*_test.rb' -exec bash -c 'echo mv $0 ${0/test.rb/spec.rb}' {} \;
csg
źródło
Cześć, myślę, że „ _test.rb” powinno mieć wartość „ _test.rb” (podwójny cudzysłów do pojedynczego cudzysłowu). Czy mogę zapytać, dlaczego używasz podkreślenia, aby przekazać argument, który chcesz ustawić na pozycji $ 1, kiedy wydaje mi się, że find . -name '*_test.rb' -exec bash -c 'echo mv $0 ${0/test.rb/spec.rb}' {} \;działa Jak by to byłofind . -name '*_test.rb' -exec bash -c 'echo mv $1 ${1/test.rb/spec.rb}' iAmArgumentZero {} \;
agtb
Dzięki za sugestie, naprawiono
csg
Dzięki za wyjaśnienie tego - skomentowałem tylko dlatego, że spędziłem chwilę zastanawiając się nad znaczeniem _ myśląc, że może to być jakiś trikowy sposób użycia $ _ ('_' jest dość trudne do wyszukania w dokumentach!)
agtb
9

Możesz to zrobić bez seda, jeśli chcesz:

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

${var%%suffix}paski suffixod wartości var.

lub, aby to zrobić używając seda:

for i in `find -name '*_test.rb'` ; do mv $i `echo $i | sed 's/test/spec/'` ; done
Wayne Conrad
źródło
to nie działa (ten sedjedyny), jak wyjaśnia zaakceptowana odpowiedź.
Ali
@Ali, to działa - sam to przetestowałem, kiedy pisałem odpowiedź. Wyjaśnienie @ larsmana nie ma zastosowania do programu for i in... ; do ... ; done, który wykonuje polecenia przez powłokę i nie rozumie znaku wstecznego .
Wayne Conrad
9

Wspominasz, że używasz bashjako powłoki, w takim przypadku tak naprawdę nie potrzebujesz findi sedaby uzyskać zmianę nazwy partii, której szukasz ...

Zakładając, że używasz bashjako powłoki:

$ echo $SHELL
/bin/bash
$ _

... i zakładając, że włączyłeś tzw. globstaropcję powłoki:

$ shopt -p globstar
shopt -s globstar
$ _

... i wreszcie zakładając, że zainstalowałeś renamenarzędzie (znalezione w util-linux-ngpakiecie)

$ which rename
/usr/bin/rename
$ _

... wtedy możesz zmienić nazwę partii w linijce bash w następujący sposób:

$ rename _test _spec **/*_test.rb

( globstaropcja powłoki zapewni, że bash znajdzie wszystkie pasujące *_test.rbpliki, bez względu na to, jak głęboko są one zagnieżdżone w hierarchii katalogów ... użyj, help shoptaby dowiedzieć się, jak ustawić tę opcję)

pvandenberk
źródło
7

Najłatwiejszy sposób :

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

Najszybszy sposób (zakładając, że masz 4 procesory):

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

Jeśli masz dużą liczbę plików do przetworzenia, możliwe jest, że lista nazw plików przesłana potokiem do xargs spowoduje, że wynikowy wiersz poleceń przekroczy maksymalną dozwoloną długość.

Możesz sprawdzić limit swojego systemu za pomocą getconf ARG_MAX

W większości systemów linuxowych możesz użyć free -blub cat /proc/meminfodowiedzieć się, z jaką ilością pamięci RAM musisz pracować; W przeciwnym razie użyj toplub aplikacji do monitorowania aktywności systemu.

Bezpieczniejszy sposób (zakładając, że masz 1000000 bajtów pamięci RAM do pracy):

find . -name "*_test.rb" | xargs -s 1000000 rename s/_test/_spec/
l3x
źródło
2

Oto, co zadziałało, gdy nazwy plików zawierały spacje. Poniższy przykład rekurencyjnie zmienia nazwy wszystkich plików .dar na pliki .zip:

find . -name "*.dar" -exec bash -c 'mv "$0" "`echo \"$0\" | sed s/.dar/.zip/`"' {} \;
rskengineer
źródło
2

Do tego nie potrzebujesz sed. Można doskonale się sam na sam z whilepętli zasilanej wyniku findpoprzez podstawienie procesowego .

Więc jeśli masz findwyrażenie, które wybiera potrzebne pliki, użyj składni:

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

Spowoduje to findpliki i zmianę nazwy wszystkich z nich, usuwając ciąg _test.rbz końca i dołączając _spec.rb.

W tym kroku używamy rozszerzenia parametrów powłoki, gdzie ${var%string}usuwa najkrótszy pasujący wzorzec „ciąg” z $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

Zobacz przykład:

$ 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
fedorqui 'SO przestań szkodzić'
źródło
Wielkie dzięki! Pomogło mi to w łatwym rekursywnym usuwaniu końcowych plików .gz ze wszystkich nazw plików. while IFS= read -r file; do mv $file ${file%.gz}; done < <(find -type f -name "*.gz")
Vinay Vissh
1
@CasualCoder miło to przeczytać :) Uwaga, możesz od razu powiedzieć find .... -exec mv .... Uważaj także, $fileponieważ nie powiedzie się, jeśli zawiera spacje. Lepiej używaj cytatów "$file".
fedorqui 'SO przestań szkodzić'
1

jeśli masz Ruby (1,9+)

ruby -e 'Dir["**/*._test.rb"].each{|x|test(?f,x) and File.rename(x,x.gsub(/_test/,"_spec") ) }'
kurumi
źródło
1

W odpowiedzi Ramtam, która mi się podoba, część wyszukiwania działa OK, ale reszta nie, jeśli ścieżka ma spacje. Nie jestem zbyt zaznajomiony z sedem, ale udało mi się zmodyfikować tę odpowiedź na:

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

Naprawdę potrzebowałem takiej zmiany, ponieważ w moim przypadku ostateczne polecenie wygląda bardziej jak

find . -name "olddir" | perl -pe 's/^((.*)olddir)$/"\1" "\2new directory"/' | xargs -n2 mv
dzs0000
źródło
1

Nie mam serca, by zrobić to od nowa, ale napisałem to w odpowiedzi na polecenie Commandline Find Sed Exec . Pytający chciał wiedzieć, jak przenieść całe drzewo, być może z wyłączeniem katalogu lub dwóch, i zmienić nazwy wszystkich plików i katalogów zawierających ciąg „STARE”, aby zamiast tego zawierały „NOWY” .

Oprócz opisania poniżej, jak z drobiazgową szczegółowością , ta metoda może być również wyjątkowa, ponieważ zawiera wbudowane debugowanie. Zasadniczo nie robi nic tak, jak napisano, z wyjątkiem kompilowania i zapisywania w zmiennej wszystkich poleceń, które uważa, że ​​powinien wykonać, aby wykonać żądaną pracę.

To również wyraźnie unika pętli tak bardzo, jak to możliwe. Oprócz sedrekurencyjnego wyszukiwania więcej niż jednego dopasowania wzorca, o ile wiem, nie ma innej rekursji.

I na koniec, jest to całkowicie nullrozdzielane - nie wyłącza żadnego znaku w żadnej nazwie pliku oprócz null. Myślę, że nie powinieneś tego mieć.

Nawiasem mówiąc, jest to NAPRAWDĘ szybkie. Popatrz:

% _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        

UWAGA: Powyższe functionprawdopodobnie będzie wymagać GNUwersji sedi finddo prawidłowej obsługi wywołań find printfi sed -z -ei :;recursive regex test;t. Jeśli te funkcje nie są dla Ciebie dostępne, funkcja może zostać zduplikowana z kilkoma drobnymi poprawkami.

Powinno to zrobić wszystko, co chciałeś od początku do końca bez większego zamieszania. Zrobiłem to forkz sed, ale ćwiczyłem też kilka sedrekurencyjnych technik rozgałęziania, więc dlatego tu jestem. Przypuszczam, że to trochę jak przecena fryzury w szkole fryzjerskiej. Oto przepływ pracy:

  • rm -rf ${UNNECESSARY}
    • Celowo pominąłem wszelkie wywołania funkcjonalne, które mogłyby usunąć lub zniszczyć jakiekolwiek dane. Wspomniałeś, że ./appmoże to być niepożądane. Usuń go lub przenieś wcześniej w inne miejsce lub, alternatywnie, możesz wbudować \( -path PATTERN -exec rm -rf \{\} \)procedurę, findaby zrobić to programowo, ale ta jest cała twoja.
  • _mvnfind "${@}"
    • Zadeklaruj jego argumenty i wywołaj funkcję roboczą. ${sh_io}jest szczególnie ważne, ponieważ zapisuje zwrot z funkcji. ${sed_sep}pojawia się w bliskiej chwili; jest to dowolny ciąg używany do odniesienia się do sedrekursji w funkcji. Jeśli ${sed_sep}jest ustawione na wartość, która mogłaby potencjalnie zostać znaleziona w dowolnej ścieżce lub nazwie pliku, na której działałeś ... cóż, po prostu nie pozwól, aby tak było.
  • mv -n $1 $2
    • Całe drzewo jest przenoszone od początku. Pozwoli to zaoszczędzić wiele bólu głowy; uwierz mi. Reszta tego, co chcesz zrobić - zmiana nazwy - jest po prostu kwestią metadanych systemu plików. Jeśli na przykład przenosisz to z jednego dysku na inny lub przekraczasz jakiekolwiek granice systemu plików, lepiej zrobić to od razu za pomocą jednego polecenia. Jest też bezpieczniejsze. Zwróć uwagę na -noclobberopcję ustawioną dla mv; jak napisano, ta funkcja nie umieści ${SRC_DIR}tam, gdzie ${TGT_DIR}już istnieje.
  • read -R SED <<HEREDOC
    • Zlokalizowałem tutaj wszystkie polecenia seda, aby zaoszczędzić na ucieczce przed kłopotami i wczytać je do zmiennej, aby przekazać sedowi poniżej. Wyjaśnienie poniżej.
  • find . -name ${OLD} -printf
    • Rozpoczynamy findproces. W przypadku findszukamy tylko wszystkiego, co wymaga zmiany nazwy, ponieważ wykonaliśmy już wszystkie mvoperacje typu miejsce do miejsca za pomocą pierwszego polecenia funkcji. Zamiast wykonywać jakąkolwiek bezpośrednią akcję find, na przykład execwywołanie, używamy go do dynamicznego budowania wiersza poleceń z -printf.
  • %dir-depth :tab: 'mv '%path-to-${SRC}' '${sed_sep}'%path-again :null delimiter:'
    • Po findzlokalizowaniu plików, których potrzebujemy, bezpośrednio kompiluje i drukuje ( większość ) polecenia, które będziemy musieli przetworzyć na zmianę nazwy. %dir-depthDołączona na początku każdej linii przyczyni się do zapewnienia, że nie próbujesz zmienić nazwę pliku lub katalogu w drzewie z obiektu nadrzędnego, który ma dopiero zostać zmieniona. findużywa różnych technik optymalizacji, aby przejść przez drzewo systemu plików i nie jest pewne, że zwróci dane, których potrzebujemy, w kolejności bezpiecznej dla operacji. Dlatego my dalej ...
  • sort -general-numerical -zero-delimited
    • Sortujemy wszystkie finddane wyjściowe w oparciu o %directory-depthto, aby ścieżki najbliższe relacji do $ {SRC} były przetwarzane jako pierwsze. Pozwala to uniknąć potencjalnych błędów związanych z umieszczaniem mvplików w nieistniejących lokalizacjach i minimalizuje potrzebę cyklicznego zapętlania. ( w rzeczywistości znalezienie pętli może być trudne )
  • sed -ex :rcrs;srch|(save${sep}*til)${OLD}|\saved${SUBSTNEW}|;til ${OLD=0}
    • Myślę, że jest to jedyna pętla w całym skrypcie, która zapętla się tylko po drugim %Pathdrukowanym dla każdego ciągu na wypadek, gdyby zawierał więcej niż jedną wartość $ {OLD}, która może wymagać zastąpienia. Wszystkie inne rozwiązania, które sobie wyobrażałem, wymagały drugiego sedprocesu i chociaż krótka pętla może nie być pożądana, z pewnością pokonuje tarło i rozwidlenie całego procesu.
    • Więc zasadniczo to, co sedtutaj robi, to wyszukanie $ {sed_sep}, a następnie po znalezieniu go, zapisanie go i wszystkich napotkanych znaków, aż znajdzie $ {STARE}, które następnie zastępuje $ {NOWY}. Następnie wraca do $ {sed_sep} i ponownie szuka $ {OLD}, na wypadek gdyby wystąpiło więcej niż raz w ciągu. Jeśli nie zostanie znaleziony, drukuje zmodyfikowany ciąg stdout( do którego następnie przechwytuje ponownie) i kończy pętlę.
    • Pozwala to uniknąć konieczności analizowania całego ciągu i zapewnia, że ​​pierwsza połowa ciągu mvpoleceń, która oczywiście musi zawierać $ {OLD}, zawiera go, a druga połowa jest zmieniana tyle razy, ile jest to konieczne do wyczyszczenia Nazwa $ {OLD} ze mvścieżki docelowej.
  • sed -ex...-ex search|%dir_depth(save*)${sed_sep}|(only_saved)|out
    • Te dwa -execwezwania odbywają się tutaj bez sekundy fork. W pierwszym, jak widzieliśmy, możemy zmodyfikować mvpolecenie dostarczany przez find„s -printfpolecenia funkcji jako niezbędne do prawidłowego zmienić wszystkie odniesienia do $ {OLD} do $ {NEW}, ale w tym celu trzeba było korzystać z niektórych dowolne punkty odniesienia, które nie powinny być uwzględniane w ostatecznym wyniku. Więc kiedy sedskończy wszystko, co musi zrobić, poinstruujemy go, aby wymazał punkty odniesienia z bufora wstrzymującego przed przekazaniem go dalej.

A TERAZ WRACAMY

read otrzyma polecenie, które wygląda następująco:

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

Zmieni readgo w ${msg}taki, ${sh_io}który można dowolnie zbadać poza funkcją.

Fajne.

-Mikrofon

mikeserv
źródło
1

Byłem w stanie obsłużyć nazwy plików ze spacjami, postępując zgodnie z przykładami sugerowanymi przez onitake.

To się nie psuje, jeśli ścieżka zawiera spacje lub ciąg test:

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

To jest przykład, który powinien działać we wszystkich przypadkach. Działa rekursywnie, potrzebuje tylko powłoki i obsługuje nazwy plików ze spacjami.

find spec -name "*_test.rb" -print0 | while read -d $'\0' file; do mv "$file" "`echo $file | sed s/test/spec/`"; done
eldy
źródło
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
Damodharan R
źródło
Ach, nie znam innego sposobu na użycie seda niż umieszczenie logiki w skrypcie powłoki i wywołanie tego w exec. nie widziałem wymogu używania seda początkowo
Damodharan R
0

Twoje pytanie wydaje się dotyczyć seda, ale aby osiągnąć cel polegający na rekurencyjnej zmianie nazwy, proponuję następującą, bezwstydnie wyrwaną z innej odpowiedzi, którą tutaj udzieliłem: rekurencyjna zmiana nazwy w 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 .
dreynold
źródło
Jak seddziała bez ucieczki, ()jeśli nie ustawisz -ropcji?
mikeserv,
0

Bezpieczniejszy sposób zmiany nazwy za pomocą funkcji find utils i wyrażenia regularnego sed:

  mkdir ~/practice

  cd ~/practice

  touch classic.txt.txt

  touch folk.txt.txt

Usuń rozszerzenie „.txt.txt” w następujący sposób -

  cd ~/practice

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

Jeśli użyjesz + zamiast; aby pracować w trybie wsadowym, powyższe polecenie zmieni nazwę tylko pierwszego pasującego pliku, ale nie całej listy pasujących plików za pomocą polecenia „znajdź”.

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

Oto fajny oneliner, który załatwia sprawę. Sed nie może sobie z tym dobrze poradzić, zwłaszcza jeśli wiele zmiennych jest przekazywanych przez xargs z -n 2. Podstawienie bash poradziłoby sobie z tym łatwo, na przykład:

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

Dodanie -type -f ograniczy operacje przenoszenia tylko do plików, -print 0 obsłuży puste spacje w ścieżkach.

Orsiris de Jong
źródło
0

To jest moje rozwiązanie robocze:

for FILE in {{FILE_PATTERN}}; do echo ${FILE} | mv ${FILE} $(sed 's/{{SOURCE_PATTERN}}/{{TARGET_PATTERN}}/g'); done
Antonio Petricca
źródło