bem0815
Goto Top

Batch in vorletzte Zeile einer Datei schreiben

Moin zusammen,

ich habe ein Script gebastelt, dass für die Personalabteilung die Logins der Mitarbeiter an ihren PC per Autostartscript erfasst, damit die Abteilung einen schnellen Überblick hat welche Mitarbeiter nicht anwesend waren. (Keine Zeiterfassung vorhanden die das regelt).

Das ganze soll die Daten in eine XML schreiben und diese wird dann per Javascript in einer HTML Datei in einer Table ausgegeben.

Die XML soll wie folgt aufgebaut sein:

<?xml version="1.0" encoding="UTF-8"?>  
<DATEN> 
  <DATENSATZ> 
    <MITARBEITER>Max Mustermann</MITARBEITER> 
    <LOGINNAME>m.mustermann</LOGINNAME> 
    <UHRZEIT> 12:34:56</UHRZEIT> 
  </DATENSATZ> 
  <DATENSATZ> 
    <MITARBEITER>Erika Mustermann</MITARBEITER> 
    <LOGINNAME>e.mustermann</LOGINNAME> 
    <UHRZEIT> 01:23:45</UHRZEIT> 
  </DATENSATZ> 
</DATEN> 

Beim ausführen der Batch wird erst mal geprüft ob die Datei bereits existiert, falls nicht werden die ersten beiden Zeilen gesetzt.
Danach wird der Datensatz selbst geschrieben und die XML mit </DATEN> geschlossen.

Nun soll das schließende </DATEN> Tag ja nur einmal in der XML vorkommen und zwar ganz am Schluss.
Wenn ich die Batch aber ausführe wird das logischerweise jedes mal erneut in die Datei geschrieben.

Also so:

<?xml version="1.0" encoding="UTF-8"?>  
<DATEN> 
  <DATENSATZ> 
    <MITARBEITER>Max Mustermann</MITARBEITER> 
    <LOGINNAME>m.mustermann</LOGINNAME> 
    <UHRZEIT> 12:34:56</UHRZEIT> 
  </DATENSATZ> 
</DATEN>
  <DATENSATZ> 
    <MITARBEITER>Erika Mustermann</MITARBEITER> 
    <LOGINNAME>e.mustermann</LOGINNAME> 
    <UHRZEIT> 01:23:45</UHRZEIT> 
  </DATENSATZ> 
</DATEN> 

(Zeile 8 ist hier der unerwünschte Teil)

Was ich nun bräuchte ist entweder die Möglichkeit, dass mir die Batch nicht an das Ende der Datei einen neuen Datensatz schreibt sondern in die Vorletzte Zeile und in dem Fall prüft ob </DATEN> bereits vorhanden ist. Falls ja soll das nicht erneut in die Datei geschrieben werden.

Alternativ würde auch ein löschen der letzten Zeile gehen damit diese dann neu geschrieben werden kann.


Hier noch der aktuelle Batch code:

if not exist "Login-%date%.xml" (  
echo ^<?xml version="1.0" encoding="UTF-8"?^> >> Login-%date%.xml  
echo ^<DATEN^> >> Login-%date%.xml"  
echo   ^<DATENSATZ^> >> Login-%date%.xml)
end

if "%userdomain%" neq "%computername%" set "dom=/domain"  
for /f "tokens=2*" %%i in ('net user "%username%" %dom%^|findstr "Vollst"') do echo     ^<MITARBEITER^>%%j^</MITARBEITER^> >> Login-%date%.xml  
end

echo     ^<LOGINNAME^>%username%^</LOGINNAME^> >> Login-%date%.xml
echo     ^<UHRZEIT^>%time:~0,8%^</UHRZEIT^> >> Login-%date%.xml
echo   ^</DATENSATZ^> >> Login-%date%.xml
echo ^</DATEN^> >> Login-%date%.xml

Kann mir da wer helfen?
Danke face-smile

Gruß Bem

Content-Key: 452705

Url: https://administrator.de/contentid/452705

Printed on: April 19, 2024 at 05:04 o'clock

