Hoe werkt de computer TrashCan?
Even een korte uitleg hoed een TrashCan op de computer werkt (andere namen: Trash, Recycle Bin, BitBucket, etc):
De betreffende objecten (bestanden/directories) die je wilt verwijderen worden simpel weg verplaatst naar een specifieke directory op jouw computer.
Dit kan een standaard of veel gebruikte directory zijn, welke wel of niet door het besturingssysteem beheert wordt.
Afhankelijk van het besturingssysteem, wordt er ook nog bijgehouden waar het betreffende item oorspronkelijk vandaan komt, zodat de gebruiker het eenvoudiger terug kan zetten.
Security – Een TrashCan moet prive zijn …
Voor veiligheid en privacy moet een TrashCan uiteraard beschermd worden en dus alleen toegankelijk zijn voor de betreffende gebruiker.
Je zou niet willen dat iemand door jouw afval emmer snuffelt en ineens tegen prive zaken aanloopt die je eigenlijk niet publiek wilde hebben.
TrashCan wordt niet gebruik in Shells, Terminals, etc …
De TrashCan wordt vaak alleen gebruikt in de grafische gebruikers interface.
Het verwijderen van bestanden in een Shell of Terminal zal zelden gebruik maken van de TrashCan.
Ad Blocking Gedetecteerd Vriendelijk verzoek om te overwegen Ad Blocking uit te zetten voor onze website.
We zijn afhankelijk van inkomen uit Advertenties om de website te kunnen draaien.
Je kunt ons ook op andere manieren ondersteunen (zie Ondersteun ons links bovenin).
Items in macOS naar de Trash brengen
Met recente macOS versies, kun je de TrashCan in de home directory van de gebruiker terug vinden, en wel hier: ~/.Trash
We zouden bestanden en directories natuurlijk handmatig kunnen verplaatsen maar dat hoeft niet. macOS heeft hiervoor een mooie functie: recycleURLs:completionHandler:.
Om hier mee te kunnen werken moeten we wel de benodigde units toevoegen en de Free Pascal compiler laten weten dat we naar de ObjectiveC mode moeten omschakelen.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| {$modeswitch ObjectiveC1}
{$linkframework CoreFoundation}
interface
uses
... MacOSAll, CocoaAll ...
implementation
function MoveToTrash(FileOrDirName:AnsiString): boolean;
var
aNSArray : NSMutableArray;
aNSURL : NSURL;
begin
aNSArray := NSMutableArray(NSMutableArray.array_).init; // How can I create a new array?
aNSURL := NSURL.fileURLWithPath(NSSTR(pchar(FileOrDirName)));
aNSArray.addObject(aNSURL);
NSWorkspace.sharedWorkspace.recycleURLs_completionHandler(aNSArray,nil);
Result:=true; // recycleURLs doesn't return a result unless we make a function for it.
end; |
Let op: Deze functie levert altijd een TRUE als retourwaarde …
Omdat de recycleURLs functie geen TRUE of FALSE terug geeft, en in plaats daarvan een speciale callback functie zou willen gebruiken, hebben we dat voor eenvoudigheid redenen hier gewoon overgeslagen.
Het achteraf controleren of bestand of directory inderdaad weg is, is geen goede optie omdat de recycleURLs functie asynchroon werkt, en het bestand dus mogelijk pas later weg haalt.
Items naar de Windows Recycle Bin
Windows, omdat het een Microsoft product is, heeft de locatie van de Recycle Bin directory de jaren al vaker veranderd (dit CCleaner artikel geeft dat duidelijk weer).
Als voorbeeld:
C:\$Recycle.Bin
voor Windows Vista, en niewer,
C:\recycler
voor Windows 2000, NT, en XP, of
C:\recycled
voor Windows 95 en 98.
Ik heb gemerkt dat C:\$Recycle.Bin
onder Windows 10 ook niet handig werkt, dus daar hebben we ook niks aan.
Gelukkig bestaat er onder Windows een API die dat voor ons kan regelen, en een mooie functie die ik in het Lazarus Forum vond, van de gebruiker aSerge vult dit helemaal voor ons in.
Het gebruikt de functie SHFileOperation van de Windows ShellAPI en ondersteund bestanden en directories. We moeten dus we de “ShellApi” unit aan onze uses clausule toevoegen.
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| uses ... ShellApi ...
...
function MoveToTrash(const FileOrDirName: UnicodeString): Boolean;
var
R: TSHFILEOPSTRUCTW;
begin
FillChar(R, SizeOf(R), 0);
R.wFunc := FO_DELETE;
R.pFrom := PWideChar(FileOrDirName + #0);
R.fFlags := FOF_ALLOWUNDO or FOF_NOCONFIRMATION;
Result := SHFileOperationW(@R) = 0;
end; |
Ad Blocking Gedetecteerd Vriendelijk verzoek om te overwegen Ad Blocking uit te zetten voor onze website.
We zijn afhankelijk van inkomen uit Advertenties om de website te kunnen draaien.
Je kunt ons ook op andere manieren ondersteunen (zie Ondersteun ons links bovenin).
Items naar de Linux TrashCan
Linux heeft helaas geen standaard locatie of API functie die ons snel even helpt. Er bestaat echter wel een officiële TrashCan specificatie welke de meeste moderne Linux distros wel volgen.
Linux biedt echter veel vrijheid, dus niet iedere Linux versie doet het op dezelfde manier … helaas is dat de achillespees van Linux.
Linux TrashCan directory locatie
De eerder genoemde specificaties stellen dat men de environment variable “$XDG_DATA_HOME” moet uitlezen, maar ik al gemerkt dat die bij een aantal Linux versie gewoon niet is gedefinieerd.
Bij de verschillende Linux en FreeBSD varianten gebruikt men echter wel vaak ~/.local/share/Trash/
.
Als deze directory gebruikt wordt, dan zie je daar vaak twee sub-directories in:
– ~/.local/share/Trash/files/
waar het verwijderde bestand of directory geplaatst wordt.
– ~/.local/share/Trash/info/
waar een referentie document wordt gezet zodat het systeem kan zien waar het bestand oorspronkelijk vandaan komt.
Maar helaas, zoals Linux eigen, niet iedereen houdt zich daaraan, en andere locaties kunnen ook zijn:
– ~/.local/share/Trash/
,
– ~/Desktop/Trash/
,
– ~/trash
,
– ~/Trash
,
– ~/.trash
,
– ~/.Trash
Als iemand er nog meer weer: plaats hieronder een opmerking zodat ik ze aan de lijst kan toevoegen.
Voor de Linux versie van de MoveToTrash functie, heb ik daarom de volgende aanpak gekozen:
- Als
~/.local/share/Trash/
bestaat, gebruik deze dan voor het verwijderde bestand/directory en het referentie document.
- Mocht deze directory niet bestaan, kijk dan of de alternatieve directories bestaat, en als dat het geval is verplaatsen we alleen bestand/directory en maken we geen referentie document.
- Mochten beide stappen falen, dan maken we
~/.local/share/Trash/
aan en gaan we de standaard volgen (dus optie 1).
Ik kan me voorstellen dat iemand het helemaal niet eens is met deze aanpak: ook hier weer – post een opmerking hieronder. Ik leer ook graag nieuwe dingen .
Dus als de standaard directory ~/.local/share/Trash/
bestaat, of als de alternatieve directories niet bestaan, dan verplaatsen we bestanden ~/.local/share/Trash/files/
en maken we een referentie document in ~/.local/share/Trash/info/
.
Als de standaard directory niet bestaat, maar we hebben we een alternatieve directory gevonden, dan verplaatsen we betreffende bestand of directory daarheen, maar maken we geen referentie bestand.
Omdat het kan zijn dat een bestand met dezelfde naam al in de Trash staat, moeten we soms een bestand een nummer geven, zodat het niet een eerder verwijderd bestand overschrijft.
Dit geldt voor de bestanden/directories in de “files” directory en de “info” directory.
De nummering volgt dit patroon: voorbeeld.doc, voorbeeld.2.doc, voorbeeld.3.doc, etc.
Je ziet dat “.1” wordt weggelaten.
Linux TrashCan Referentie Document (.trashinfo)
In mijn functie, indien we de standaard volgen ( ~/.local/share/Trash/
), maken we een referentie document aan.
Voor de alternatieve (niet-standaard) directories slaan we dat over.
Het referentie bestand wordt aangemaakt in “~/.local/share/Trash/info/” waarin originele bestandsnaam en verwijder datum staan opgeslagen.
De naam van het referentie bestand is hetzelfde als die van de verwijderde directory of bestand en wordt opgeslagen in “~/.local/share/Trash/files/” met “.trashinfo” achter de naam (inclusief nummer indien van toepassing).
Merk op: de naam van het originele bestand wordt met escape characters geschreven, dus b.v. een psatei wordt een “%20”, zodat we dat ook bij URLS zien.
De inhoud van het referentie bestand ziet er ongeveer zo uit:
1 2 3
| [Trash Info]
Path=/path/to/original/location/deletefile.extension
DeletionDate=2020-01-09T17:05:41 |
Niet alle Linux versies letten op het .trashinfo bestand!
Hoewel .trashinfo bestanden zouden moeten helpen met het herstellen van een verwijderd bestand, doet niet iedere Linux versie hier iets mee.
Als voorbeeld: LinuxMint 19 (Cinamon) negeert dit compleet en lijkt een eigen formaat te gebruiken in “gvfs-metadata”.
Helaas kan ik niet alle custom oplossing in mijn unit plaatsen, en het bestandsformaat is soms exotisch, dus de gebruikers van dergelijk Linux versie zullen bestanden zelf uit de Trash moeten slepen en geen handige “restore” functie hebben.
Linux TrashCan Functie in Pascal
Omdat we dus geen handige API call hebben, ga ik als volgt te werk:
- Probeer of we bestand of directory met een “rename” kunnen verplaatsen (werk niet voor elk besturingssysteem).
- Mocht stap 1 falen, dan proberen we bestand of directory te kopiëren we het eerst naar de TrashCan.
- Mocht stap 2 ook falen, dan verwijderen we de kopie uit de TrashCan en geven we aan dat het verplaatsen naar de TrashCan faalde (FALSE).
- Mocht stap 2 echter goed zijn gegaan, dan verwijderen we daarna het origineel.
Uiteraard maakt dit alles een beetje complexer dan de handige Windows of macOS functies:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153
| uses
... DateUtils, LCLProc, LazFileUtils, FileUtil ...
...
function MoveToTrash(FileOrDirName:AnsiString): boolean;
var
TrashCanBase : string = '~/.local/share/Trash/';
TrashCanFiles : string = '~/.local/share/Trash/files/';
TrashCanInfo : string = '~/.local/share/Trash/info/';
TrashCanAlt : string;
InfoFile : TStringList;
isDirectory : boolean;
newFileOrDirName : string;
NamePart1, NamePart2 : string;
FilesInDirectory : TStringList;
counter : integer;
function EscapeURLString(s: string): string;
var
i: integer;
source: PAnsiChar;
begin
Result := '';
source := pansichar(s);
for i := 1 to length(source) do
if not (source[i - 1] in ['A'..'Z', 'a'..'z', '0'..'9', '-', '_', '~', '.', ':', '/']) then
Result := Result + '%' + IntToHex(Ord(source[i - 1]), 2)
else
Result := Result + source[i - 1];
end;
begin
if RightStr(FileOrDirName,1)=DirectorySeparator then // remove directory separator if this is the last char
FileOrDirName := LeftStr(FileOrDirName,Length(FileOrDirName)-1);
// Are we moving a directory into the trashcan?
isDirectory := DirectoryExists(FileOrDirName);
TrashCanFiles := ExpandFileName(TrashCanFiles);
TrashCanInfo := ExpandFileName(TrashCanInfo);
TrashCanBase := ExpandFileName(TrashCanBase);
if not DirectoryExists(TrashCanBase) then // We didn't find the standard dir, so now we are guessing alternatives
begin
TrashCanAlt := '';
if DirectoryExists(ExpandFileName('~/Desktop/Trash/')) then
TrashCanAlt:=ExpandFileName('~/Desktop/Trash/')
else if DirectoryExists(ExpandFileName('~/trash/')) then
TrashCanAlt:=ExpandFileName('~/trash/')
else if DirectoryExists(ExpandFileName('~/Trash/')) then
TrashCanAlt:=ExpandFileName('~/Trash/')
else if DirectoryExists(ExpandFileName('~/.trash/')) then
TrashCanAlt:=ExpandFileName('~/.trash/')
else if DirectoryExists(ExpandFileName('~/.Trash/')) then
TrashCanAlt:=ExpandFileName('~/.Trash/');
if TrashCanAlt='' then // we didn't find an alternative: create standard dirs
begin
ForceDirectories(TrashCanFiles);
ForceDirectories(TrashCanInfo);
end
else // we did find an alternative, make sure we use it for files (and not for trashinfo)
begin
TrashCanFiles := TrashCanAlt;
TrashCanInfo := '';
end;
end;
newFileOrDirName := ExtractFileName(FileOrDirName);
NamePart1 := LazFileUtils.ExtractFileNameWithoutExt(newFileOrDirName);
NamePart2 := ExtractFileExt(newFileOrDirName);
counter := 0;
// if file already in Trashcan, we add a number (eg. example.doc, example.2.doc, expample.3.doc etc.)
while (isDirectory and DirectoryExists(TrashCanFiles+newFileOrDirName)) or
(not(isDirectory) and FileExists(TrashCanFiles+newFileOrDirName)) do
begin
inc(Counter);
newFileOrDirName := NamePart1 + BoolToStr(Counter>1,'.'+IntToStr(Counter),'') + NamePart2;
end;
// Create a trashinfo file, if the standard dir did exist!
if TrashCanInfo<>'' then
begin
InfoFile := TStringList.Create;
InfoFile.Text := '[Trash Info]'+LineEnding+
'Path='+EscapeURLString(FileOrDirName)+LineEnding+
'DeletionDate='+FormatDateTime('YYYY-MM-DD',Now)+'T'+FormatDateTime('hh:nn:ss',Now)+LineEnding; //'2020-01-09T17:05:41'+
InfoFile.SaveToFile(TrashCanInfo+newFileOrDirName+'.trashinfo');
InfoFile.Free;
end;
// make new filename now full path
newFileOrDirName := TrashCanFiles+newFileOrDirName;
// Move File or Directory - try rename first, if that fails try copying files, and if copying worked delete originals
Result := RenameFile(FileOrDirName,newFileOrDirName); // try moving file or dir
if not Result then // if rename failed then try copy and delete (aka move)
begin
if isDirectory then
begin
FilesInDirectory := FindAllFiles(FileOrDirName, '*', true);
for Counter:=0 to FilesInDirectory.Count-1 do // Copy the file structure to the trashcan
begin
if ForceDirectories( ExtractFilePath( StringReplace(FilesInDirectory.Strings[Counter],FileOrDirName,newFileOrDirName,[] ) ) ) then
begin
if not CopyFile( FilesInDirectory.Strings[Counter], StringReplace(FilesInDirectory.Strings[Counter],FileOrDirName,newFileOrDirName,[] ) ) then
begin // failed copying file - cleanup and bail
Result := false;
if DeleteDirectory(newFileOrDirName,true) then // delete what we have copied so far - things are going side ways
RemoveDir(newFileOrDirName);
if FileExists(TrashCanInfo+newFileOrDirName+'.trashinfo') then
DeleteFile(TrashCanInfo+newFileOrDirName+'.trashinfo');
Exit;
end;
end
else // failed creating dir - cleanup and bail
begin
Result := false;
if DeleteDirectory(newFileOrDirName,true) then // delete what we have copied so far - things are going sideways
RemoveDir(newFileOrDirName);
if FileExists(TrashCanInfo+newFileOrDirName+'.trashinfo') then
DeleteFile(TrashCanInfo+newFileOrDirName+'.trashinfo');
Exit;
end;
end;
// all went well, remove old dir
if DeleteDirectory(FileOrDirName,true) then
Result := RemoveDir(FileOrDirName);
FilesInDirectory.Free;
end
else
begin
if CopyFile(FileOrDirName,newFileOrDirName,true,false) then // If rename fails: copy file
Result := DeleteFile(FileOrDirName) // If copy succeeded: remove original
else
Result := false; // Copy/Delete failed d
end;
end;
end; |
Ik kon me niet voorstellen dat zo’n functie nog niet bestond, maar ik kon er geen vinden.
Ik heb daarom mijn unit hier beschikbaar gemaakt, hopelijk is het handig voor anderen om onder Windows, Linux en macOS, bestanden of directories naar de TrashCan te verplaatsen (ookwel: Recycle Bin, Bit Bucket, etc).
Ik kan me goed voorstellen dat er ruimte voor verbetering is en suggesties zijn zeer welkom.
Hier kun je de hele unit downloaden:
Download - Lazarus-Pascal-TrashCanUnit.zip
Bestandsnaam: |
Lazarus-Pascal-TrashCanUnit.zip |
Platform: |
Undefined |
Versie: |
1.0 |
Omvang: |
3.5 kB |
Datum: |
2020-01-11 |
Download Nu
Stuur me Koffie
|
Reacties
Er zijn nog geen reacties geplaatst.
Je kunt jouw eigen opmerkingen plaatsen m.b.v. dit formulier, of een reactie op een bestaande opmerking plaatsen door op de "Beantwoorden" knop te klikken.