在 linux bash 脚本中使用 tee 随机更新文件失败
updating a file using tee randomly fails in linux bash script
当使用 sed -e
更新配置文件的一些参数并将其通过管道传输到 | tee
(将更新的内容写入文件)时,这会随机中断并导致文件无效(尺寸 0)。
总而言之,这段代码用于更新参数:
# 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次
- 在原生系统(具有 Ubuntu 的 IntelNUC)上,此行为最多发生 15 次
有人可以解释为什么会这样吗?
这是一个功能齐全的脚本,您也可以在其中 运行 进行实验。 (所有必要的文件都由脚本生成,因此您可以简单地 copy/paste 将其转换为 bashscript 文件并 运行 它)
#!/bin/bash
# main function at bottom
#====================
#===HELPER METHOD====
#====================
# This method updates parameters with a new value. The replacement is performed linewise.
doUpdateParameterInFile()
{
local valueOfInterest=""
local newValue=""
local filePath=""
# 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]*\)[:].*//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
问候
唐普罗米洛
问题是您在使用 tee
覆盖 $filepath
的同时,sed
正在尝试读取它。如果 tee
先截断它然后 sed
得到一个空文件,你最终在另一端得到一个 0
长度的文件。
如果你有 GNU sed
,你可以使用 -i
标志让 sed
修改文件(其他版本支持 -i
但需要一个参数来它)。如果您的 sed
不支持它,您可以将其写入临时文件并将其移回原始名称,如
tmpname=$(mktemp)
sed -e "$lineToReplace a # $(date '+%Y-%m-%d %H:%M:%S'): replaced: $oldValue with: $newValue" -e "$lineToReplace a $newValue" -e "$lineToReplace d" "$filePath" > "$tmpname"
sudo mv "$tmpname" "$filePath"
或者如果您想保留原始权限,您可以这样做
sudo sh -c "cat '$tmpname' > '$filePath'"
rm "$tmpname"
或使用您的 tee
方法,例如
sudo tee "$filePath" >/dev/null <"$tmpname"
rm "$tmpname"
当使用 sed -e
更新配置文件的一些参数并将其通过管道传输到 | tee
(将更新的内容写入文件)时,这会随机中断并导致文件无效(尺寸 0)。
总而言之,这段代码用于更新参数:
# 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次
- 在原生系统(具有 Ubuntu 的 IntelNUC)上,此行为最多发生 15 次
有人可以解释为什么会这样吗?
这是一个功能齐全的脚本,您也可以在其中 运行 进行实验。 (所有必要的文件都由脚本生成,因此您可以简单地 copy/paste 将其转换为 bashscript 文件并 运行 它)
#!/bin/bash
# main function at bottom
#====================
#===HELPER METHOD====
#====================
# This method updates parameters with a new value. The replacement is performed linewise.
doUpdateParameterInFile()
{
local valueOfInterest=""
local newValue=""
local filePath=""
# 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]*\)[:].*//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
问候 唐普罗米洛
问题是您在使用 tee
覆盖 $filepath
的同时,sed
正在尝试读取它。如果 tee
先截断它然后 sed
得到一个空文件,你最终在另一端得到一个 0
长度的文件。
如果你有 GNU sed
,你可以使用 -i
标志让 sed
修改文件(其他版本支持 -i
但需要一个参数来它)。如果您的 sed
不支持它,您可以将其写入临时文件并将其移回原始名称,如
tmpname=$(mktemp)
sed -e "$lineToReplace a # $(date '+%Y-%m-%d %H:%M:%S'): replaced: $oldValue with: $newValue" -e "$lineToReplace a $newValue" -e "$lineToReplace d" "$filePath" > "$tmpname"
sudo mv "$tmpname" "$filePath"
或者如果您想保留原始权限,您可以这样做
sudo sh -c "cat '$tmpname' > '$filePath'"
rm "$tmpname"
或使用您的 tee
方法,例如
sudo tee "$filePath" >/dev/null <"$tmpname"
rm "$tmpname"