Member: erikro
erikro May 17, 2019 at 07:18:31 (UTC)
Goto Top
Moin,

weiß Euer Betriebsrat von dem Ansinnen und hat dem zugestimmt?

Ansonsten nimm die Powershell, da die mit XML umgehen kann. Hier ist schön beschrieben, wie das geht:
https://www.langlitz-it.de/?p=1297

hth

Erik
Member: Bem0815
Bem0815 May 17, 2019 updated at 07:58:43 (UTC)
Goto Top
Zitat von @erikro:

Moin,

weiß Euer Betriebsrat von dem Ansinnen und hat dem zugestimmt?

Gibt keinen.

Ansonsten nimm die Powershell, da die mit XML umgehen kann. Hier ist schön beschrieben, wie das geht:
https://www.langlitz-it.de/?p=1297

Hmm möglich, nur bin ich nicht sicher ob das an diesem Punkt noch viel Sinn macht, da das Script ansonsten fast fertig ist.
Wenn ich das jetzt nochmal in PS umschreibe gehen hier wohl nochmal 1-2 Stunden ins Land bis ich das geschrieben habe. z.B. die Syntax zur Ausgabe des Anzeigenamens aus der AD ist natürlich eine ganz andere.

Da das ganze als Loginscript auf allen Rechnern laufen soll hätte ich zusätzlich noch den Nachteil, dass ich bei PS erst mal für alle Kisten das Active Directory-Modul für Windows PowerShell aktivieren darf.

Das es grundsätzlich mehr Sinn macht PS zu benutzen wenn das direkt mit XML umgehen kann will ich hier natürlich nicht bestreiten.
Ich denke mit einer batch bin ich hier dennoch schneller.
Member: erikro
Solution erikro May 17, 2019 updated at 09:14:03 (UTC)
Goto Top
Moin,

Zitat von @Bem0815:

Zitat von @erikro:

Moin,

weiß Euer Betriebsrat von dem Ansinnen und hat dem zugestimmt?

Gibt keinen.

Dann muss wohl jeder einzelne Mitarbeiter zustimmen. face-wink


Ansonsten nimm die Powershell, da die mit XML umgehen kann. Hier ist schön beschrieben, wie das geht:
https://www.langlitz-it.de/?p=1297

Hmm möglich, nur bin ich nicht sicher ob das an diesem Punkt noch viel Sinn macht, da das Script ansonsten fast fertig ist.
Wenn ich das jetzt nochmal in PS umschreibe gehen hier wohl nochmal 1-2 Stunden ins Land bis ich das geschrieben habe. z.B. die Syntax zur Ausgabe des Anzeigenamens aus der AD ist natürlich eine ganz andere.


Das ist nicht wirklich schwer. Das Skript setzt voraus, dass die Datei existiert.

# Datei einlesen
[xml]$xml = get-content e:\datei.xml

# Neuen Datensatz erzeugen
$new_ds = $xml.CreateElement("Datensatz")  

# Edit: Du wolltest ja auch noch den Klarnamen haben
$new_name = $xml.CreateElement("Mitarbeiter")  
$new_name.set_InnerText($(get-aduser $env:username).name)

# Neuen Mitarbeiter erzeugen
$new_login = $xml.CreateElement("Loginname")  
# Mitarbeiter mit Loginnamen füllen
$new_login.set_innerText($env:USERNAME)

# Neues Datum erzeugen
$new_date = $xml.CreateElement("Uhrzeit")  
# Datum mit aktueller Zeit füllen
$new_date.set_InnerText($(get-date))

# Daten an Datensatz anhängen
$new_ds.AppendChild($new_name)
$new_ds.AppendChild($new_login)
$new_ds.AppendChild($new_date)

# Datensatz in die Datei einfügen
$xml.daten.AppendChild($new_ds)
# Datei speichern
$xml.Save("e:\datei.xml")  

That's it. Hat keine zwei Stunden gedauert. face-wink

hth

Erik
Member: Bem0815
Bem0815 May 17, 2019 updated at 09:23:52 (UTC)
Goto Top
Danke dir für die Mühe. Leider bringt mich das nicht wirklich weiter.

