Dlaczego przekierowanie pliku wyjściowego do samego siebie tworzy pusty plik?

19

Dlaczego przekierowanie pliku wyjściowego do samego siebie tworzy pusty plik?

Podane w Bash, dlaczego?

less foo.txt > foo.txt

i

fold foo.txt > foo.txt

produkować pusty foo.txt? Ponieważ dodatek taki jak less eggs.py >> eggs.pytworzy dwie kopie tekstu w eggs.py, można oczekiwać, że nadpisanie spowoduje utworzenie jednej kopii tekstu.

Uwaga: nie mówię, że to błąd, bardziej prawdopodobne jest, że jest wskaźnikiem czegoś głębokiego w Uniksie.

marynarz
źródło
Rozwiązane w kanonicznym U & L Jakie są operatory kontroli i przekierowania powłoki? pytanie.
Scott

Odpowiedzi:

20

Podczas używania >plik jest otwierany w trybie obcięcia, więc jego zawartość jest usuwana, zanim polecenie spróbuje go odczytać.

Podczas używania >>plik jest otwierany w trybie dołączania, aby istniejące dane zostały zachowane. W tym przypadku nadal jednak dość ryzykowne jest używanie tego samego pliku jako danych wejściowych i wyjściowych. Jeśli plik jest na tyle duży, że nie mieści się w rozmiarze bufora wejściowego do odczytu, jego rozmiar może rosnąć w nieskończoność, dopóki system plików nie zostanie zapełniony (lub osiągnięty zostanie limit miejsca na dysku).

Jeśli chcesz użyć pliku jako danych wejściowych i wyjściowych z poleceniem, które nie obsługuje modyfikacji w miejscu, możesz zastosować kilka obejść:

  • Po zakończeniu użyj pliku pośredniego i zastąp oryginalny plik tylko wtedy, gdy nie wystąpi błąd podczas uruchamiania narzędzia (jest to najbezpieczniejszy i najczęstszy sposób).

    fold foo.txt > fold.txt.$$ && mv fold.txt.$$ foo.txt
  • Unikaj pliku pośredniego kosztem potencjalnej częściowej lub całkowitej utraty danych w przypadku wystąpienia błędu lub przerwy. W tym przykładzie zawartość pliku jest foo.txtprzekazywana jako dane wejściowe do podpowłoki (w nawiasach) przed usunięciem pliku. Poprzedni i-węzeł pozostaje przy życiu, ponieważ podpowłoka utrzymuje ją otwartą podczas odczytu danych. Plik zapisany przez narzędzie wewnętrzne (tutaj fold) o tej samej nazwie (foo.txt) wskazuje na inny i-węzeł, ponieważ stary wpis katalogu został usunięty, więc technicznie istnieją dwa różne „pliki” o tej samej nazwie podczas procesu. Po zakończeniu podpowłoki stary i-węzeł zostaje zwolniony, a jego dane utracone. Uważaj, aby upewnić się, że masz wystarczająco dużo miejsca, aby tymczasowo zapisać zarówno stary plik, jak i nowy w tym samym czasie, w przeciwnym razie stracisz dane.

    (rm foo.txt; fold > foo.txt) < foo.txt
jlliagre
źródło
3
spongez moreutils może również pomóc. fold foo.txt | sponge foo.txt- lub fold foo.txt | sponge !$powinien również zrobić.
slhck
@slhck Rzeczywiście, gąbka też mogłaby to zrobić. Jednak ponieważ nie jest określony przez POSIX ani główny nurt w systemach uniksopodobnych, jest mało prawdopodobne, aby był obecny.
jlliagre
Nie jest jednak tak, że nie można go przedstawić;)
slhck
7

Plik jest otwierany do zapisu przez powłokę, zanim aplikacja będzie mogła go odczytać. Otwarcie pliku do zapisu powoduje jego obcięcie.

Ignacio Vazquez-Abrams
źródło
0

W bash operator przekierowania strumienia ... > foo.txtopróżnia się foo.txt przed oceną lewego operandu .

Można użyć podstawienia polecenia i wydrukować jego wynik jako obejście. To rozwiązanie wymaga mniej dodatkowych znaków niż w innych odpowiedziach:

printf "%s\n" "$(less foo.txt)" > foo.txt

Uwaga: to polecenie nie zachowuje żadnych nowych linii w foo.txt. Więcej informacji znajdziesz w sekcji komentarzy poniżej

W tym przypadku podpowłoka $(...)jest oceniana przed operatorem przekierowania strumienia >, stąd zachowanie informacji.

Louis-Jacob Lebel
źródło
@KamilMaciorowski: Właściwie jest tmp=$(cmd; printf q);  printf '%s' "${tmp%q}". Ale z tą odpowiedzią pominąłeś inny problem: mówi „podpowłoka”, gdy oznacza „zastępowanie poleceń”. Tak, podstawienia poleceń są ogólnie podpowłokami, ale nie odwrotnie, a podpowłoki ogólnie nie pomagają w rozwiązaniu tego problemu.
Scott
@KamilMaciorowski Tak mi przykro, że przegapiłem to wszystko. Dzięki za wskazanie tego wszystkiego. Jeśli chodzi o twój (4) punkt: czy odwrotne cytaty załatwią sprawę, tj. Zachowają końcowe znaki (-y)?
Louis-Jacob Lebel
@Scott dzięki za odpowiedź. Zmieniłem „podpowłokę” na „podstawianie poleceń”. Nawiasem mówiąc, zastanawiam się, jaka jest dokładnie różnica między nimi dwoma.
Louis-Jacob Lebel
Nie, cudzysłowy (backticks) również usuwają końcowe znaki nowego wiersza.
Kamil Maciorowski
W porządku, na razie dodałem komunikat ostrzegawczy. Usunę go, jeśli znajdę rozwiązanie.
Louis-Jacob Lebel