Jak odbywa się modyfikacja pliku w miejscu?

10

Co oznacza modyfikacja pliku „na miejscu”, np. Za pośrednictwem sed -ilub perl -i?
Moje pytanie dotyczy sposobu wykonania tej modyfikacji w miejscu. Czy plik został skopiowany, modyfikacja jest wykonywana w kopii, a następnie zastępowana jest oryginalna? A może oryginalny plik jest jakoś modyfikowany?

Jim
źródło
Szczegółowe informacje na ten temat można znaleźć na stronie backreference.org/2011/01/29/in-place-editing-of-files .
scy
W tym przypadku, jak to się robi z exlub vi?
Wildcard
@Wildcard - każdy z nich ma cały system. exutrzymuje plik poczty (jak dead.maillub coś w ~ tobie, i zwykle inny gdzieś w pobliżu bufora poczty) . sprawdź specyfikację - każda z nich ma zdefiniowany stan do dużych długości ... exw większości przypadków ma swój własny format binarny (spójrz na -rescueplik) i służy to do prezerowania oddzielnych plików tymczasowych buforów (być może nawet sześciu) . więc czy kopiują bloki danych wejściowych do edycji buforów i synchronizują zapisy do przesunięć na zmiany :!written?
mikeserv

Odpowiedzi:

18

sed tworzy plik tymczasowy, zapisuje dane wyjściowe w tym pliku, a następnie zmienia nazwę pliku tymczasowego na górną część oryginału.

Możesz obserwować, co się dzieje, używając strace:

$ strace -e trace=file sed -i -e '' a
execve("/usr/bin/sed", ["sed", "-i", "-e", "", "a"], [/* 34 vars */]) = 0
<...trimmed...>
open("a", O_RDONLY)                     = 3
open("./sedxvhRY8", O_RDWR|O_CREAT|O_EXCL, 0600) = 4
rename("./sedxvhRY8", "a")              = 0
+++ exited with 0 +++

Rejestruje wszystkie operacje na plikach sed: tworzy nowy plik (bezpiecznie z O_CREAT|O_EXCL), zapisuje w nim dane, a następnie przenosi go z powrotem na wierzch mojego oryginalnego pliku a.

sed -iakceptuje sufiks do użycia jako kopii zapasowej, w takim przypadku najpierw usuwa oryginał (zamiast zmiany nazwy na wierzch). Argument ten jest obowiązkowy w większości BSD sed. W takim przypadku w katalogu nie ma w ogóle pliku o właściwej nazwie.

perl w najnowszych wersjach otwiera plik wejściowy, a następnie usuwa go i tworzy nowy plik o tej samej nazwie:

open("a", O_RDONLY)               = 3
unlink("a")                       = 0
open("a", O_WRONLY|O_CREAT|O_EXCL, 0600) = 4

Po usunięciu ( unlink) pliku, który już otworzyłeś, masz do niego dostęp tak długo, jak długo trzymasz uchwyt, aby mógł nadal odczytywać dane z usuniętego pliku. W ten sposób perlpisze bezpośrednio do pliku wyjściowego, zamiast do pliku tymczasowego: żaden dodatkowy plik jest tworzony, ale jeśli odczytać pliku podczas procesu dostaniesz częściowe treści, w przeciwieństwie do sed„s podejście. Jest też krótki czas, kiedy nie ma pliku o właściwej nazwie, który jest na początku procesu, a nie na końcu (jak w sed -i .bak).


Zarówno sedi perlbędzie:

  • Zamień dowiązanie symboliczne na zwykły plik.
  • Zerwać twarde linki.
  • Jeśli to możliwe, zachowaj własność grupy.
  • Utwórz plik ze swoją domyślną grupą (lub grupą katalogu nadrzędnego, jeśli ten katalog ma setgidbit), jeśli był własnością grupy, w której nie jesteś i nie jesteś rootem.
  • Zachowaj własność pliku, jeśli jesteś rootem.
  • Zachowaj podstawowe uprawnienia.
  • Zachowaj setuidi setgrpbity, jeśli wynikowa grupa jest taka sama jak grupa, w której zaczął.
  • Zachowaj lepki kawałek.
  • Nie zachowuj xattrs.

sed będzie:

  • Zachowaj listy ACL (w systemie Linux; nie wiem o innych) .

perl będzie:

  • Nie zachowuj list ACL.

Powyższe dotyczy prawdy w systemie Linux z GNU sedi Mac OS X z (pochodzącym z FreeBSD) sed.

Michael Homer
źródło
3

Oprócz odpowiedzi na @ Homer od perldoc perlrun:

określa, że ​​pliki przetwarzane przez konstrukcję „<>” mają być edytowane w miejscu. 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, zgodnie z następującymi regułami:

Jeśli nie podano rozszerzenia, kopia zapasowa nie jest tworzona, a bieżący plik jest zastępowany.

Jeśli rozszerzenie nie zawiera *, jest dołączane na końcu bieżącej nazwy pliku jako przyrostek. Jeśli rozszerzenie zawiera co najmniej jeden * znak, wówczas każdy * jest zastępowany bieżącą nazwą pliku.

I pamiętaj, że nie zostaje zachowany żaden link miękki lub link twardy:

Pamiętaj, że ponieważ -i zmienia nazwę lub usuwa oryginalny plik przed utworzeniem nowego pliku o tej samej nazwie, miękkie i twarde łącza w stylu UNIX nie zostaną zachowane.

Wreszcie przełącznik -i nie utrudnia wykonywania, gdy w wierszu poleceń nie podano żadnych plików. W takim przypadku nie jest wykonywana kopia zapasowa (nie można oczywiście ustalić oryginalnego pliku), a przetwarzanie przechodzi ze STDIN do STDOUT, jak można się spodziewać.

Wyjaśnia to również, dlaczego należy używać -iz -popcją lub użyć jawnej printinstrukcji, jeśli chcesz edytować w miejscu za pomocą perl:

# Opps, file will be truncated, becomes empty
$ perl -i.bak -ne 's/123/qwe/' file

# Right way
$ perl -i.bak -ne 's/123/qwe/;print' file

# Or
$ perl -i.bak -pe 's/123/qwe/' file
Cuonglm
źródło