Как скопировать файл, только если он не существует, иначе выдать ошибку?

aws s3 cp "dist/myfile" "s3://my-bucket/production/myfile"

Он всегда копирует myfile в s3 - я хотел бы скопировать файл ТОЛЬКО, если он не существует, иначе выдать ошибку. Как я могу это сделать? Или, по крайней мере, как я могу использовать awscli для проверки существования файла?


person user606521    schedule 17.11.2014    source источник
comment
Связано: GH-404, GH-1449 и GH-2874 на GitHub.   -  person kenorb    schedule 13.02.2018


Ответы (6)


Вы можете проверить существование файла, перечислив файл и посмотрев, возвращает ли он что-то. Например:

aws s3 ls s3://bucket/file.txt | wc -l

Это вернет ноль (без строк), если файл не существует.


Если вы хотите скопировать файл только в том случае, если он не существует, попробуйте sync команда, например:

aws s3 sync . s3://bucket/ --exclude '*' --include 'file.txt'

Это синхронизирует локальный файл с удаленным объектом, копируя его только в том случае, если он не существует или если локальный файл отличается от удаленного объекта.

person John Rotenstein    schedule 18.11.2014
comment
aws s3 ls s3://bucket/file.txt | wc -l не является точным - в нем будут перечислены файлы с этим префиксом, а не с точным именем. Рассмотрим следующее: stackoverflow.com/a/17456170/391618 - person T.K.; 12.06.2015
comment
Вы можете использовать его для файлов, если вы укажете параметры --include и --exclude, чтобы ограничить его только одним файлом в каталоге. Теперь я исправил приведенный выше пример, чтобы отразить это. - person John Rotenstein; 20.10.2016
comment
aws s3 ls ... | wc -l — ложноотрицательный результат, когда команда aws не существует или если возникает какая-либо другая ошибка. - person Messa; 09.09.2018

Итак, получается, что «синхронизация aws s3» не работает с файлами, а только с каталогами. Если вы дадите ему файл, вы получите... интересное... поведение, поскольку он обрабатывает все, что вы ему даете, как каталог и добавляет к нему косую черту. По крайней мере, aws-cli/1.6.7 Python/2.7.5 Darwin/13.4.0.

    %% date > test.txt
    %% aws s3 sync test.txt s3://bucket/test.txt
    warning: Skipping file /Users/draistrick/aws/test.txt/. File does not exist.

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

    file="test.txt"
    aws s3 sync --exclude '*' --include "$file" "$(dirname $file)" "s3://bucket/"

Обратите внимание на порядок исключения/включения — если вы измените его, он ничего не будет включать. И ваш исходный и включаемый пути должны быть разумными в отношении их соответствия, поэтому, возможно, $(базовое имя $file) подходит для --include, если вы используете полные пути... aws --debug s3 sync - ваш друг здесь чтобы увидеть, как оцениваются включения.

И не забывайте, что целью является ключ каталога, а не ключ файла.

Вот рабочий пример:

  %% file="test.txt"
  %% date >> $file
  %% aws s3 sync --exclude '*' --include "$file" "$(dirname $file)" "s3://bucket/"
  upload: ./test.txt to s3://bucket/test.txt/test.txt
  %% aws s3 sync --exclude '*' --include "$file" "$(dirname $file)" "s3://bucket/"
  %% date >> $file
  %% aws s3 sync --exclude '*' --include "$file" "$(dirname $file)" "s3://bucket/"
  upload: ./test.txt to s3://bucket/test.txt/test.txt

(теперь, если бы только был способ попросить aws s3 просто проверить контрольную сумму, поскольку он, кажется, всегда выполняет контрольные суммы в многокомпонентном стиле.

person keen    schedule 21.04.2015
comment
не согласно документам - docs.aws.amazon.com/ cli/latest/reference/s3/sync.html Syncs directories and S3 prefixes - person keen; 20.10.2016
comment
он также говорит: Рекурсивно копирует новые и обновленные файлы из исходного каталога в место назначения. И я пробовал, и кажется, что файлы передаются из локальной файловой системы в S3. То есть перенос в первый раз не означает их синхронизацию? - person olala; 20.10.2016
comment
вопрос (и этот ответ) касался синхронизации ОДНОГО файла, а не каталога, содержащего файлы. aws s3 sync не поддерживает указание пути к исходному файлу, только к исходному каталогу (см. первый блок кода в моем ответе). так что нет, aws s3 sync по-прежнему не поддерживает отдельные файлы. хотя это различие может показаться незначительным, оно оказывается значительным, если вы пытаетесь достичь этого. :) - person keen; 28.10.2016

Вы можете сделать это, перечислив и скопировав, если и только если список будет успешным.