Hab mit den 2 Stunden auch nicht das reine coding gemeint, sondern bis das Ding auch fehlerfrei läuft und aktuell sieht mir das so aus als würde es weiterhin genau darauf hinauslaufen.

Testlauf ergibt folgendes:

get-content : Der Pfad "C:\test\datei.xml" kann nicht gefunden werden, da er nicht vorhanden ist.  
In C:\test\test.ps1:2 Zeichen:13
+ [xml]$xml = get-content c:\test\datei.xml
+             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : ObjectNotFound: (C:\test\datei.xml:String) [Get-Content], ItemNotFoundException
    + FullyQualifiedErrorId : PathNotFound,Microsoft.PowerShell.Commands.GetContentCommand
 
Es ist nicht möglich, eine Methode für einen Ausdruck aufzurufen, der den NULL hat.
In C:\test\test.ps1:5 Zeichen:1
+ $new_ds = $xml.CreateElement("Datensatz")  
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (:) , RuntimeException
    + FullyQualifiedErrorId : InvokeMethodOnNull
 
Es ist nicht möglich, eine Methode für einen Ausdruck aufzurufen, der den NULL hat.
In C:\test\test.ps1:8 Zeichen:1
+ $new_name = $xml.CreateElement("Mitarbeiter")  
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (:) , RuntimeException
    + FullyQualifiedErrorId : InvokeMethodOnNull
 
get-aduser : Die Benennung "get-aduser" wurde nicht als Name eines Cmdlet, einer Funktion, einer Skriptdatei oder eines ausführbaren Programms erkannt. Überprüfen Sie die Schreibweise des   
Namens, oder ob der Pfad korrekt ist (sofern enthalten), und wiederholen Sie den Vorgang.
In C:\test\test.ps1:9 Zeichen:27
+ $new_name.set_InnerText($(get-aduser $env:unsername).name)
+                           ~~~~~~~~~~
    + CategoryInfo          : ObjectNotFound: (get-aduser:String) , CommandNotFoundException
    + FullyQualifiedErrorId : CommandNotFoundException
 
Es ist nicht möglich, eine Methode für einen Ausdruck aufzurufen, der den NULL hat.
In C:\test\test.ps1:9 Zeichen:27
+ $new_name.set_InnerText($(get-aduser $env:unsername).name)
+                           ~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (:) , RuntimeException
    + FullyQualifiedErrorId : InvokeMethodOnNull
 
Es ist nicht möglich, eine Methode für einen Ausdruck aufzurufen, der den NULL hat.
In C:\test\test.ps1:12 Zeichen:1
+ $new_login = $xml.CreateElement("Loginname")  
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (:) , RuntimeException
    + FullyQualifiedErrorId : InvokeMethodOnNull
 
Es ist nicht möglich, eine Methode für einen Ausdruck aufzurufen, der den NULL hat.
In C:\test\test.ps1:14 Zeichen:1
+ $new_login.set_innerText($env:USERNAME)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (:) , RuntimeException
    + FullyQualifiedErrorId : InvokeMethodOnNull
 
Es ist nicht möglich, eine Methode für einen Ausdruck aufzurufen, der den NULL hat.
In C:\test\test.ps1:17 Zeichen:1
+ $new_date = $xml.CreateElement("Uhrzeit")  
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (:) , RuntimeException
    + FullyQualifiedErrorId : InvokeMethodOnNull
 
Es ist nicht möglich, eine Methode für einen Ausdruck aufzurufen, der den NULL hat.
In C:\test\test.ps1:19 Zeichen:27
+ $new_date.set_InnerText($(get-date))
+                           ~~~~~~~~
    + CategoryInfo          : InvalidOperation: (:) , RuntimeException
    + FullyQualifiedErrorId : InvokeMethodOnNull
 
Es ist nicht möglich, eine Methode für einen Ausdruck aufzurufen, der den NULL hat.
In C:\test\test.ps1:22 Zeichen:1
+ $new_ds.AppendChild($new_name)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (:) , RuntimeException
    + FullyQualifiedErrorId : InvokeMethodOnNull
 
