Как объединить дубликаты записей, которые созданы для цикла - PullRequest
2 голосов
/ 10 января 2020

После моего предыдущего вопроса , который был закрыт - в основном у меня есть скрипт, который проверяет доступность пакетов на целевом сервере, целевой сервер и пакеты были сохранены в массиве.

declare -a prog=("gdebi" "firefox" "chromium-browser" "thunar")
declare -a snap=("beer2" "beer3")

# checkvar=$(
for f in "${prog[@]}"; do
  for connect in "${snap[@]}"; do
    ssh lg@"$connect" /bin/bash <<- EOF
      if dpkg --get-selections | grep -qE "(^|\s)"$f"(\$|\s)"; then
          status="[INSTALLED] [$connect]"
      else
          status=""
      fi
      printf '%s %s\n' "$f" "\$status"
EOF
    done
  done

С помощью одного из моих коллег я сделал несколько исправлений для оригинального скрипта, скрипт работал довольно хорошо - за исключением одной проблемы, вывод содержит повторяющиеся записи.

gdebi [INSTALLED] [beer2]
gdebi 
firefox [INSTALLED] [beer2]
firefox [INSTALLED] [beer3]
chromium-browser [INSTALLED] [beer2]
chromium-browser [INSTALLED] [beer3]
thunar 
thunar

Я знаю это так это нормальное поведение, так как for передает несколько серверов из массива snap, заставляя ssh отправляться на все два сервера.

Учитывая, что скрипт проверяет один и тот же пакет для двух серверов, я хочу, чтобы вывод быть объединенным.

  • Если beer2 имеет firefox пакетов, но beer3 нет.

    firefox [INSTALLED] [beer2]
    
  • Если * У 1026 * есть firefox пакетов, но beer2 нет.

    firefox [INSTALLED] [beer3]
    
  • Если оба пакета beer2 и beer3 имеют.

    firefox [INSTALLED] [beer2, beer3]
    

    или

    firefox [INSTALLED] [beer2] [beer3]
    
  • Если оба пакета beer2 и beer3 не имеют пакета, он вернется без дополнительных параметров.

    firefox
    

Звучит как легкая задача, но ради любви к Богу я не могу найти, как этого достичь, вот список вещей, которые у меня есть пробовал.

  • Попробуйте манипулировать циклами for.
  • Попробуйте выставить возвращаемое значение после одного успешного цикла (код выхода).
  • Попробуйте вложенный if , Все вышеперечисленное, похоже, не работает, я не пробовал изменять / манипулировать возвращаемой строкой, поскольку я не очень разбираюсь в какой-либо обработке текста, такой как: awk, sed, tr и многие другие .

Может кто-нибудь покажет, как это делается? Это действительно значило бы для меня мир.

Ответы [ 3 ]

1 голос
/ 10 января 2020

На основе @Ivan ответа

#!/bin/bash

prog=( "gdebi" "firefox" "chromium-browser" "thunar" )
snap=( "beer2" "beer3" )

# First, retrieve the list on installed program for each host
for connect in ${snap[@]}; do
    ssh lg@"$connect" /bin/bash >/tmp/installed.${connect} <<- EOF
      progs=( "${prog[@]}" )
      for prog in \${progs[@]}; do
          dpkg --get-selections | awk -v pkg=\$prog '\$1 == pkg && \$NF ~ /install/ {print \$1}'
      done
EOF
done

# Filter the previous results to format the output as you need
awk '{
    f = FILENAME;
    gsub(/.*\./,"",f);
    a[$1] = a[$1] "," f
}
END {
    for (i in a)
        print i ":[" substr(a[i],2) "]"
}' /tmp/installed.*

rm /tmp/installed.*

Пример вывода:

# With prog=( bash cat sed tail something firefox-esr )
firefox-esr:[localhost]
bash:[localhost,localhost2]
sed:[localhost,localhost2]
1 голос
/ 10 января 2020

Чистое Bash 4+ решение с использованием ассоциативного массива для хранения хостов, на которых установлена ​​программа:

#!/usr/bin/env bash

declare -A hosts_with_package=(["gdebi"]="" ["firefox"]="" ["chromium-browser"]="" ["thunar"]="")
declare -a hosts=("beer2" "beer3")

# Collect installed status
# Iterate all hosts
for host in "${hosts[@]}"; do
  # Read the output of dpkg --get-selections with searched packages
  while IFS=$' \t' read -r package status; do

    # Test weather package is installed on host
    if [ "$status" = "install" ]; then

      # If no host listed for package, create first entry
      if [ -z "${hosts_with_package[$package]}" ]; then
        # Record the first host having the package installed
        hosts_with_package["$package"]="$host"
      else
        # Additional hosts are concatenated as CSV
        hosts_with_package["$package"]="${hosts_with_package[$package]}, $host"
      fi
    fi
    # Feed the whole loop with the output of the dpkg --get-selections for packages names
    # Packages names are the index of the hosts_with_package array
  done < <(ssh "lg@$host" dpkg --get-selections "${!hosts_with_package[@]}")
done

# Output results
# Iterate the package name keys
for package in "${!hosts_with_package[@]}"; do
  # Print package name without newline
  printf '%s' "$package"
  # If package is installed on some hosts
  if [ -n "${hosts_with_package[$package]}" ]; then
    # Continue the line with installed hosts
    printf ' [INSTALLED] [%s]' "${hosts_with_package[$package]}"
  fi
  # End with a newline
  echo
done
1 голос
/ 10 января 2020

Вместо нескольких соединений s sh во вложенных циклах учтите это изменение

prog=( mysql-server apache2 php ufw )
snap=( localhost )


for connect in ${snap[@]}; do
    ssh $connect "
        progs=( ${prog[@]} )
        for prog in \${progs[@]}; do
            dpkg -l | grep -q \$prog && echo \"\$prog [INSTALLED]\" || echo \"\$prog\"
        done
    "
done
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...