bei der Verwendung von sed -e
, einige Parameter einer Konfigurationsdatei aktualisieren und es an | tee
(um den aktualisierten Inhalt in die Datei zu schreiben), dies bricht zufällig und verursacht die Datei zu sein ungültig (Größe 0).Aktualisieren einer Datei mit Tee zufällig fehlschlägt in Linux Bash-Skript
Zusammengefasst wird dieser Code für die Aktualisierung Parameter verwendet:
# based on the provided linenumber, add some comments, add the new value, delete old line
sed -e "$lineNr a # comments" -e "$lineNr a $newValue" -e "$lineNr d" $myFile | sudo tee $myFile
ich ein Skript einrichten, die dieses Update-Befehl 100 Mal aufruft.
- In einer Ubuntu VM (Parallels Desktop) auf einem gemeinsamen Verzeichnis mit OSX dies geschieht Verhalten
- In einer Ubuntu VM (Parallels Desktop) auf der Ubuntu-Partition dieses Verhalten bis zu 40 bis 50-fache bis auftritt mal
- Auf einem nativen System (IntelNUC mit Ubuntu) tritt dieses Verhalten
bis 15-mal nach oben Kann jemand erklären, warum dies geschieht?
Hier ist ein voll funktionsfähiges Skript, wo Sie das Experiment auch ausführen können. (Alle erforderlichen Dateien werden vom Skript generiert, so können Sie einfach kopieren/sie in eine bashscriptfile einfügen und ausführen)
#!/bin/bash
# main function at bottom
#====================
#===HELPER METHOD====
#====================
# This method updates parameters with a new value. The replacement is performed linewise.
doUpdateParameterInFile()
{
local valueOfInterest="$1"
local newValue="$2"
local filePath="$3"
# stores all matching linenumbers
local listOfLines=""
# stores the linenumber which is going to be replaced
local lineToReplace=""
# find value of interest in all non-commented lines and store related lineNumber
lineToReplace=$(grep -nr "^[^#]*$valueOfInterest" $filePath | sed -n 's/^\([0-9]*\)[:].*/\1/p')
# Update parameters
# replace the matching line with the desired value
oldValue=$(sed -n "$lineToReplace p" $filePath)
sed -e "$lineToReplace a # $(date '+%Y-%m-%d %H:%M:%S'): replaced: $oldValue with: $newValue" -e "$lineToReplace a $newValue" -e "$lineToReplace d" $filePath | sudo tee $filePath >/dev/null
# Sanity check to make sure file did not get corrupted by updating parameters
if [[ ! -s $filePath ]] ; then
echo "[ERROR]: While updating file it turned invalid."
return 31
fi
}
#===============================
#=== Actual Update Function ====
#===============================
main_script()
{
echo -n "Update Parameter1 ..."
doUpdateParameterInFile "Parameter1" "Parameter1 YES" "config.txt"
if [[ "$?" == "0" ]] ; then echo "[ OK ]" ; else echo "[FAIL]"; return 33 ; fi
echo -n "Update Parameter2 ..."
doUpdateParameterInFile "Parameter2" "Parameter2=90" "config.txt"
if [[ "$?" == "0" ]] ; then echo "[ OK ]" ; else echo "[FAIL]"; return 34 ; fi
echo -n "Update Parameter3 ..."
doUpdateParameterInFile "Parameter3" "Parameter3 YES" "config.txt"
if [[ "$?" == "0" ]] ; then echo "[ OK ]" ; else echo "[FAIL]"; return 35 ; fi
}
#=================
#=== Main Loop ===
#=================
#generate file config.txt
printf "# Configfile with 3 Parameters\n#[Parameter1]\n#only takes YES or NO\nParameter1 NO \n\n#[Parameter2]\n#Parameter2 takes numbers\nParameter2 = 100 \n\n#[Parameter3]\n#Parameter3 takes YES or NO \nParameter3 YES\n" > config.txt
cp config.txt config.txt.bkup
# Start the experiment and let it run 100 times
cnt=0
failSum=0
while [[ $cnt != "100" ]] ; do
echo "==========run: $cnt; fails: $failSum======="
main_script
if [[ $? != "0" ]] ; then cp config.txt.bkup config.txt ; failSum=$(($failSum+1)) ; fi
cnt=$((cnt+1))
sleep 0.5
done
Grüße DonPromillo
Warum Sie diese 'tee' verwenden, wenn Sie' stdout' sind wegwerfen? Es sieht auch so aus, als ob Sie eine Race Condition am wahrscheinlichsten haben, da Sie "tee" verwenden, um die Datei genau zur gleichen Zeit zu überschreiben, wie mit "sed", um sie zu verarbeiten. Wenn 'tee' die Datei abschneidet, bevor' sed' es bekommen kann, erhalten Sie eine '0' Datei. Wenn Ihr 'sed' es unterstützt, können Sie die Datei an Ort und Stelle ändern, andernfalls sollten Sie die Ausgabe in eine temporäre Datei schreiben und dann den ursprünglichen Namen verwenden. –
Die beiden Seiten einer Pipe sind asynchron; Sie können nicht garantieren, dass "sed" den Inhalt von "myFile" vollständig verbraucht, bevor "tee" es überschreibt. – chepner
Danke @EricRenouf und @chepner für den Hinweis ganz klar. Der Grund, warum ich "Tee" benutze, ist im Grunde Neugier in Rohrleitungen. – DonPromillo