Es ist nicht möglich, eine Methode für einen Ausdruck aufzurufen, der den NULL hat.
In C:\test\test.ps1:23 Zeichen:1
+ $new_ds.AppendChild($new_login)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (:) , RuntimeException
    + FullyQualifiedErrorId : InvokeMethodOnNull
 
Es ist nicht möglich, eine Methode für einen Ausdruck aufzurufen, der den NULL hat.
In C:\test\test.ps1:24 Zeichen:1
+ $new_ds.AppendChild($new_date)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (:) , RuntimeException
    + FullyQualifiedErrorId : InvokeMethodOnNull
 
Es ist nicht möglich, eine Methode für einen Ausdruck aufzurufen, der den NULL hat.
In C:\test\test.ps1:27 Zeichen:1
+ $xml.daten.AppendChild($new_ds)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (:) , RuntimeException
    + FullyQualifiedErrorId : InvokeMethodOnNull
 
Es ist nicht möglich, eine Methode für einen Ausdruck aufzurufen, der den NULL hat.
In C:\test\test.ps1:29 Zeichen:1
+ $xml.Save("c:\test\datei.xml")  
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (:) , RuntimeException
    + FullyQualifiedErrorId : InvokeMethodOnNull
Member: erikro
Solution erikro May 17, 2019 at 09:27:28 (UTC)
Goto Top
Heut ist nix los. Hier das Skript nochmal mit Erzeugen der Datei, wenn sie nicht exisistiert und Datum im Dateinamen:

# Datei einlesen
if(!$(test-path e:\login-$(get-date -format "yyMMdd").xml)) {  

    [xml]$xml = '<?xml version="1.0" encoding="UTF-8"?> <DATEN><HEAD>Mitarbeiterlogin</HEAD></DATEN>'  

}
else {
    
    [xml]$xml = get-content e:\daten1tb\test\login-$(get-date -format "yyMMdd").xml  

}
# Neuen Datensatz erzeugen
$new_ds = $xml.CreateElement("Datensatz")  

# Edit: Du wolltest ja auch noch den Klarnamen haben
$new_name = $xml.CreateElement("Mitarbeiter")  
$new_name.set_InnerText($(get-aduser $env:username).name)

# Neuen Mitarbeiter erzeugen
$new_login = $xml.CreateElement("Loginname")  
# Mitarbeiter mit Loginnamen füllen
$new_login.set_innerText($env:USERNAME)

# Neues Datum erzeugen
$new_date = $xml.CreateElement("Uhrzeit")  
# Datum mit aktueller Zeit füllen
$new_date.set_InnerText($(get-date))

# Daten an Datensatz anhängen
$new_ds.AppendChild($new_name)
$new_ds.AppendChild($new_login)
$new_ds.AppendChild($new_date)

# Datensatz in die Datei einfügen
$xml.daten.AppendChild($new_ds)
# Datei speichern
$xml.Save("e:\login-$(get-date -format "yyMMdd").xml")  
Member: erikro
erikro May 17, 2019 updated at 09:32:39 (UTC)
Goto Top
Zitat von @Bem0815:
get-content : Der Pfad "C:\test\datei.xml" kann nicht gefunden werden, da er nicht vorhanden ist.  
> In C:\test\test.ps1:2 Zeichen:13
> + [xml]$xml = get-content c:\test\datei.xml
> +             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
>     + CategoryInfo          : ObjectNotFound: (C:\test\datei.xml:String) [Get-Content], ItemNotFoundException
>     + FullyQualifiedErrorId : PathNotFound,Microsoft.PowerShell.Commands.GetContentCommand

Die Datei ist nicht am angegebenen Pfad. Alles andere sind Folgefehler. Ich habe das Skript getestet.

 get-aduser : Die Benennung "get-aduser" wurde nicht als Name eines Cmdlet, einer Funktion, einer Skriptdatei oder eines ausführbaren Programms erkannt. Überprüfen Sie die Schreibweise des   
