Fortschrittsanzeige in Batch Datei?

6 Antworten

Vom Beitragsersteller als hilfreich ausgezeichnet

Erstmal möchte ich mich entschuldigen ,das es solang mit der Antwort gedaert hat.
ich bin ehrlich... mir sind 400qm Garten und 1Kasten Bier dazwischen gekommen...

Gesten Nachmittag habe ich mich hingesetzt um die paar Zeilen Code in meinen Editor zu hacken.

Nun zum Thema:

Die Fortschrittsanzeige selbst war kein Thema die meisten Routinen hatte ich irgendwann mal Stückweise geschrieben und in meiner Batchbibliothek.

...was kann denn so schwer sein den Kram um zwei For-loops mit findstr herumzubauen? der Klassiker...

nur hat kein Batchschreiber , der mal ein paar 100 Zeilen CSV liest auf dem Schirm das findstr ein paar Tücken hat. über welche auch Du gestolpert bist.

Es ist elendiglich langsam. ...und buffert erstmal jegliche Anzeige nur um dann alles auf einmal auszuspucken... Man hockt tatsächlich ewigkeiten vorm blinden Fenster...

dabei geht es doch , wenn man mit...

for /f "usebackq tokens=* delims=:" %%a in ("%quell-CSV%") do (
       echo %%a | find /i "irgendwas"  >> "%ziel-CSV%"        )

...filtert ohne zu Buffern.
die Krux an dem die meisten scheitern, ist, das obiger Loop ohne usebackq nur Dateien ohne Leerzeichen im Namen liest, weil dafür Anführunszeichen nötig sind, diese jedoch anderen Funktionen vorbehalten sind. usebackq macht es möglich... Batchirrsinn.

Egal... es klappt... Zeilen werden 90mal schneller und Einzeln gelesen als mit findstr im in Block. Diese Methode Filtert automatisch alle Leerzeilen aus.(besser geht es kaum...)

Finstr ist damit Disqalifiziert... raus...!!!

Mit 4 Sekunden / 100.000 Zeilen merkt man fast garnicht das eine primitiver for-loop zählt. Demo1

Ich war der Meinung das wäre noch zu toppen und habe gaanz tief in die Batcher Trickkiste gelangt... Chimera... ein mix aus mehreren Schriptsprachen in einer Batchdatei .

Die Batch enthält ein kleines Javascript-programm welches nichts weiter kann als Zeilen zu zählen und das Ergebnis auszugeben...
wenns dazu Fragen gibt, später...
Diese Methode benötigt etwa 1,15 Sekunde für die 100.000 Zeilen. da ist sogar C# langsamer...:p
siehe Demo2 .

Demo3 nur zum Testen.

In Demo 1 ist alles bestens? kommentiert.
bei den anderen beiden Code-Listings habe ich alles weggelassen, was das gleiche wie in Demo 1 ist....

Zum Schluss noch eine kleine Batch die 100000 zufällige Datensätze zum Testen erzeugt.

jetzt bete ich das Gutefrage so viele Zeilen erlaubt....

Die Listings:

Demo1:

@echo off
setlocal enabledelayedexpansion
call :init_clrln
set "quell-CSV=mein Quelle.csv" 
set "ziel-CSV=mein Ziel.csv" 
set "lineOffLast_Record=0" 
set "currentLine=0"
echo %time%

  rem die "fileRead-Methode  mit ist ca. 90 mal schneller als das zählen mit Findstring!
  rem Leerzeilen werden automatisch entfernt ca.4 Sec /100000 Zeilen!
  rem Zeilen Zählen...
for /f  "usebackq " %%a in ("%quell-CSV%") do (set /a "lineOffLast_Record+=1")

echo %time% %lineOffLast_Record%


  rem Datensätze Verarbeiten:
  rem Dito wie Oben, jedoch wird nun der Rest im token * an %%b übergeben
