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