Na początek kod z książki:
fname = "/writable/file/path/example_file"
if not os.path.isfile(fname):
f = open(fname, "w")
I teraz tak: zacznę od tego, że w zasadzie książce napisałem jak to zrobić poprawnie na stronie 353 w rozdziale o systemie plików (rozdział 10). Ale przykład dotyczył C i uprawnień do plików, i w sumie nigdzie w rozdziale o synchronizacji nie wspominam, żeby rzucić na niego okiem - mój błąd.
(Tak na marginesie, to ten kod i tak by się wysypał jeśli istniałby katalog o takiej nazwie; isfile wtedy zwróci False, open spróbuje utworzyć plik, ale to się nie powiedzie. Tak btw, z mojej pracy przy analizie malware pamiętam, że jedna ze sztuczek, która używałem, żeby malware nie tworzył ponownie plików w danym miejscu, było właśnie utworzenie katalogu o takiej samej nazwie - pliki malware usuwał, ale do usunięcia katalogu często trzeba użyć innego API, więc malware się wykładał - taka tam sztuczka, którą warto czasem pamiętać)
Konkretniej, napisałem tam, że trzeba obie operacje sprowadzić do jednej atomowej (tj. nie tyle chodzi o zmniejszenie okna czasu pomiędzy operacjami, co o całkowite jego usunięcie). W przypadku tworzenia plików trzeba o coś takiego poprosić system operacyjny (a konkretniej: jądro systemu lub API systemowe, które i tak poprosi jądro systemu) o "utworzenie pliku tylko jeśli ten nie istnieje".
By the way...
On 22nd Nov'24 we're running a webinar called "CVEs of SSH" – it's free, but requires sign up: https://hexarcana.ch/workshops/cves-of-ssh (Dan from HexArcana is the speaker).
W przypadku systemów GNU/Linux służy do tego syscall/funkcja open z parametrami:
• O_CREAT - utworzenie pliku, jeśli ten nie istnieje.
• O_EXCL - upewnienie się, że plik naprawdę zostanie utworzony (jeśli już istnieje, open zwróci błąd).
Na przykład, cytując kod w języku C ze strony 353:
int fd = open("/tmp/knownname",
O_CREAT | // Utwórz nowy plik, jeśli nie istnieje.
O_EXCL | // Upewnij się, że na pewno zostanie
// utworzony nowy plik (tj. plik nie istniał
// wcześniej).
O_WRONLY, // Otwarcie tylko do zapisu.
0600 // S_IRUSR | S_IWUSR - czyli rw-------
);
if (fd == -1) {
perror("Failed to create file");
return 1;
}
Jeśli chodzi o to jak to zrobić w Pythonie, to w zasadzie trzeba skorzystać z tego samego API (działa zarówno pod Windowsem jak i pod Linuxem, chociaż szczerze, to nie sprawdzałem dokładnie jak pod Windowsem jest zaimplementowane), a następnie posiadając deskryptor w postaci numerycznej, utworzyć na jego podstawie obiekt typu file znany z Pythona. Realizuje się to w następujący sposób:
>>> import os, io
>>> d = os.open("/tmp/thisfiledoesnotexist", os.O_CREAT | os.O_EXCL | os.O_WRONLY, 0o600)
>>> f = io.FileIO(d, 'w')
>>> f.write('ala ma kota')
11L
>>> f.close()
>>> print open("/tmp/thisfiledoesnotexist").read()
ala ma kota
>>>
Jeśli plik by już istniał, to os.open rzuci wyjątek:
• Python 2.7: OSError: [Errno 17] File exists: ...
• Python 3.4: FileExistsError: [Errno 17] File exists: ...
I tyle.
P.S. W końcu jakiś post techniczny! YAY! :)
P.S.2. Pewnie co jakiś czas będę wrzucać posty bazujące na pytaniach o to co tam w książce napisałem a było w jakiś sposób niejasne/niekompletne - trochę takich pytań dostałem, więc materiał jest.
Comments:
f.write(b'ala ma kota')
Ogólnie zgoda :)
Warto dodać, że są przypadki kiedy plik musi zostać otwarty dłużej - wtedy context manager raczej się nie przyda.
@Qba
Masz rację :)
Testowałem f.write tylko na 2.7 i przyznaję, że założyłem, że FileIO w przypadku "w" zachowa się jak open w przypadku "w", tj. zostanie utworzony plik "tekstowy" (a więc tym str będzie akceptowany i automagicznie kodowany domyślnym kodowaniem, w przeciwieństwie do "wb").
Add a comment: