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 sed
rekurencyjnego wyszukiwania więcej niż jednego dopasowania wzorca, o ile wiem, nie ma innej rekursji.
I na koniec, jest to całkowicie null
rozdzielane - 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 function
prawdopodobnie będzie wymagać GNU
wersji sed
i find
do prawidłowej obsługi wywołań find printf
i sed -z -e
i :;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 fork
z sed
, ale ćwiczyłem też kilka sed
rekurencyjnych 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
./app
moż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ę, find
aby 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 sed
rekursji 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
-noclobber
opcję 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
find
proces. W przypadku find
szukamy tylko wszystkiego, co wymaga zmiany nazwy, ponieważ wykonaliśmy już wszystkie mv
operacje typu miejsce do miejsca za pomocą pierwszego polecenia funkcji. Zamiast wykonywać jakąkolwiek bezpośrednią akcję find
, na przykład exec
wywoł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
find
zlokalizowaniu plików, których potrzebujemy, bezpośrednio kompiluje i drukuje ( większość ) polecenia, które będziemy musieli przetworzyć na zmianę nazwy. %dir-depth
Dołą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. find
uż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
find
dane wyjściowe w oparciu o %directory-depth
to, aby ścieżki najbliższe relacji do $ {SRC} były przetwarzane jako pierwsze. Pozwala to uniknąć potencjalnych błędów związanych z umieszczaniem mv
plikó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
%Path
drukowanym 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 sed
procesu 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
sed
tutaj 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
mv
poleceń, 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
-exec
wezwania odbywają się tutaj bez sekundy fork
. W pierwszym, jak widzieliśmy, możemy zmodyfikować mv
polecenie dostarczany przez find
„s -printf
polecenia 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 sed
skoń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 read
go w ${msg}
taki, ${sh_io}
który można dowolnie zbadać poza funkcją.
Fajne.
-Mikrofon