aws s3 ls "s3://my-bucket/production/myfile" || aws s3 cp "dist/myfile" "s3://my-bucket/production/myfile"

Изменить: заменить && на || чтобы получить желаемый эффект, если список не работает, скопируйте

person aviggiano    schedule 09.07.2015
comment
хотя это правда, он создает 2 вызова s3 ​​с удвоенной возможностью отказа запроса и вдвое большей потребностью в обработке ошибок/повторных попыток... - person keen; 26.09.2015
comment
Это логически делает обратное, он копирует файл, если он уже есть, функционально он всегда копирует bc aws s3 ls не терпит неудачу, если файла там нет - person Pat Mc; 15.08.2016
comment
В случае, если мы хотим, чтобы сборка не удалась, явно с помощью инструментов CI/CD, таких как CircleCI. Помогает следующая модификация. (aws s3 ls "s3://my-bucket/production/myfile") && exit 1 || aws s3 cp "dist/myfile" "s3://my-bucket/production/myfile" - person Sudarshan Vidhate; 20.08.2019
comment
Это не работает должным образом. aws s3 ls соответствует не только файлам с точным именем, но и файлам, которые начинаются с того же имени. Таким образом, aws s3 ls s3://my-bucket/test также будет соответствовать test-foo. - person jelhan; 15.01.2021

Вы также можете проверить наличие файла с помощью подкоманды aws s3api head-object. Преимущество этого перед aws s3 ls заключается в том, что для этого требуется только разрешение s3:GetObject вместо s3:ListBucket.

$ aws s3api head-object --bucket ${BUCKET} --key ${EXISTENT_KEY}
{
    "AcceptRanges": "bytes",
    "LastModified": "Wed, 1 Jan 2020 00:00:00 GMT",
    "ContentLength": 10,
    "ETag": "\"...\"",
    "VersionId": "...",
    "ContentType": "binary/octet-stream",
    "ServerSideEncryption": "AES256",
    "Metadata": {}
}
$ echo $?
0

$ aws s3api head-object --bucket ${BUCKET} --key ${NON_EXISTENT_KEY}

An error occurred (403) when calling the HeadObject operation: Forbidden
$ echo $?
255

Обратите внимание, что код состояния HTTP для несуществующего объекта зависит от того, есть ли у вас разрешение s3:ListObject. Дополнительные сведения см. в документе по API:

  • Если у вас есть разрешение s3:ListBucket для корзины, Amazon S3 возвращает ошибку с кодом состояния HTTP 404 (нет такого ключа).
  • Если у вас нет разрешения s3:ListBucket, Amazon S3 возвращает ошибку с кодом состояния HTTP 403 (отказано в доступе).
person okapies    schedule 22.10.2020

ВЗЛОМ AWS

Вы можете запустить следующую команду, чтобы вызвать ОШИБКУ, если файл уже существует

  • Запустите команду синхронизации aws s3, чтобы синхронизировать файл с s3, он вернет скопированный путь, если файл не существует, или выдаст пустой вывод, если он выйдет
  • Запустите команду wc -c, чтобы проверить количество символов и вызвать ошибку, если вывод равен нулю

com=$(aws s3 sync dist/ s3://my-bucket/production/ | wc -c); if [[ $com -ne 0 ]]; затем выход 1; иначе выход 0; фи;

OR

#!/usr/bin/env bash
com=$(aws s3 sync dist s3://my-bucket/production/ | wc -c)
echo "hello $com"
if [[ $com -ne 0 ]]; then
echo "File already exists"
exit 1
else
echo "success"
exit 0
fi
person Anand Tripathi    schedule 14.08.2019

Я проголосовал за Авигжано. Используя его пример выше, я смог заставить это работать в моем файле Windows .bat. Если путь S3 существует, он выдаст ошибку и завершит пакетное задание. Если файл не существует, он продолжит выполнять функцию копирования. Надеюсь, это поможет кому-то.

:Step1

aws s3 ls s3://00000000000-fake-bucket/my/s3/path/inbound/test.txt && ECHO Could not copy to S3 bucket becasue S3 Object already exists, ending script. && GOTO :Failure

ECHO No file found in bucket, begin upload.

aws s3 cp Z:\MY\LOCAL\PATH\test.txt s3://00000000000-fake-bucket/my/s3/path/inbound/test.txt --exclude "*" --include "*.txt"


:Step2

ECHO YOU MADE IT, LET'S CELEBRATE

IF %ERRORLEVEL% == 0 GOTO :Success
GOTO :Failure

:Success
echo Job Endedsuccess
GOTO :ExitScript

:Failure
echo BC_Script_Execution_Complete Failure
GOTO :ExitScript

:ExitScript
person Kachopsticks    schedule 15.04.2020