for /f "usebackq tokens=* delims=:" %%a in ("%quell-CSV%") do (

      rem einen Datensatz aus der Quell-CSV-Datei auslesen
      rem und an die ZielCSV-Datei anhängen 
      rem statt einfach an eine andere CSV-Datei anzuhängen  können auch andere
      rem auf den Datensatz bezogene Befehle/Umformatierungen eingesetzt werden.
    echo %%a >> "%ziel-CSV%"
      rem Filtern kann man  mit einer Pipe..
      rem zB.  echo %%a |find /i /v "Rudi" >>datei  verwirft alle Zeilen  mit Rudi
    
      rem hier  beginnt der Fortschritt-Kram 
      rem mitzählen bei welchem Datensatz wir sind..
      rem aktuelle Zeilennummer...
    set /a "currentLine+=1"
      rem Unterprogramm aufrufen
    call :show_progress 30
)
echo:
echo %time%
pause
exit /b 

  rem ================Subroutines===============================

:show_progress
      rem parameter :  Number  >0 (eg. Div by Zero)
    
      rem schreibt  nur  wenn die aktuell Zeilennummer nach modulo 1 ist.
      rem verhindert Flackern der Anzeige
    set /a "refreshLine=%currentLine% %% %~1"
    if %currentLine% neq %lineOffLast_Record%  if %refreshLine% neq 1 exit /b 
    call :calc_PerMille progress %lineOffLast_Record% %currentLine%
    call :ppmToPecentString percentStr %progress%
    call :formatString  currentLine currLnStr  10
    call :formatString  percentStr  percentStr 7
    
             rem lösche aktuelle Zeile rückwärts  bis  zum Zeilenanfang und schreibe Text
             rem entspricht "echo ...." jedoch wechselt der cursor nicht in  die nächste  Zeile
    <nul set /p =%clrln%Fortschritt: %percentStr% Zeile: %currLnStr%  Die Zeit vergeht: %time% ||ver>nul
exit /b

:calc_PerMille
      rem params: Zielvariable %Grundwert% %Promillewert%  
      rem nein... hat nicht  mit saufen zu tun! 
      rem fiese Batchalgebra: an die GrundwertVariable %3 einfach 3 Nullen "malen" (mal 1000)
    set /a "%~1=%3000 / %2 "
exit /b

:ppmToPecentString  
      rem params  outVariablename Number
      rem Batch  kennt  keine Fließomma-Berechnug, also wird einfach  die Ganzzahl Zerschnitten 
      rem und  um das Komma wieder zusammen gefiedelt
    set "ppm=%2"
    if !ppm! gtr 9 (
        set "%~1=!ppm:~0,-1!,!ppm:~-1! %%"
    ) else (
        set "%~1=0,!ppm:~-1! %%"
    )
exit /b

:formatString
      rem params: inVar outVar outStringLenght (max.50)
      
    set "%~2=                                                  !%~1:~-%~3!"
      rem jetzt ist er wieder bis zu  21 Zeichen lang und wir holen uns wieder die letzten 10 
      rem nun h
    set "%~2=!%~2:~-%~3!"
exit /b

:init_clrln
      rem  Zeile mit 80 Backspaces erzeugen ... (löscht eine Zeile vom end  zum Anfang)  erzeugt die Variablen %bkspc% und %clrln%
    for /f "delims=;" %%. in ('"prompt $H; & for %%. in (nul) do call"') do ( set "bkspc=%%." & set "clrln=!bkspc!" & for /l %%: in (1,1,4) do (set "clrln=!clrln!!clrln!!clrln!"))
exit /b
  rem ================Subroutines end===============================

Demo2:

<!-- : diese Zeile nicht  löschen
@echo off
setlocal enabledelayedexpansion
call :init_clrln
set "quell-CSV=mein Quelle.csv" 
set "ziel-CSV=mein Ziel.csv" 
set "lineOffLast_Record=0" 
set "currentLine=0"
echo %time%

  rem die "Batch-fileRead-Methode ist ca. 300 mal schneller als das zählen mit Findstr!
  rem Jascript  benötigt  nur 1,15 Sekunden für 100000Zeilen  in ist damit etwa 200 mal schneller als zählen mit Findstr ...
for /f  "usebackq " %%a in (`mshta "%~f0" "%quell-CSV%"`) do (set  "lineOffLast_Record=%%a")

