我有一个看起来像这样的代码段
while grep "{{SECRETS}}" /tmp/kubernetes/$basefile | grep -v "#"; do
grep -n "{{SECRETS}}" /tmp/kubernetes/$basefile | grep -v "#" | head -n1 | while read -r line ; do
lineno=$(echo $line | cut -d':' -f1)
spaces=$(sed "${lineno}!d" /tmp/kubernetes/$basefile | awk -F'[^ \t]' '{print length($1)}')
spaces=$((spaces-1))
# Delete line that had {{SECRETS}}
sed -i -e "${lineno}d" /tmp/kubernetes/$basefile
while IFS='' read -r secretline || [[ -n "$secretline" ]]; do
newline=$(printf "%*s%s" $spaces "" "$secretline")
sed -i "${lineno}i\ ${newline}" /tmp/kubernetes/$basefile
lineno=$((lineno+1))
done < "/tmp/secrets.yaml"
done
done
在/ tmp / kubernetes / $ basefile中,字符串{{SECRETS}}出现两次的百分比为100%。
几乎每次都可以完成。但是,很少有脚本在文件的第二个循环中出错。像这样,根据-x
...
IFS=
+ read -r secretline
+ [[ -n '' ]]
+ read -r line
exit code 1
工作时,设置-x如下所示,并继续正确处理文件。
...
+ IFS=
+ read -r secretline
+ [[ -n '' ]]
+ read -r line
+ grep '{{SECRETS}}' /tmp/kubernetes/deployment.yaml
+ grep -v '#'
对于只能偶尔发生这种情况我没有答案,所以我认为bash管道的并行性有些我不了解的东西。 grep -n "{{SECRETS}}" /tmp/kubernetes/$basefile | grep -v "#" | head -n1 | while read -r line ; do
中是否有某种方式可能导致执行混乱?根据该错误,似乎正在尝试读取一行,但是由于先前的命令无效而无法执行。但是在set -x输出中没有任何指示。
答案 0 :(得分:0)
该问题的可能原因是包含内部循环的管道同时读取和写入“基本文件”。参见How to make reading and writing the same file in the same pipeline always “fail”?。
解决问题的一种方法是在尝试更新文件之前先对其进行完全读取。试试:
basepath=/tmp/kubernetes/$basefile
secretspath=/tmp/secrets.yaml
while
line=$(grep -n "{{SECRETS}}" "$basepath" | grep -v "#" | head -n1)
[[ -n $line ]]
do
lineno=$(echo "$line" | cut -d':' -f1)
spaces=$(sed "${lineno}!d" "$basepath" \
| awk -F'[^ \t]' '{print length($1)}')
spaces=$((spaces-1))
# Delete line that had {{SECRETS}}
sed -i -e "${lineno}d" "$basepath"
while IFS='' read -r secretline || [[ -n "$secretline" ]]; do
newline=$(printf "%*s%s" $spaces "" "$secretline")
sed -i "${lineno}i\ ${newline}" "$basepath"
lineno=$((lineno+1))
done < "$secretspath"
done
(我引入了变量basepath
和secretspath
,以使代码更易于测试。)
顺便说一句,也可以使用纯Bash代码执行此操作:
basepath=/tmp/kubernetes/$basefile
secretspath=/tmp/secrets.yaml
updated_lines=()
is_updated=0
while IFS= read -r line || [[ -n $line ]] ; do
if [[ $line == *'{{SECRETS}}'* && $line != *'#'* ]] ; then
spaces=${line%%[^[:space:]]*}
while IFS= read -r secretline || [[ -n $secretline ]]; do
updated_lines+=( "${spaces}${secretline}" )
done < "$secretspath"
is_updated=1
else
updated_lines+=( "$line" )
fi
done <"$basepath"
(( is_updated )) && printf '%s\n' "${updated_lines[@]}" >"$basepath"
update_lines
数组中),但这不成问题,因为任何太大而无法存储在内存中的文件都肯定会太大而无法逐行处理与Bash联机。 Bash通常非常慢。spaces
保留了用于缩进的实际空格字符,而不是其数量。