不需要的多个重复行 - sed

不需要的多个重复行 - sed

不需要的多个重复行 -sed

有一个任务是从 postgresql 数据库中选择地址,然后通过 ssh 连接到所选地址,并在每个主机上的配置中的正确位置添加特定行。

例如,有一个文件中间包含值,之后需要插入具有不同值的新行:

# grep hostname config.cfg

hostname, port = 2234: Vasya
hostname, ip address: John, 192.168.1.5
hostname: Andrew

接下来,例如,在该行之后

hostname: Andrew

我需要在新行中插入新值:

hostname: Sheglina

我这样做(首先尝试一个主机):

for HOST in 192.168.1.1; do ssh $HOST "for VALUE in $(nl -ba /root/config.cfg|grep hostname | tail -1 | awk '{print $1}'); do sed -i $VALUE'a\hostname: Sheglina'/root/config.cfg; done"; done

该命令适用于虚拟机。

# grep hostname config.cfg

hostname, port = 2234: Vasya
hostname, ip address: John, 192.168.1.5
hostname: Andrew
hostname: Sheglina

但是当我在真实服务器上执行操作时,通过过滤器选择 IP 地址,然后我尝试应用此命令:

for HOST in $(psql -U postgres name_db -c "SELECT name, ip_address FROM servers ORDER BY ip_address;" | grep -i 'moscow' | awk '{print $5}'); do ssh $HOST "for VALUE in $(nl -ba /root/config.cfg|grep hostname | tail -1 | awk '{print $1}'); do sed -i $VALUE'a\hostname: Sheglina '/root/config.cfg;done"; done

然后主机名:Sheglina 行在每一行中重复/root/config.cfg,不在一处。我不需要它。我不明白为什么这个命令对虚拟机有效,但对服务器却不起作用。即使我转到集群中的一个单独的节点并为同一节点发出命令,仍然存在重复的行...可能是什么原因?

在真实服务器中的样子:

# grep hostname config.cfg
hostname: Sheglina
hostname: Sheglina
hostname: Sheglina
hostname: Sheglina
...

具有主机名值的旧行保留在文件中,但由于某种原因,Sheglina 行在每行中重复

答案1

你让事情变得比需要的更加复杂。不要尝试用一行又长、复杂且难以阅读的行来完成所有这些工作,而是将其分解为多个步骤并按顺序执行。

这个脚本需要做两件事

  1. 获取 IP 地址列表,其中名称字段类似于moscowpsql(使用不区分大小写的匹配)
  2. 紧接在包含 的行之后,使用ssh和 insert hostname: Sheglinainto连接到它们中的每一个。/root/config.cfghostname: andrew

例如:

#!/bin/bash

city='moscow'
sql="SELECT DISTINCT ip_address FROM servers WHERE name ILIKE '$city'"

# get the query output into an array called 'hosts'
hosts=( $(psql -U postgres name_db -t --csv -c "$sql") )

for host in "${hosts[@]}"; do
  ssh "$host" "sed -i -e 's/^hostname: andrew/&\nhostname: Sheglina/i' /root/config.cfg"
done

这使用 psql 的内置选项以正确的格式提取所需的数据。 -t抑制页眉和页脚输出,并--csv请求逗号分隔格式。 SQL 查询仅选择匹配的值 - 无需将其通过管道传输到 grep 或 awk - 并且在查询中使用“DISTINCT”来防止重复。

接下来,ssh用于连接到每个主机,sed 用于搜索hostname: andrewhostname: Sheglina在其后面追加。


注意:在 shell 中正确引用可能非常困难,尤其是当您需要将带引号的值传递给也需要自己引用的其他程序(如 psql 或 ssh)时。您可以使用printf '%q'将字符串转换为可与此类命令一起使用的正确引用的格式,但 postgres 使用自己的引用/转义样式,除非您通过在引用前面加上 . 来告诉它使用类似 posix 的样式E。这告诉 postgres 引用的字符串可能包含 posix 样式的字符串转义。

E'quoted-string'而不只是'quoted-string'.

例如,如果 $city 包含任何需要由 shell 和 postgres 转义的字符,请使用类似以下内容的内容:

sql="SELECT DISTINCT ip_address FROM servers WHERE name ILIKE E'%"$(printf "%q" "$city")"'"

相关内容