при использовании sed -e
для обновления некоторых параметров конфигурационного файла и передачи его на | tee
(для записи обновленного содержимого в файл) это случайное разбиение и приводит к тому, что файл недействителен (размер 0).Обновление файла с использованием tee случайным образом завершается неудачей в сценарии linux bash
В сводке, этот код используется для обновления параметров:
# 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
Я создал скрипт, который вызывает эту команду обновления 100 раз.
- В Ubuntu VM (Parallels Desktop) на совместно используемый каталог с OSX это происходит до 50 раз
- в Ubuntu VM (Parallels Desktop) на Ubuntu раздела это поведение происходит до 40 раз
- на родной системе (IntelNUC с Ubuntu), это происходит до 15 раз
Может кто-нибудь объяснить, почему это происходит?
Вот полнофункциональный сценарий, в котором вы также можете запустить эксперимент. (Все необходимые файлы создаются с помощью сценария, так что вы можете просто скопировать/вставить его в bashscriptfile и запустить его)
#!/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
С уважением DonPromillo
Почему вы используете 'tee' для этого, если вы выбрасывая' stdout'? Кроме того, похоже, что у вас есть условие гонки, скорее всего, поскольку вы используете 'tee' для перезаписи файла в то же время, что и для' sed' для его обработки. Если 'tee' обрезает файл до того, как' sed' сможет его получить, вы получите файл длины '0'. Если ваш 'sed' поддерживает его, вы можете заставить его изменить файл на месте, иначе вы должны записать вывод в файл temp, а затем переместить его на исходное имя. –
. Две стороны трубы являются асинхронными; вы не можете гарантировать, что 'sed' полностью поглощает содержимое' myFile' до того, как 'tee' перезапишет его. – chepner
Спасибо @EricRenouf и @chepner за то, что вы указали это довольно четко. Причина, по которой я использовал 'tee', в основном вызывает любопытство в трубопроводах. – DonPromillo