Łamanie twardego linku na miejscu?

13

Trzymam moje pliki dot pod kontrolą wersji, a wdrażanie ich przez skrypt tworzy twarde linki. Używam również, etckeeperaby poddać moją /etckontrolę wersji. Ostatnio otrzymałem takie ostrzeżenia:

warning: hard-linked files could cause problems with bzr

Prosta kopia ( cp filename.ext filename.ext) nie będzie działać:

cp: `filename.ext' and `filename.ext' are the same file

Zmiana nazwy / przeniesienie pliku - z wyjątkiem woluminów - również nie przerywa twardego łącza.

Moje pytanie brzmi zatem: czy istnieje sposób na zerwanie twardego łącza do pliku bez konieczności dowiedzenia się, gdzie znajdują się inne twarde łącze / pliki tego pliku?

0xC0000022L
źródło
2
Komenda „rm” przerywa twarde linki.
Johan

Odpowiedzi:

14
cp -p filename filename.tmp
mv -f filename.tmp filename

Czyniąc go skryptowalnym:

dir=$(dirname -- "$filename")
tmp=$(TMPDIR=$dir mktemp)
cp -p -- "$filename" "$tmp"
mv -f -- "$tmp" "$filename"

Wykonanie kopii najpierw, a następnie przeniesienie jej na miejsce ma tę zaletę, że plik atomowo zmienia się z twardego łącza w oddzielną kopię (nie ma momentu, w którym filenamejest częściowy lub brakujący).

Gilles „SO- przestań być zły”
źródło
7

Prawdopodobnie masz na myśli to, że chcesz podzielić twardy link na osobny, niezależny plik.

mv hardlink tempname && cp tempname hardlink && rm tempname

Hardlink to połączenie między wpisem w katalogu a blokiem i-węzła na dysku.

i-węzły przechowują metadane plików, aw przypadku małych plików niektóre systemy plików przechowują dane w i-węzle, w przeciwnym razie wskaźniki do bloków danych, aw przypadku bardzo dużych plików pośrednie i podwójnie pośrednie listy wskaźników do jednostek alokacji dysku.

Niezależnie od tego połączenie między nazwą pliku (która jest wynikiem polecenia ls) a blokiem i-węzła, który przechowuje te metadane, nazywa się twardym łączem.

Posiadanie wielu twardych dowiązań do jednego pliku oznacza ten sam i-węzeł, do którego odwołuje się więcej niż jedna pozycja katalogu, być może w różnych katalogach (w jednym systemie plików)

rm usuwa wpis nazwy pliku z katalogu. Gdy do i-węzła nie będą się już odwoływać żadne pliki, jego przestrzeń zostanie zwolniona do wykorzystania przez inne pliki.

Johan
źródło
W rzeczy samej. To jest to, co cpi mvprzykłady domniemanych. Rozumiem, więc nie ma mowy o używaniu pliku tymczasowego.
0xC0000022L
@ 0xC0000022L, Nie, i-węzły nie zawierają wskaźników do innych i-węzłów. Tylko do bloków danych (lub mogą one podwoić się jako przestrzeń danych, jeśli obiekt jest mały).
vonbrand,
4
Upewnij się, że uprawnienia i inne dane są zachowane podczas kopiowania, tj. use cp -a(przynajmniej coreutils GNU).
vonbrand
@ 0xC0000022L, jeśli przyjrzysz się uważnie, istnieje tylko tymczasowa nazwa oryginalnego pliku, nowy plik jest tworzony tylko w ostatnim kroku.
vonbrand
@vonbrand Mogą rzeczywiście, w zależności od używanego systemu plików.
Johan
4

Umieść to na końcu pliku ~ / .bashrc.

delink () { tmpfile="$1$(date)"; cp -a "$1" "$tmpfile"; mv "$tmpfile" "$1"; }

Uruchom to w ten sposób

delink filename
Rucent88
źródło
3

Najlepszym sposobem, aby to zrobić za pomocą skryptu bash, byłoby coś takiego:

if [ -f "$1" ] ; then
dir="$(dirname -- "$1")"
tmpfile="$(mktemp --tmpdir="$dir")"
cp --preserve=all -f -- "$1" "$tmpfile"
mv -f -- "$tmpfile" "$1"
fi

punkty do odnotowania:

  • sprawdź, czy plik jest zwykłym plikiem, zanim spróbujesz go skopiować
  • trzymaj stary plik na miejscu, dopóki kopia nie będzie gotowa
  • służy mktempdo generowania pliku, który z pewnością nie istnieje
  • Użyj, -faby wymusić nadpisanie i --preserve=allzachować metadane możliwie najbardziej zbliżone do oryginalnego pliku
  • użyj --i, "aby zacytować ścieżki zawierające spacje i / lub zaczynające się od-

Wykonanie zamiany bez tworzenia pliku tymczasowego nie jest możliwe w przypadku bieżących wywołań systemowych linux (3.16): chociaż możliwe jest zastąpienie pliku atomowo (tj. Usunięcie starego pliku i zastąpienie go nowym jako pojedynczą operacją), to nie jest to możliwe w przypadku pliku, który nie ma nazwy w systemie plików (tzn. plik tymczasowy utworzony przy użyciu O_TMPFILEflagi openfunkcji), ponieważ renamefunkcja wymaga nazwy pliku jako danych wejściowych (nie ma wersji, renamektóra przyjmuje jako dane wejściowe deskryptor pliku - zobacz tutaj szczegóły)

pqnet
źródło
1
Pamiętaj, że nie podałeś nazwy w swoim dirnamei mktemppołączeniach. Naprawiono to dla ciebie ...
derobert
@derobert oh dziękuję, ale to nie zadziała, ponieważ są zagnieżdżone podwójne cudzysłowy ... potrzebuję kolejnej poprawki! Trochę nienawiści bash
pqnet
3
Będzie działać z powodu $( ... )zastąpienia polecenia -style. Jednym z powodów jest ładniejszy niż ` ... `styl.
derobert
@derobert miło, nie wiedziałem o tym. W jaki sposób możesz używać `wewnątrz wbudowanych znaczników kodu?
pqnet,
Możesz uciec przed nimi za pomocą ukośników odwrotnych. Więc `żebyś wprowadził: `\``(oczywiście, podwójnie uniknąłem tego, aby pokazać ci, co piszesz).
derobert
-1

Polecenie, którego szukasz, to unlink

Jenny D.
źródło
Być może moje pytanie nie było jasne, ale przez „na miejscu” i przykłady z cpi mvchciałem wyjaśnić, że chcę, aby plik istniał później.
0xC0000022L
Ach, nie uważałem tego za jasne, nie. Zatem powinieneś iść z odpowiedzią Johana.
Jenny D.
1
Polecenie „unlink” po prostu usuwa plik (co robi wywołanie systemowe unlink ()).
Raúl Salinas-Monteagudo
-1

Jeśli szukasz całej nazwy pliku, która jest dowiązaniem twardym do tego pliku, możesz użyć:

find -samefile myknowhardlinkfile

również ls -il myknowhardlinkfilepokaże liczbę pliku hardlinked do tego samego węzła (trzecie pole).

101612442 -rw-rw-r--. 2 me me 0 Aug  5 07:07 myknowhardlinkfile
Jean-Pierre Laurin
źródło
To nie odpowiada na pytanie, chociaż może być przydatne.
Flimm,