Более элегантное решение для цикла «для»

2

здесь моя проблема.

Позвольте мне описать ситуацию: я работаю на большом количестве серверов, которые содержат большое количество клиентов; все они имеют веб-пространство, но только некоторые из них используют Wordpress.

По соображениям безопасности я хотел бы получить версию Wordpress всех установок Wordpress для определенного сервера (я бы применил этот процесс к другим после того, как нашел решение).

Я нашел файл /wp-includes/version.php, который содержит строку "$ wp_version = 'xxx" - кажется идеальным для того, чтобы следить за установками клиентов и уведомлять их, когда их версия опасна.

Я начал писать о командной строке, которую я бы затем помещал в cron для получения результатов один раз в месяц:

for files in $(find /home -type f -iwholename "*/wp-includes/version.php") ; do domain=$(echo $files | awk -F'domains/' '{print $2}' | awk -F'/' '{print $1}') ; version=$(grep "wp_version = " $files | awk -F'=' '{print $2}') ; echo "$domain : $version" ; done | grep -v "4.8" | sed -e "s/'/ /g" -e "s/;/ /g"

Вышеуказанная строка дает результат такого рода:

domain.com : 4.1.3
another.com : 4.7.6
again.com : 4.7.6

Похоже, я получил результат, который хотел, но, как вы можете видеть, эта командная строка ДЕЙСТВИТЕЛЬНО длинна, и даже если я пробовал разные версии, я не нашел элегантный вариант.

Конечно, я пробовал разные версии до этого результата, и это единственный, который работает правильно; и я не смог найти что-то подобное, но лучше. Реальная проблема, я думаю, связана с определениями переменных из результата; Я изучаю awk, но это что-то вроде уродливого, даже если это потрясающий инструмент. Команда sed в конце очищает исходный результат, который выглядит следующим образом:

domain.com : '4.1.3';
another.com : '4.7.6';
again.com : '4.7.6';

Wp-includes/version.php выглядит примерно так:

<?php
/**
 * The WordPress version string
 *
 * @global string $wp_version
 */
$wp_version = '3.9'; # for example
...

Не могли бы вы рассказать мне, видите ли вы какую-либо "оптимизацию"? Это больше связано с образованием, чем с реальной проблемой.

Изменение: наиболее оптимизированная версия на данный момент является следующей, на мой взгляд (достаточно ясно для легкой модификации, чистого результата, а не так трудно понять функции):

for f in $(find /home -iwholename "*/wp-includes/version.php") ; do d=$(echo $f | awk -F'domains/' '{print $2}' | cut -d"/" -f1) ; v=$(grep '$wp_version = ' $f | sed -e 's/$wp_version = //g' -e "s/'//g" -e 's/;//g') ; echo "$v - $d" ; done
  • 0
    Добро пожаловать в переполнение стека. Не могли бы вы также опубликовать более подробную информацию о вводе файла?
  • 0
    Спасибо за Ваш ответ ! Обновлен образцом файла version.php.
Теги:
awk
sed

3 ответа

0
Лучший ответ

Я не знаю, что вы намерены более элегантным, что является довольно субъективным критерием, но вот несколько советов о более эффективном использовании некоторых команд:

  1. find более мощное, чем то, что вы используете. Он может выполнять поиск с использованием регулярных выражений (-regex), выполнять команды в найденных файлах (действие -exec)... Примеры:

    $ re1=".*/domains/[^/]*"
    $ find /home -type d -regex "$re1" -printf "%f : \n"
    domain.com : 
    another.com : 
    again.com : 
    
    $ re2="*/domains/*/wp-includes/version.php"
    $ re3="wp_version"
    $ find /home -type f -ipath "$re2" -exec grep "$re3" '{}' \;
    $wp_version = '1.1'; # for example
    $wp_version = '2.2'; # for example
    $wp_version = '3.3'; # for example
    

    Параметр {} -exec заменяет путь найденного файла. Обратите внимание, что команды -exec должны быть -exec которая должна быть экранирована (\;), чтобы предотвратить ее интерпретацию оболочкой. find также может комбинировать несколько сгруппированных (\(...\)) тестовых выражений -o (-o, -a):

    $ find /home \( -type d -regex "$re1" -printf "%f : " \) -o \
        \( -type f -ipath "$re2" -exec grep "wp_version" '{}' \; \)
    domain.com : $wp_version = '1.1'; # for example
    another.com : $wp_version = '2.2'; # for example
    again.com : $wp_version = '3.3'; # for example
    
  2. grep тоже, если это недавняя версия GNU grep, более мощна, чем вы используете. Он имеет частичную поддержку регулярных выражений perl (опция -P) и может выводить только соответствующую часть (-o). Пример:

    $ re3="\\\$wp_version\s*=\s*'\K\d+(\.\d+)*"
    $ grep -Po "$re3" version.php
    1.1
    

