Czy istnieje sposób, aby perl -i nie zamazywał dowiązań symbolicznych?

12

Mój przyjaciel wskazuje, że jeśli to zrobisz:

perl -pi.bak -e 's/foo/bar/' somefile

kiedy „somefile” jest w rzeczywistości dowiązaniem symbolicznym, perl robi to, co mówią doktorzy:

Robi to poprzez zmianę nazwy pliku wejściowego, otwarcie pliku wyjściowego pod oryginalną nazwą i wybranie tego pliku wyjściowego jako domyślnego dla instrukcji print (). Rozszerzenie, jeśli jest dostarczone, służy do modyfikacji nazwy starego pliku w celu utworzenia kopii zapasowej [...]

W rezultacie powstaje nowe dowiązanie symboliczne „somefile.bak” wskazujące na niezmieniony plik rzeczywisty oraz nowy, zmieniony zwykły plik „somefile” ze zmianami.

W wielu przypadkach podążanie za dowiązaniem symbolicznym byłoby pożądanym zachowaniem (nawet jeśli pozostawia ono niejednoznaczną prawidłową lokalizację pliku .bak). Czy istnieje prosty sposób, aby to zrobić inaczej niż testowanie dowiązań symbolicznych w opakowaniu i odpowiednia obsługa sprawy?

( sedrobi to samo, za co jest tego warte.)

mattdm
źródło
1
Wywołać vim lub emacs (myślę, że oboje podążają za dowiązaniami symbolicznymi)? Poważnie, obawiam się, że odpowiedzią jest ponowne zaimplementowanie -p -iskryptu.
Gilles „SO - przestań być zły”,
Sed w rzeczywistości nie robi tego samego, przynajmniej nie od wersji 4.2.1, wydanej w czerwcu 2009 roku. Musisz dodać opcję --follow-symlinks do edycji, aby edytować plik, do którego prowadzi link, zamiast blokować dowiązanie symboliczne ; Zakładam, że zostało to zrobione, aby uniknąć uszkodzenia istniejących skryptów, które mogą zależeć od starego zachowania.
Garrett,

Odpowiedzi:

6

Zastanawiam się, czy małe spongenarzędzie ogólnego zastosowania („wchłanianie standardowych danych wejściowych i zapisywanie do pliku”) z moreutils będzie pomocne w tym przypadku i czy będzie podążało za dowiązaniem symbolicznym.

Autor opisuje sponge tak:

Rozwiązuje problem edycji plików w miejscu za pomocą narzędzi uniksowych, mianowicie jeśli przekierujesz dane wyjściowe do pliku, który próbujesz edytować, przekierowanie zacznie obowiązywać (usuwanie zawartości pliku) przed pierwszym poleceniem w potoku przechodzi do czytania z pliku. Przełączniki to sed -ilub perl -iomijają, ale nie każde polecenie, którego możesz chcieć użyć w potoku, ma taką opcję, a i tak nie możesz użyć tego podejścia w potokach z wieloma poleceniami.

Zwykle używam gąbki trochę tak:

sed '...' file | grep '...' | sponge file
imz - Ivan Zakharyaschev
źródło
Hej spoko, to działa. Podejrzewam, że powyższy komentarz Gillesa jest „prawdziwą” odpowiedzią, ale ponieważ nie udzielił jej jako odpowiedzi, a ponieważ nauczyłem się nowego narzędzia, wezmę to. :)
mattdm
Ale czy przetestowałeś spongetakie zastosowanie w praktyce? Co do mnie: jeszcze nie. Czy mógłbyś zostawić komentarz stwierdzający, czy zachował się on w teście tak, jak chciałem tutaj? Och, widzę komentarz. Dziękuję za potwierdzenie!
imz - Ivan Zakharyaschev
- tak, wypróbowałem to przed udzieleniem odpowiedzi. Gdy filew powyższym przykładzie znajduje się dowiązanie symboliczne, łącze jest pozostawione w spokoju, a rzeczywisty plik ulega zmianie.
mattdm
@mattdm: Tak, dziękuję za potwierdzenie! (Zauważyłem, że twoje słowa „to działa” nieco później niż napisałem swój komentarz.)
imz - Ivan Zakharyaschev
3

Jeśli wiesz, że somefilejest to dowiązanie symboliczne, możesz jawnie podać pełną ścieżkę do pliku w następujący sposób:

perl -pi.bak -e 's/foo/bar/' $(readlink somefile)

W ten sposób oryginalne dowiązanie symboliczne pozostaje nienaruszone, ponieważ zastępowanie odbywa się teraz bezpośrednio z oryginalnym plikiem.

IDDQD
źródło
Ale wynik readlinkmoże być nadal innym dowiązaniem symbolicznym. Najlepiej byłoby użycie -flub -egdzie dostępne lub zsh„s $file:Plub (:P)glob kwalifikator jak pokazano @ikegami dostać dowiązaniem wolna drogę.
Stéphane Chazelas,
3

Jeśli masz do czynienia z jednym plikiem, komenda wyglądałaby następująco:

perl -i -pe'...' -- "$qfn"

Rozwiązanie jest następujące:

perl -i -pe'...' -- "$( readlink -e -- "$qfn" )"

To obsługuje zarówno dowiązania symboliczne, jak i nie-dowiązania symboliczne.


Jeśli masz do czynienia z dowolnie dużą liczbą plików, polecenie wyglądałoby następująco:

... | xargs -r perl -i -pe'...' --

Rozwiązanie jest następujące:

... | xargs -r readlink -ze -- | xargs -r0 perl -i -pe'...' --

To obsługuje zarówno dowiązania symboliczne, jak i nie-dowiązania symboliczne.


Zastrzeżenie readlink nie jest standardowym poleceniem, więc jego obecność, argumenty i funkcjonalność różnią się w zależności od systemu, więc sprawdź swoją dokumentację. Na przykład niektóre implementacje mają -f(których można również użyć tutaj), ale nie -eniektóre, żadne z nich. busybox„s readlink -fzachowuje się jak GNU readlink -e. Niektóre systemy mają realpathpolecenie, zamiast readlinkktórego ogólnie zachowuje się jak GNU readlink -f.

Uwaga: w GNU readlink -edowiązania symboliczne, których nie można rozpoznać w istniejącym pliku, są po cichu usuwane.

ikegami
źródło
@ Stéphane Chazelas, $var:Pnie działa dla mnie. ( X='tmp' zsh -c 'perl -E"say for @ARGV" $X:P'wyniki tmp:P)
ikegami,
potrzebujesz Zsh 5.3 (2016) lub nowszego dla $var:P. W starszych wersjach zawsze możesz użyć $var:Azamiast tego. Zobacz instrukcję różnic i na stronie zsh.org/mla/users/2016/msg00593.html uzasadnienie wprowadzenia :P(prawdziwy realpath()interfejs).
Stéphane Chazelas
( :Adodano w 4.3.10 (2009), GNU readlink -zw 2012 roku, nie znam żadnej innej readlinkimplementacji, która obsługuje -z). Zauważ, że w GNU xargszazwyczaj potrzebujesz tej -ropcji, chyba że możesz zagwarantować, że dane wejściowe nie będą puste. Tutaj nie jest to wielka sprawa (chyba że perlkod zawiera instrukcję BEGIN/ END), po prostu dostaniesz -i used with no filenames on the command line, reading from STDIN.ostrzeżenie, jeśli tak się stanie.
Stéphane Chazelas
Ups, źle odczytałem -rdokumentację. Myślałem, że zrobiłem coś przeciwnego. Gotowy
ikegami,