AWS S3:如何使用bash检查存储桶中的文件是否存在

64

我想知道是否有可能检查某个存储桶中是否存在特定文件。

这是我找到的内容:

使用s3cmd检查S3存储桶中是否存在文件

它应该解决我的问题,但出于某种原因,它始终返回文件不存在,而实际上文件存在。此解决方案还有点过时,并且不使用doesObjectExist方法。

Amazon S3 Web服务中可用的所有方法的摘要

这提供了如何使用此方法的语法,但似乎我无法使其正常工作。

他们是否希望您创建一个布尔变量来保存方法的状态,或者该函数直接给出输出/抛出错误?

这是我当前在我的bash脚本中使用的代码:

existBool=doesObjectExist(${BucketName}, backup_${DomainName}_${CurrentDate}.zip)

if $existBool ; then
        echo 'No worries, the file exists.'
fi

我只用文件名进行了测试,而不是提供完整路径。但由于我得到的错误是语法错误,所以我可能只是使用方法不对。

希望有人可以帮助我,告诉我我做错了什么。

!编辑

最终我寻找了另一种方法来完成这个任务,因为使用doesObjectExist不是最快或最简单的方法。


2
这不是你要找的吗?这里 - imTachu
@TachúSalamanca 有点是的,谢谢!我已经快速阅读了答案,我认为我会寻找另一种检查文件是否存在的方法。可能有比使用“doesBucketExist”方法更快更容易的方法。 - J. Swaelen
11个回答

70

上次我看到的性能比较中,getObjectMetadata 是检查对象是否存在的最快方法。使用 AWS CLI,可以使用 head-object 方法,示例:

aws s3api head-object --bucket www.codeengine.com --key index.html

返回:

{
    "AcceptRanges": "bytes",
    "ContentType": "text/html; charset=utf-8",
    "LastModified": "Sun, 08 Jan 2017 22:49:19 GMT",
    "ContentLength": 38106,
    "ContentEncoding": "gzip",
    "ETag": "\"bda80810592763dcaa8627d44c2bf8bb\"",
    "StorageClass": "REDUCED_REDUNDANCY",
    "CacheControl": "no-cache, no-store",
    "Metadata": {}
}

5
我喜欢这个,因为它也验证了你正在检查的是一个对象。在这方面使用AWS S3 ls有点太宽容了。 - Karl Rosaen
对于那些正在寻找IF语句的人,这里是代码:not_exist=$(aws s3api head-object --bucket "bucket_name" --key "file/path.ext" >/dev/null 2>1; echo $?) if [ $not_exist == 255 ]; then echo "它不存在" else echo "它存在" fi - Dimitry Orgonov
1
测试255对我不起作用,但254可以。 - Elifarley
是的,截至2023年6月,成功时返回的值为254,而不是255 - Garret Wilson
1
@DimitryOrgonov 我认为你想使用 2>&1 而不是 2>1,对吧?否则这不会重定向到名为 1 的文件。请参见 https://dev59.com/Z3RA5IYBdhLWcg3wzhXZ#818284 。尽管如此,在 Windows 10 上的 Git Bash 中,2>&1 甚至 2>'&1' 对我都没有用(git 版本为 2.40.1.windows.1),所以我切换到了 >/dev/null 2>/dev/null,我认为它做的事情相同,并且应该更兼容。 - Garret Wilson

43

参考 @DaveMaple 和 @MichaelGlenn 的回答,这是我正在使用的条件:

aws s3api head-object --bucket <some_bucket> --key <some_key> || not_exist=true
if [ $not_exist ]; then
  echo "it does not exist"
else
  echo "it exists"
fi

1
这似乎是回显响应,有没有一种方法可以仅分配$not_exists变量而不显示结果或错误? - John Mellor
你可以考虑将 echo 命令替换为任何你喜欢的赋值语句,怎么样? - ItayB
我不确定我理解你的观点,或者也许是你误解了我的观点?我并不是说它会回显“它不存在”,这显然可以很容易地改变。我是说在回显“它不存在”之前,它会将“调用HeadObject操作时发生错误(404):未找到”打印到终端上。有没有办法防止它打印404消息? - John Mellor
2
@JohnMellor 在第一条命令中添加 > /dev/null 2>&1aws s3api head-object --bucket <some_bucket> --key <some_key> > /dev/null 2>&1 || not_exist=true - ItayB
1
@ItayB 这会导致 if 语句不起作用,看起来它总是假设为 false,因为没有错误输出。对我来说添加这个并没有起作用,所以这是我的假设 - 我可能是错的。 - sojim2

28
请注意,即使答案被接受,“aws s3 ls”也不能完全工作。 它按前缀搜索,而不是按特定对象键搜索。 当有人通过在文件名末尾添加“1”来重命名文件时,我通过艰难的方式发现了这一点,但存在性检查仍然会返回True。
(尝试将此作为注释添加,但还没有足够的声望。)

2
我刚刚注意到了完全相同的行为,这就是我提出这个问题的原因。 - BiBi

8