Если мы склеиваем все это вместе и используем переменные bash для лучшей читаемости (печально известный чит, чтобы сократить конечную командную строку), мы можем использовать следующее:

$ re1=".*/domains/[^/]*"
$ re2="*/domains/*/wp-includes/version.php"
$ re3="\\\$wp_version\s*=\s*'\K\d+(\.\d+)*"
$ find /home \( -type d -regex "$re1" -printf "%f : " \) -o \
    \( -type f -ipath "$re2" -exec grep -Po "$re3" '{}' \; \)
domain.com : 1.1
another.com : 2.2
again.com : 3.3

Но все это очень сложно. Более того, он работает только в том случае, если для домена есть один и только один файл версии, и он не отфильтровывает версии более поздними, чем 4.8. Цикл и немного черной магии bash, вероятно, намного проще, легче понять и поддерживать, и легко расширить с помощью фильтрации версий:

re1="*/domains/*/wp-includes/version.php"
re2="\\\$wp_version\s*=\s*'\K\d+(\.\d+)*"

while read -d $'\0' f; do
  [[ $f =~ .*/domains/([^/]+)/.* ]] && domain="${BASH_REMATCH[1]}"
  version=$( grep -Po "$re2" "$f" )
  [[ ! $version =~ 4\.8(\.\d+)* ]] && printf "%s : %s\n" "$domain" "$version"
done < <( find . -type f -ipath "$re1" -print0 )

Обратите внимание на использование разделителя \0 (вместо новых строк) в read (-d $'\0') и find -Print0). Это может помочь, если у вас есть странные имена каталогов.

  • 0
    Под «более элегантным» я говорил о лучшем использовании некоторых команд, которые дали бы тот же результат, что и длинная. Спасибо за объяснения и примеры; Я попробую это на работе и возможно осуществлю это. Выглядит отлично и более оптимизировано.
1
version=$( awk -F"[\"']" '/^\$wp_version/{print $2}' "$files" )

Получить версию

$ cat version.php 
<?php
/**
 * The WordPress version string
 *
 * @global string $wp_version
 */
$wp_version = '3.9'; # for example
...

# used field separator either single quote or double quote
$ awk -F"[\"']" '/^\$wp_version/{print $2}' version.php 
3.9

Для домена вы можете изменить

domain=$(echo $files | awk -F'domains/' '{print $2}' | awk -F'/' '{print $1}') ;

к

domain=$(awk -F'domains/' '{split($2,tmp,/\//); print tmp[1]}' <<<"$files")

Предположим, если у вас есть что-то вроде

$ files="path/to/domains/domain.com/wordpress/wp-includes/version.php"
$ awk -F'domains/' '{split($2,tmp,/\//); print tmp[1]}' <<<"$files"
domain.com
  • 1
    Привет ; выглядит явно лучше, чем моя труба, делаю заметки. Спасибо!
  • 0
    для домена domain=$(awk -F'domains/' '{split($2,tmp,/\//); print tmp[1]}' <<<"$files")
Показать ещё 4 комментария
0

Вы можете значительно упростить свою логику и сделать ее более эффективной и читаемой:

config_re='/wp_includes/version.php$'
while IFS=: read -r file match; do
    [[ $file =~ $config_re ]] || continue # skip if it is some other version.php

    # logic to extract the domain from file variable
    IFS=/ read -ra domain_arr <<< "$file"
    domain="${domain[2]}" # adjust the array index as needed

    # logic to extract version from matched part
    IFS='=' read -ra version_arr <<< "$match"
    version="${version_arr[1]}"
done < <(find /path/to/domains -type f -name version.php -exec grep '$wp_version' {} /dev/null \;)
  • find/path/to/domains -type f -name version.php -exec grep -H '$wp_version' {} \; извлекает все матчи за один снимок; grep -H гарантирует, что полный путь к файлу версии (обратите внимание, что полный путь в аргументе find directory имеет префикс в соответствие на выходе

Я не тестировал этот код. Я готов исправить это, если вы столкнетесь с какими-либо проблемами.

  • 1
    Спасибо за ваш ответ: у меня нет достаточного опыта в разработке, чтобы действительно понять этот синтаксис; Раньше я работал с действительно линейными командными строками. Будет практиковаться на этом примере.
  • 0
    По сути, команда find создает разделенный запятыми вывод (/ path / to / domain / file: $ wp_version ...) и цикл чтения считывает их в file двух переменных и match с IFS, установленным в : который является входным разделителем. Внутри цикла мы извлекаем домен из полного пути к файлу и версию из совпадения. Это все.
Показать ещё 3 комментария

Ещё вопросы

Сообщество Overcoder
Наверх
Меню