> Namens, oder ob der Pfad korrekt ist (sofern enthalten), und wiederholen Sie den Vorgang.
> In C:\test\test.ps1:9 Zeichen:27
> + $new_name.set_InnerText($(get-aduser $env:unsername).name)
> +                           ~~~~~~~~~~
>     + CategoryInfo          : ObjectNotFound: (get-aduser:String) , CommandNotFoundException
>     + FullyQualifiedErrorId : CommandNotFoundException

Kein AD vorhanden? Sind die RSAT installiert (zumindest die Powershell-Module)? Der Rest sind wieder Folgefehler.

Das Skript mit Erzeugen der Datei habe ich ja schon gepostet.

hth

Erik
Member: Bem0815
Bem0815 May 17, 2019 updated at 09:46:30 (UTC)
Goto Top
Ok,
das sieht schon mal echt gut aus.
Vielen dank.

Ein Problem habe ich noch, fällt dir ggfs. noch eine Alternative ein den Anzeigenamen des Domänenbenutzers über PS auszulesen?

Da erhalte ich als einziges noch einen Fehler:
get-aduser : Die Benennung "get-aduser" wurde nicht als Name eines Cmdlet, einer Funktion, einer Skriptdatei oder eines ausführbaren Programms erkannt. Überprüfen Sie die Schreibweise des   
Namens, oder ob der Pfad korrekt ist (sofern enthalten), und wiederholen Sie den Vorgang.
In C:\test\test.ps1:17 Zeichen:27
+ $new_name.set_InnerText($(get-aduser $env:username).name)
+                           ~~~~~~~~~~
    + CategoryInfo          : ObjectNotFound: (get-aduser:String) , CommandNotFoundException
    + FullyQualifiedErrorId : CommandNotFoundException

Zum beheben brauche ich wie du gesagt hast die Remoteserver-Verwaltungstools für Windows 10. AD ist vorhanden.

Da das Script ja auf jedem PC bei der Anmeldung laufen soll müsste ich das domänenweit auf die Rechner klatschen.
Das scheint mir für den Zweck ein bisschen Overkill zu sein, daher wäre mir hier eine alternative die ohne das auskommt lieber.

Falls es aber nicht geht okay, dann muss ich das vorher verteilen.
Mitglied: 139708
Solution 139708 May 17, 2019 updated at 10:07:37 (UTC)
Goto Top
fällt dir ggfs. noch eine Alternative ein den Anzeigenamen des Domänenbenutzers über PS auszulesen?
AD Tools sind überflüssig, dazu nimmt man einfach einen ADSISEARCHER der ist in jeder Powershell per Default verfügbar:
$new_name.InnerText = ([adsisearcher]"(SamAccountName=$env:USERNAME)").FindOne().Properties.displayname  
Gruß wireguard
Member: Bem0815
Bem0815 May 17, 2019 at 10:23:46 (UTC)
Goto Top
Perfekt. Das funktioniert.

Vielen Dank euch beiden.
Member: erikro
erikro May 17, 2019 at 10:30:59 (UTC)
Goto Top
Jo, auch das geht mit dem ADSI-Connector:

$sam = $env:USERNAME
$root = [ADSI]''  
$search = New-Object System.DirectoryServices.DirectorySearcher($root)
$search.filter = "(sAMAccountName=$sam)"  
$user = $search.FindAll()
$displayname = ($user.properties).name

Das stellst Du vor das $new_name.set_Innertext und ersetzt das $(get-aduser ...) durch $displayname.
Member: erikro
erikro May 17, 2019 at 10:31:56 (UTC)
Goto Top
Zitat von @139708:

fällt dir ggfs. noch eine Alternative ein den Anzeigenamen des Domänenbenutzers über PS auszulesen?
AD Tools sind überflüssig, dazu nimmt man einfach einen ADSISEARCHER der ist in jeder Powershell per Default verfügbar:
> $new_name.InnerText = ([adsisearcher]"(SamAccountName=$env:USERNAME)").FindOne().Properties.displayname  
> 

