Очень хороший инструмент для проверки мертвых ссылок (например, ссылок, указывающих на ошибку 404) - это wget --spider. Однако у меня есть несколько другой вариант использования, когда я создаю статический веб-сайт и хочу проверить неработающие ссылки перед загрузкой. Точнее, хочу проверить оба:

  • Относительные ссылки, например <a href="some/file.pdf">file.pdf</a>

  • Абсолютные ссылки, скорее всего, на внешние сайты, такие как <a href="http://example.com">example</a>.

Я попробовал wget --spyder --force-html -i file-to-check.html, который читает локальный файл, считает его HTML и следует по каждой ссылке. К сожалению, он не может работать с относительными ссылками в локальном HTML-файле (ошибки с Cannot resolve incomplete link some/file.pdf). Я пробовал использовать file://, но wget его не поддерживает.

В настоящее время у меня есть взлом, основанный на запуске локального веб-сервера через python3 http.serve и проверке локальных файлов через HTTP:

python3 -m http.server &
pid=$! 
sleep .5
error=0
wget --spider -nd -nv -H -r -l 1 http://localhost:8000/index.html || error=$? 
kill $pid
wait $pid
exit $error

Я не очень доволен этим по нескольким причинам:

  • Мне нужен этот sleep .5, чтобы дождаться готовности веб-сервера. Без него скрипт не сработает, но я не могу гарантировать, что хватит 0,5 секунды. Я бы предпочел иметь способ запустить команду wget, когда сервер будет готов.

  • И наоборот, это kill $pid кажется уродливым.

В идеале python3 -m http.server должен иметь возможность запускать команду, когда сервер готов, и отключаться после завершения команды. Это звучит выполнимо, если написать немного Python, но мне было интересно, существует ли более чистое решение.

Я что-нибудь пропустил? Есть ли лучшее решение? Я упоминаю wget в своем вопросе, потому что он делает почти то, что я хочу, но использование wget не является для меня требованием (как и python -m http.server). Мне просто нужно иметь что-нибудь, что легко запускать и автоматизировать в Linux.

16
Matthieu Moy 14 Мар 2018 в 15:45

2 ответа

Лучший ответ

Так что я думаю, вы идете в правильном направлении. Я бы использовал wget и python, так как это два доступных варианта во многих системах. И хорошо то, что он выполняет свою работу за вас. Теперь вы хотите услышать Serving HTTP on 0.0.0.0 от stdout этого процесса.

Итак, я бы начал процесс, используя что-то вроде ниже

python3 -u -m http.server > ./myserver.log &

Обратите внимание на -u, который я использовал здесь для небуферизованного вывода, это действительно важно

Теперь следующий ожидает появления этого текста в myserver.log

timeout 10 awk '/Serving HTTP on 0.0.0.0/{print; exit}' <(tail -f ./myserver.log)

Итак, 10 секунд - ваше максимальное время ожидания здесь. А остальное говорит само за себя. Теперь о вашем kill $pid. Я не думаю, что это проблема, но если вы хотите, чтобы это было больше похоже на то, как это делает пользователь, я бы изменил его на

kill -s SIGINT $pid

Это будет эквивалентно обработке вами CTRL+C после запуска программы. Также я бы обработал SIGINT мой сценарий bash, используя что-то вроде ниже

https://unix.stackexchange.com/questions/313644/execute-command-or-function-when-sigint-or-sigterm-is-send-to-the-parent-script/313648

Вышеупомянутое в основном добавляет ниже в начало сценария bash, чтобы обработать вас, убивая сценарий с помощью CTRL+C или внешнего сигнала уничтожения.

#!/bin/bash
exit_script() {
    echo "Printing something special!"
    echo "Maybe executing other commands!"
    trap - SIGINT SIGTERM # clear the trap
    kill -- -$$ # Sends SIGTERM to child/sub processes
}

trap exit_script SIGINT SIGTERM
10
Tarun Lalwani 16 Мар 2018 в 19:08

Ответ Таруна Лалвани правильный, и, следуя приведенным там советам, можно написать чистый и короткий сценарий оболочки (опираясь на Python и awk). Другое решение - полностью написать сценарий на Python, что даст немного более подробный, но, возможно, более чистый сценарий. Сервер может быть запущен в потоке, затем выполняется команда проверки веб-сайта и, наконец, сервер отключается. Нам больше не нужно ни анализировать текстовый вывод, ни посылать сигнал внешнему процессу. Таким образом, ключевыми частями сценария являются:

def start_server(port,
                 server_class=HTTPServer,
                 handler_class=SimpleHTTPRequestHandler):
    server_address = ('', port)
    httpd = server_class(server_address, handler_class)
    thread = threading.Thread(target=httpd.serve_forever)
    thread.start()
    return httpd

def main(cmd, port):
    httpd = start_server(port)
    status = subprocess.call(cmd)
    httpd.shutdown()
    sys.exit(status)

Я написал немного более продвинутый сценарий (с небольшим количеством синтаксического анализа параметров командной строки поверх этого) и опубликовал его как: https://gitlab.com/moy/check-links

0
Matthieu Moy 4 Апр 2018 в 12:56