Как получить имя скачанного файла через wget или curl: подробный гайд

Если вы работаете с shell-скриптами, рано или поздно столкнётесь с задачей: скачать файл по URL и сохранить его оригинальное имя, которое выдаёт сервер. Казалось бы, и wget, и curl умеют автоматически определять имя из заголовков (вроде Content-Disposition), но вот загвоздка – эти команды не возвращают имя файла в stdout. А значит, нельзя просто взять и присвоить его переменной, как в примере ниже:

newfilename=$(wget ...) # Так не сработает

Почему так? Возможно, разработчики опасаются инъекций (если имя файла содержит опасные символы), но сама команда всё равно создаёт файл с этим именем. Получается парадокс: имя используется, но скрипту его не видно. К счастью, есть обходные пути – от парсинга логов до расширенных атрибутов. Давайте разберём их по шагам.

Решение для curl: магия –write-out и xattr

Curl – гибкий инструмент, и его опция –write-out позволяет выводить в stdout любые метаданные о загрузке. Например, чтобы получить имя сохранённого файла, используйте плейсхолдер %{filename_effective}:

newfilename=$(curl -L -O -J --silent --remote-header-name --write-out "%{filename_effective}" "${myurl}")

Кстати, ключ -O (заглавная буква O) включает сохранение под оригинальным именем, а -J активирует парсинг Content-Disposition. Но если сервер не передаёт этот заголовок, имя может быть взято из URL (например, после последнего слеша).

А ещё curl умеет записывать URL в расширенные атрибуты файла (xattr) – это удобно для аудита. Просто добавьте опцию --xattr, и ссылка сохранится в атрибут user.xdg.origin.url:

curl --xattr -O -J "${myurl}"

Теперь проверить URL можно командой:

xattr -p user.xdg.origin.url filename.txt

Wget: парсим логи и настраиваем xattr

С wget история немного сложнее. Команда не выводит имя файла напрямую, но сохраняет его в лог. Вот как извлечь его через sed:

logfile=$(mktemp /tmp/wgetlog.XXXXXX)  
wget --content-disposition --trust-server-names -o "$logfile" "${myurl}"  
newfilename=$(sed -n 's/^Saving to: ‘(.*)’/1/p' "$logfile")  
rm "$logfile"

Обратите внимание на кавычки в логе – они могут быть «умными» (типографскими), поэтому в sed используется ‘ и ’ вместо обычных. Если столкнётесь с ошибкой «No match», попробуйте сменить локаль на UTF-8:

LC_ALL=C.utf-8 wget ...

Для автоматического сохранения URL в xattr добавьте в ~/.wgetrc строку:

~/.wgetrc  

xattr = on

Теперь wget запишет URL в атрибут user.xdg.origin.url, и вам не придётся делать это вручную через xattr -w.

А если сервер не отдаёт имя файла?

Иногда сервер не передаёт Content-Disposition, и тогда имя берётся из URL. Но что, если в URL нет явного имени (например, генерация файла через скрипт)? Тут поможет таймстамп:

wget --timestamping "${myurl}"

Команда проверит дату изменения файла на сервере и скачает его только при обновлении. А если файл уже есть локально, просто обновит его. Правда, в этом случае имя файла будет зависеть от предыдущих загрузок – возможно, стоит комбинировать методы.

И напоследок: не забывайте про безопасность. Если имя файла формируется удалённо, всегда проверяйте его на наличие нежелательных символов (вроде / или ;). Например, обрежьте всё, кроме букв, цифр и точек:

safe_name=$(echo "$newfilename" | sed 's/[^а-яА-Яa-zA-Z0-9.]//g')

(Да, иногда проще переименовать файл, чем полагаться на сервер).

Новое
Интересное