Noch einfacher. face-wink
Mitglied: 139708
139708 May 17, 2019 updated at 11:10:41 (UTC)
Goto Top
Es sei dir aber noch gesagt das das gleichzeitige Schreiben in eine einzige Datei zu Fehlern führt wenn zwei Systeme gleichzeitig versuchen das File zu ersetzen. D.h. System 1 schreibt das File gerade neu, während System2 das auch versucht, dann wird der Login-Eintrag von System2 nicht vorhanden sein weil der Schreibvorgang fehlschlägt. Es sollte also zumindest noch eine Lock-Abfrage eingefügt und solange gewartet werden bis das File wieder neu geschrieben werden kann!
Besser wäre es hier gleich ein Datenbank-Backend für die Transactions zu verwenden.
Member: erikro
erikro May 17, 2019 at 11:29:39 (UTC)
Goto Top
Zitat von @139708:

Es sei dir aber noch gesagt das das gleichzeitige Schreiben in eine einzige Datei zu Fehlern führt wenn zwei Systeme gleichzeitig versuchen das File zu ersetzen. D.h. System 1 schreibt das File gerade neu, während System2 das auch versucht, dann wird der Login-Eintrag von System2 nicht vorhanden sein weil der Schreibvorgang fehlschlägt. Es sollte also zumindest noch eine Lock-Abfrage eingefügt und solange gewartet werden bis das File wieder neu geschrieben werden kann!
Besser wäre es hier gleich ein Datenbank-Backend für die Transactions zu verwenden.

Da hast Du vollkommen recht. Das könnte man so lösen:

while($testpath "login-lock.txt") {  

# Die Schleife dient nur dazu, dass nicht weiter gemacht wird

}
out-file login-lock.txt -inputobject "locked"  

# Hier der Rest des Skripts

# Letzte Zeile

remove-item "login-lock.txt"  

Was mir daran aber nicht wirklich gefällt, ist, dass die User alle auf den Ordner und die Dateien Schreibzugriff brauchen.
Mitglied: 139708
139708 May 17, 2019 updated at 12:08:04 (UTC)
Goto Top
Das könnte man so lösen:
Dafür braucht es kein extra File, es reicht mit der System.IO.File Open Methode mit Schreibzugriff auf das File in einem try catch Konstrukt zuzugreifen. Wird eine entsprechende Exception geworfen weis man das das File gerade in Benutzung ist, das ganze in einer While Schleife.
Vorzuziehen ist aber weiterhin eine DB Instanz wenn Konsistenz gefordert ist.
Member: erikro
erikro May 17, 2019 at 12:06:44 (UTC)
Goto Top
Zitat von @139708:

Das könnte man so lösen:
Dafür braucht es kein extra File, es reicht mit der System.IO Open Methode mit Schreibzugriff auf das File in einem try catch Konstrukt zuzugreifen. Wird eine entsprechende Exception geworfen weis man das das File gerade in Benutzung ist, das ganze in einer While Schleife.
Vorzuziehen ist aber weiterhin eine DB Instanz wenn Konsistenz gefordert ist.

Auf jeden Fall.
Member: Bem0815
Bem0815 May 17, 2019 updated at 12:22:52 (UTC)
Goto Top
Auch hierfür nochmal Danke.

Die Syntax hat nicht ganz gepasst.
Aber das hab ich mit meinen eingeschränkten PS Kenntnissen dann zumindest selbst noch hinbekommen, dass es läuft.

Sieht jetzt so aus:

# Datei einlesen
if(!$(test-path c:\test\login-$(get-date -format "yyMMdd").xml)) {  

    [xml]$xml = '<?xml version="1.0" encoding="UTF-8"?> <DATEN><HEAD>Mitarbeiterlogin</HEAD></DATEN>'  

}
else {
    
    [xml]$xml = get-content c:\test\login-$(get-date -format "yyMMdd").xml  

}
# Neuen Datensatz erzeugen
$new_ds = $xml.CreateElement("Datensatz")  

# Neuen Anzeigename erzeugen
$new_name = $xml.CreateElement("Mitarbeiter")  
$new_name.InnerText = ([adsisearcher]"(SamAccountName=$env:USERNAME)").FindOne().Properties.displayname  

# Neuen Mitarbeiter erzeugen
$new_login = $xml.CreateElement("Loginname")  
# Mitarbeiter mit Loginnamen füllen
$new_login.set_innerText($env:USERNAME)

