pobierz plik przez http tylko jeśli został zmieniony od ostatniej aktualizacji

20

Muszę pobrać plik z serwera HTTP, ale tylko jeśli zmienił się od czasu ostatniego pobrania (np. Przez If-Modified-Sincenagłówek). Potrzebuję też użyć niestandardowej nazwy pliku na moim dysku.

Jakiego narzędzia mogę użyć do tego zadania w systemie Linux?


wget -Nnie można użyć, ponieważ -Nnie można go używać z -O.

cweiske
źródło
Dlaczego nie pobrać pliku, a następnie zmienić jego nazwę?
Julian Knight
.. ponieważ narzędzie nadal musi być w stanie sprawdzić, czy zasób HTTP zmienił się od ostatniego pobrania? Będzie to trudne, jeśli nazwa pliku zostanie zmieniona, a zatem nie będzie już istnieć w miejscu, w którym narzędzie tego oczekuje.
cweiske
Przepraszam, rzuciłem ten komentarz, zobacz moją odpowiedź.
Julian Knight

Odpowiedzi:

26

Rozważ użycie curlzamiast wget:

curl -o "$file" -z "$file" "$uri"

man curl mówi:

-z/ --time-cond <wyrażenie daty>

(HTTP / FTP) Poproś o plik, który został zmodyfikowany później niż podana godzina i data, lub plik, który został zmodyfikowany przed tym czasem. Wyrażenie daty może być wszelkiego rodzaju ciągami dat lub jeśli nie pasuje do żadnych wewnętrznych, próbuje zamiast tego uzyskać czas z podanej nazwy pliku.

Jeśli $fileniekoniecznie musi istnieć wcześniej, musisz uzależnić użycie -zflagi, używając test -e "$file":

if test -e "$file"
then zflag="-z '$file'"
else zflag=
fi
curl -o "$file" $zflag "$uri"

(Pamiętaj, że nie cytujemy $zflagtutaj rozszerzenia , ponieważ chcemy, aby zostało podzielone na 0 lub 2 tokeny).

Jeśli twoja powłoka obsługuje tablice (np. Bash), mamy bezpieczniejszą i czystszą wersję:

if test -e "$file"
then zflag=(-z "$file")
else zflag=()
fi
curl -o "$file" "${zflag[@]}" "$uri"
Toby Speight
źródło
7

Przełącznik wget -Npobiera plik tylko wtedy, gdy został zmieniony, więc możliwe byłoby użycie prostego -Nprzełącznika, który pobierze plik, jeśli będzie to konieczne, ale pozostawi go z niewłaściwą nazwą. Następnie utwórz twardy link za pomocą ln -Ppolecenia, aby połączyć go z „plikiem” o poprawnej nazwie. Połączony plik ma te same metadane co oryginał.

Jedynym ograniczeniem jest to, że nie można mieć twardych łączy między granicami systemu plików.

Julian Knight
źródło
Do wielu celów symboliczne łącze może być wystarczające - chyba że tożsamość i-węzła ma znaczenie dla pytającego.
Toby Speight
1
wget jest lepszym narzędziem do tego zadania. Sprawdza znacznik czasu ORAZ rozmiar pliku, którego nie zawija (7.38.0). Ponadto, wget kończy się z non-0 na 4xx / 5xx, podczas gdy curl tak naprawdę nie przejmuje się kodami serwera.
schieferstapel
4

Skrypt Python 3.5+ do zawijania polecenia curl:

import argparse
import pathlib

from subprocess import run
from itertools import chain

parser = argparse.ArgumentParser()
parser.add_argument('url')
parser.add_argument('filename', type=pathlib.Path)
args = parser.parse_args()

run(chain(
    ('curl', '-s', args.url),
    ('-o', str(args.filename)),
    ('-z', str(args.filename)) if args.filename.exists() else (),
))
Sirex
źródło
To jest niesamowite! TIL chain:)
John Oxley
1

Podobne podejście do „ sprawdzania daty ” (z „curl - time-cond”) polegałoby na pobraniu zgodnie z porównaniem wielkości pliku, tj. Pobraniu tylko wtedy, gdy plik lokalny ma inny rozmiar niż plik zdalny .

Jest to przydatne na przykład, gdy proces pobierania nie powiódł się w środku , a zatem lokalny pobrany plik otrzymuje nowszą datę niż plik zdalny, ale w rzeczywistości jest uszkodzony i konieczne jest ponowne pobranie:

local_file_size=$([[ -f ${FILE_NAME} ]] && wc -c < ${FILE_NAME} || echo "0")
remote_file_size=$(curl -sI ${FILE_URL} | awk '/Content-Length/ { print $2 }' | tr -d '\r' )

if [[ "$local_file_size" -ne "$remote_file_size" ]]; then
    curl -o ${FILE_NAME} ${FILE_URL}
fi

Opcja „curl -z / --time-cond” (która została zasugerowana w innej odpowiedzi) nie spowoduje pobrania pliku zdalnego w tym przypadku (ponieważ plik lokalny ma nowszą datę), ale ten skrypt „ sprawdzania rozmiaru ” to zrobi!

Noam Manos
źródło