使用sed处理数组差异更新SSH密钥时的误删问题求助
需求与问题描述
需要实现authorized_keys文件中SSH密钥的自动化更新(因限制无法使用Ansible),脚本需完成以下步骤:
- 读取文件中的现有密钥
- 对比密钥,找出不在指定允许列表中的密钥
- 打印待删除密钥用于日志,并删除这些密钥
当前脚本存在异常:当允许列表array1与文件中读取的array2_from_file存在匹配密钥时,会误删最后一个匹配的密钥(例如示例中的key2)。原脚本代码如下:
array1=( "ssh-ed25519 AAAAC3... key1" "ssh-ed25519 AAAAC3NzaC1l key2" ); path=".ssh/authorized_keys" mapfile -t array2_from_file < "$path" #updated here removed <(cat "$path") escape_sed_pattern() { printf '%s\n' "$1" | sed 's/[\\/&]/\\&/g' } for key in "${array2_from_file[@]}"; do if ! [[ " ${array1[@]} " =~ " $key " ]]; then echo "To be deleted: $key" escaped_key=$(escape_sed_pattern "$key") # Remove the key from the authorized_keys file sed -i "/$escaped_key/d" "$path" fi done
问题原因
原脚本使用[[ " ${array1[@]} " =~ " $key " ]]的匹配方式,会将允许列表数组拼接成一个空格分隔的字符串。当密钥位于拼接字符串的末尾时,其后方没有空格,导致带前后空格的" $key "无法匹配,被误判为非允许密钥,从而被错误删除。此外,若密钥本身包含特殊正则字符,这种匹配方式也可能出现意外问题。
修复后的脚本
改用精确遍历匹配替代字符串正则匹配,同时先批量收集待删除密钥再统一处理,逻辑更可靠:
array1=( "ssh-ed25519 AAAAC3... key1" "ssh-ed25519 AAAAC3NzaC1l key2" ); path=".ssh/authorized_keys" mapfile -t array2_from_file < "$path" escape_sed_pattern() { printf '%s\n' "$1" | sed 's/[\\/&]/\\&/g' } # 批量收集所有待删除的密钥 to_delete=() for key in "${array2_from_file[@]}"; do match_found=0 # 遍历允许列表做精确相等判断 for allowed_key in "${array1[@]}"; do if [[ "$key" == "$allowed_key" ]]; then match_found=1 break fi done if [[ $match_found -eq 0 ]]; then to_delete+=("$key") fi done # 打印日志并执行删除操作 for key in "${to_delete[@]}"; do echo "To be deleted: $key" escaped_key=$(escape_sed_pattern "$key") sed -i "/$escaped_key/d" "$path" done
修复说明
- 采用循环遍历允许列表的方式做精确字符串相等判断,彻底避免了数组拼接导致的匹配误差
- 先收集所有待删除密钥再统一处理,逻辑更清晰,也避免了边遍历边修改文件可能带来的潜在问题
- 保留了原有的sed转义函数,确保密钥中的特殊字符不会破坏sed命令的执行
内容的提问来源于stack exchange,提问作者Drey'
相关产品推荐
相关产品推荐