# Neues Datum erzeugen
$new_date = $xml.CreateElement("Uhrzeit")  
# Datum mit aktueller Zeit füllen
$new_date.set_InnerText($(get-date))

# Daten an Datensatz anhängen
$new_ds.AppendChild($new_name)
$new_ds.AppendChild($new_login)
$new_ds.AppendChild($new_date)

while($(test-path c:\test\login-locked.txt)) {

# Schleife pausiert solange login-locked.txt existiert.

}

New-Item -Path 'c:\test\login-locked.txt'  

# Datensatz in die Datei einfügen
$xml.daten.AppendChild($new_ds)
# Datei speichern
$xml.Save("c:\test\login-$(get-date -format "yyMMdd").xml")  

remove-item -Path 'c:\test\login-locked.txt'  

Das mit dem Schreibzugriff ist natürlich nicht ganz optimal.
Ich denke ich kann mit einer versteckten Freigabe hier aber mit Security by Obscurity leben.
Bei uns wissen 99% der Mitarbeiter nicht einmal was die Kraftmuschel ist.

Und die Daten sollen nur für die Personalabteilung als kleine Hilfe gelten, damit die nicht jeden Tag manuell die Anwesenheit von allen Mitarbeitern prüfen müssen muss sondern anhand der Datei schon mal Anhaltspunkte haben und dann nur noch punktuell prüfen muss.

Für mehr wird das ganze nicht eingesetzt. Daher ist denke ich eine DB schon fast zuviel des guten.
Für die Abteilung ist das auch okay, dass hier Doppelteinträge entstehen können sollte sich jemand am Tag mehrmals anmelden. ;)

Ich denke nicht dass es jemand bei uns mit Schreibzugriff schafft so etwas hinzubekommen:
https://www.spiegel.de/karriere/spanien-beamter-kommt-sechs-jahre-nicht- ...
Member: erikro
erikro May 17, 2019 at 12:36:20 (UTC)
Goto Top
Nein! Ob die Datei in Benutzung ist oder nicht, musst Du egal wie vor dem Auslesen ganz am Anfang testen. Sonst öffnest Du sie im Zustand mit z. B. Meier, Müller, Schulze. Kurz darauf öffnet eine andere Instanz die Datei auch in diesem Zustand. Die erste Instanz schreibt Hansen mit rein und speichert. Die zweite Instanz kann jetzt nicht speichern, weil die Lock-Datei exisitiert. Die erste löscht die Lock-Datei wieder. Jetzt darf die zweite schreiben und schreibt Petersen rein. Sie hat aber die Datei noch in dem Zustand ohne Hansen. Also überschreibt sie die gerade geschriebene Datei wieder und Hansen hat nicht gearbeitet.
Member: Bem0815
Bem0815 May 17, 2019 at 12:53:59 (UTC)
Goto Top
Ah okay, verstehe.

Ja stimmt, bin hier noch zu arg Batch gewöhnt.
Da wird ja nur Zeilenweise geschrieben ohne die Daten vorher beim auslesen im Cache zu haben.

Ok, dann ändere ich das nochmal.
Mitglied: 139708
139708 May 17, 2019 updated at 13:12:08 (UTC)
Goto Top
Hier optimierter Code für die Prüfung ob ein File im Zugriff ist, mit zus. 20s Abbruchbedingung damit das Ding nicht ewig hängt.
$file = 'C:\test.xml'  

function Is-FileLocked([string]$filePath){
    try{
        $fileStream = (New-Object System.IO.FileInfo $filePath).OpenWrite()
        # file is not locked by another process
        return $false
    }catch {
        # file is locked
        return $true
    }finally{
        if($filestream){
            $fileStream.Close(); $fileStream.Dispose()
        }
    }
}
$maxtries = 20
$tries = 0
while ($tries -lt $maxtries -and (Is-FileLocked $file)){$tries++;sleep 1}
if ($tries -ge $maxtries){
    write-error -Message "File could not bee opened for writing!" -Category LimitsExceeded  
    exit 1
}