echo %time% %lineOffLast_Record%

for /f "usebackq tokens=* delims=:" %%a in ("%quell-CSV%") do (
    echo %%a >> "%ziel-CSV%"
    set /a "currentLine+=1"
      rem Unterprogramm aufrufen
    call :show_progress 30
)
echo:
echo %time%
pause
exit /b 

  rem ================Subroutines===============================
...
wie Demo1
...
  rem ================Subroutines end===============================
und diese auch nicht -->
<hta:application 
    ID="oHTA"
    applicationname="CountLines"
    windowState="minimize" >
<script>
if(typeof oHTA.commandLine !== "undefined")
{
    args=oHTA.commandLine;
    args=args.replace(/" "/g, String.fromCharCode(34,1,34)).replace(/"/g,'');
    res = args.split(String.fromCharCode(1));
    shortcut=res[1];
    countlines();
}
close();

function countlines()
{
    var x=0;
    fso=new ActiveXObject('Scripting.FileSystemObject');
    iS=fso.OpenTextFile(shortcut,1, false);
    while(!iS.AtEndOfStream)
    {
        line=iS.ReadLine()
        if ( line.length > 0 ) 
        {  //leerzeilen ausschließen
           x+=1;
        } ;          
    };
    iS.Close();
    fso.GetStandardStream(1).Write(x);
}
</script>   

Demo3

wie demo1
...

 rem lahhhhm.... 375 Sekunden um 100000 Zeilen zu zählen...
for /f "usebackq tokens=1 delims=:" %%a in (`findstr /n /c:"," "%quell-CSV%"`) do (set "lineOffLast_Record=%%a")

echo %time% %lineOffLast_Record%
pause
 rem Datensätze Verarbeiten:
 rem Dito wie Oben, jedoch wird nun der Rest im token * an %%b übergeben
for /f "usebackq tokens=1* delims=:" %%a in (`findstr /n /c:"," "%quell-CSV%"`) do (
 rem ...und da sieht man ewig keinen Fortschritt, 
 rem findstr liest erstmal gemächlich im Blindflug alle Zeilen bevor es ein Lebenszeichen von sich gib.
 rem durchgefallen....
  echo %%b >> "%ziel-CSV%"
  set /a "currentLine+=1"
   rem Unterprogramm aufrufen
  call :show_progress 30
)
echo:
echo %time%
pause
exit /b 

 rem ================Subroutines===============================
wie Demo1
 rem ================Subroutines end===============================

csv erzeugen:

@echo off
setlocal enabledelayedexpansion
for /l %%f in (1,1,100000) do (
  echo %%f 
  echo !random!,!random!,!random!,!random!,!random!,!random!,!random!,!random!,!random!,%%f>>"mein Quelle.csv") 
pause
exit /b 
Woher ich das weiß:eigene Erfahrung – Ich mach das seit 30 Jahren

Ja.

Benutze statt des normalen Copy Befehl das tool robocopy.

Das ist bei jedem Windows mit dabei.

Ist ein sehr leistungsfähiges Copy mit vielen Kommandozeilenparametern.

Unter anderem auch welchen für eine Art Fortschrittsanzeige

(mit % - aber keinen grafischen Balken)

...

Eine andere Möglichkeit wäre, statt des cmd copy ein selbstgebasteltes VBS script zu nutzen.

Da man im Batch Variablen verwenden kann und man Schleifen programmieren kann, sollte das möglich sein.

Bin kein Batch-Experte, aber Variablen macht man meine ich mit Set und %Variable% und eine for-Schleife gibt es meine ich auch. Notfalls nimmst du GoTo.

Schön wäre es, wenn du noch ein Kommando finden würdest, mit dem du den Cursor genau auf dem Bildschirm positionieren könntest.

Woher ich das weiß:Berufserfahrung – Programmierer

Mit welchem Programm (/Befehl) liest du die CSV denn ein? Oder kopierst du diese einfach nur?

So viel ich weiss nicht ohne externe Programme. Aber auch dann ist es überhaupt nicht schön. Solche arbeiten sollte man eigentlich nicht mit einem Batch erledigen ;)