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 inbasename 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