Hoe loop je over bestanden in de map en verander je het pad en voeg je een achtervoegsel toe aan de bestandsnaam

Ik moet een script schrijven dat mijn programma met verschillende argumenten start, maar Bash is nieuw voor mij. Ik start mijn programma met:

./MyProgram.exe Data/data1.txt [Logs/data1_Log.txt].

Hier is de pseudocode voor wat ik wil doen:

for each filename in /Data do
  for int i = 0, i = 3, i++
    ./MyProgram.exe Data/filename.txt Logs/filename_Log{i}.txt
  end for
end for

Dus ik vraag me echt af hoe ik een tweede argument van het eerste kan maken, dus het lijkt op dataABCD_Log1.txt en start mijn programma.


Antwoord 1, autoriteit 100%

Eerst een paar opmerkingen: als je Data/data1.txt als argument gebruikt, moet het dan echt /Data/data1.txt zijn (met een slash )? Moet de buitenste lus ook alleen scannen op .txt-bestanden of alle bestanden in /Data? Hier is een antwoord, ervan uitgaande dat alleen /Data/data1.txt en .txt-bestanden:

#!/bin/bash
for filename in /Data/*.txt; do
    for ((i=0; i<=3; i++)); do
        ./MyProgram.exe "$filename" "Logs/$(basename "$filename" .txt)_Log$i.txt"
    done
done

Opmerkingen:

  • /Data/*.txt breidt uit naar de paden van de tekstbestanden in /Data (inclusief het /Data/-gedeelte)
  • $( ... ) voert een shell-opdracht uit en voegt de uitvoer op dat punt in de opdrachtregel in
  • basename somepath .txt voert het basisgedeelte van somepath uit, met .txt verwijderd van het einde (bijv. /Data/file.txt -> file)

Als u MyProgram moest uitvoeren met Data/file.txt in plaats van /Data/file.txt, gebruik dan "${filename#/}" om de voorloopslash te verwijderen. Aan de andere kant, als het echt Data en niet /Data is die je wilt scannen, gebruik dan gewoon for filename in Data/*.txt.


Antwoord 2, autoriteit 46%

Sorry voor het necromantiseren van de thread, maar wanneer je bestanden herhaalt door globbing, is het een goede gewoonte om het hoekgeval te vermijden waarin de glob niet overeenkomt (waardoor de lusvariabele uitbreidt naar de (niet-overeenkomende) glob-patroonreeks zelf ).

Bijvoorbeeld:

for filename in Data/*.txt; do
    [ -e "$filename" ] || continue
    # ... rest of the loop body
done

Referentie: Bash-valkuilen


Antwoord 3, autoriteit 10%

for file in Data/*.txt
do
    for ((i = 0; i < 3; i++))
    do
        name=${file##*/}
        base=${name%.txt}
        ./MyProgram.exe "$file" Logs/"${base}_Log$i.txt"
    done
done

De name=${file##*/} vervanging (shell-parameteruitbreiding) verwijdert de leidende padnaam tot de laatste /.

De base=${name%.txt} vervanging verwijdert de achterliggende .txt. Het is wat lastiger als de extensies kunnen variëren.


Antwoord 4, autoriteit 2%

U kunt de uitvoeroptie Finds null-gescheiden gebruiken met read om mapstructuren veilig te doorlopen.

#!/bin/bash
find . -type f -print0 | while IFS= read -r -d $'\0' file; 
  do echo "$file" ;
done

Dus voor jouw geval

#!/bin/bash
find . -maxdepth 1 -type f  -print0 | while IFS= read -r -d $'\0' file; do
  for ((i=0; i<=3; i++)); do
    ./MyProgram.exe "$file" 'Logs/'"`basename "$file"`""$i"'.txt'
  done
done

aanvullend

#!/bin/bash
while IFS= read -r -d $'\0' file; do
  for ((i=0; i<=3; i++)); do
    ./MyProgram.exe "$file" 'Logs/'"`basename "$file"`""$i"'.txt'
  done
done < <(find . -maxdepth 1 -type f  -print0)

voert de while-lus uit in het huidige bereik van het script ( proces ) en staat toe dat de uitvoer van find wordt gebruikt bij het instellen van variabelen indien nodig


Antwoord 5

Het lijkt erop dat je een Windows-bestand (.exe) probeert uit te voeren. Je zou zeker powershell moeten gebruiken. Hoe dan ook, op een Linux bash-shell is een simpele one-liner voldoende.

[/home/$] for filename in /Data/*.txt; do for i in {0..3}; do ./MyProgam.exe  Data/filenameLogs/$filename_log$i.txt; done done

Of in een bash

#!/bin/bash
for filename in /Data/*.txt; 
   do
     for i in {0..3}; 
       do ./MyProgam.exe Data/filename.txt Logs/$filename_log$i.txt; 
     done 
 done

LEAVE A REPLY

Please enter your comment!
Please enter your name here

6 + eight =

Other episodes