一个简单的方法是使用aws s3 ls

exists=$(aws s3 ls $path_to_file)
if [ -z "$exists" ]; then
  echo "it does not exist"
else
  echo "it exists"
fi

10
抱歉如果我的措辞听起来太严厉,但这不应该作为答案被接受,原因在其他两个帖子中已经解释过了。 - nodakai
2
如果您有相同前缀的文件,则此解决方案无法正常工作。例如,当存储桶中存在s3://bucket/file.txt.gz文件时,s3://bucket/file.txt将被视为已存在。使用head-object方法可能是正确的选择,但它会强制您将s3:// URI拆分为不同的部分。 - Marius Grigaitis

8

我通常使用set -eufo pipefail,因为这种方式更适合我,不需要担心未设置的变量或整个脚本退出。

object_exists=$(aws s3api head-object --bucket $bucket --key $key || true)
if [ -z "$object_exists" ]; then
  echo "it does not exist"
else
  echo "it exists"
fi

5

这个语句将会返回一个truefalse的响应:

aws s3api list-objects-v2 \
  --bucket <bucket_name> \
  --query "contains(Contents[].Key, '<object_name>')"

所以,针对问题中提供的例子:
aws s3api list-objects-v2 \
  --bucket ${BucketName} \
  --query "contains(Contents[].Key, 'backup_${DomainName}_${CurrentDate}.zip')"

我喜欢这种方法,因为:

  • The --query option uses the JMESPath syntax for client-side filtering and it is well documented here how to use it.

  • Since the --query option is build into the aws cli, no additional dependencies need to be installed.

  • You can first run the command without the --query option, like:

      aws s3api list-objects-v2 --bucket <bucket_name> 
    

    That returns a nicely formatted JSON, something like:

{
    "Contents": [
        {
            "Key": "my_file_1.tar.gz",
            "LastModified": "----",
            "ETag": "\"-----\"",
            "Size": -----,
            "StorageClass": "------"
        },
        {
            "Key": "my_file_2.txt",
            "LastModified": "----",
            "ETag": "\"----\"",
            "Size": ----,
            "StorageClass": "----"
        },
        ...
    ]
}
  • This then allows you to design an appropriate query. In this case you want to check if the JSON contains a list Contents and that an item in that list has a Key equal to your file (object) name:

    --query "contains(Contents[].Key, '<object_name>')"
    

https://awscli.amazonaws.com/v2/documentation/api/latest/reference/s3api/list-objects-v2.html 表示:“返回一个存储桶中的一些或全部对象(最多1,000个)。” 因此,如果您的存储桶中有超过1,000个项目,即使对象存在,由于分页,这可能会给您带来 false - karfau
https://awscli.amazonaws.com/v2/documentation/api/latest/reference/s3api/list-objects-v2.html中提到:“每次请求返回存储桶中一些或全部(最多1,000个)对象。”因此,如果您的存储桶中有超过1000个项目,即使对象存在,也可能返回`false`,这是由于分页的原因。 - undefined

3
一个更简单的解决方案,但不如其他AWS S3 API那样复杂,是使用退出代码。
aws s3 ls <full path to object>

如果对象不存在,则返回非零的返回代码。如果对象存在,则返回0。

2

从awscli中,我们可以使用lsgrep命令,例如:

aws s3 ls s3://<bucket_name> | grep 'filename'

这可以包含在Bash脚本中。


2
这个可以运行,但是当文件数量很高时非常慢。 - man.2067067

1
受上面的答案启发,我也使用这个来检查文件大小,因为我的存储桶被一些返回404的脚本破坏了。但需要使用jq
minsize=100
s3objhead=$(aws s3api head-object \
  --bucket "$BUCKET" --key "$KEY" 
  --output json || echo '{"ContentLength": 0}')

if [ $(printf "%s" "$s3objhead" | jq '.ContentLength') -lt "$minsize" ]; then
  # missing or small
else
  # exist and big
fi

0
这是一个简单的POSIX shell函数(因此在Bash中也适用),基于@Dmitri Orgonov的答案:
s3_key_exists() {
  aws >/dev/null 2>&1 s3api head-object --bucket "$1" --key "$2"
  test $? != 254
}

以下是如何使用它:

s3_key_exists myBucket path/to/my/file.txt \
  && echo "It's there!" \
  || echo "Not found..."

现在,如果你手头的是一个S3路径而不是一个存储桶和一个键:

s3_file_exists() {
  local bucketAndKey="$(s3_bucket_and_key "$1")"
  s3_key_exists "${bucketAndKey%:*}" "${bucketAndKey#*:}"
}
s3_bucket_and_key() {
  local input="${1#/}"; local bucket="${input%%/*}"; local key="${input#$bucket}"
  echo "$bucket:${key#/}"
}

这里是一个使用示例:

s3_file_exists /myBucket/path/to/my/file.txt \
  && echo "It's there!" \
  || echo "Not found..."

或者...

s3_file_exists myBucket/path/to/my/other-file.txt \
  && echo "It's there too!" \
  || echo "Not found either..."

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接