【问题标题】:Check if a remote file exists in bash检查bash中是否存在远程文件
【发布时间】:2018-02-04 13:08:23
【问题描述】:

我正在使用此脚本下载文件:

parallel --progress -j16 -a ./temp/img-url.txt 'wget -nc -q -P ./images/ {}; wget -nc -q -P ./images/ {.}_{001..005}.jpg'

是否可以不下载文件,只需在远程端检查它们,如果存在则创建一个虚拟文件而不是下载?

类似:

if wget --spider $url 2>/dev/null; then
  #touch img.file
fi

应该可以,但我不知道如何将此代码与 GNU Parallel 结合起来。

编辑:

根据 Ole 的回答,我编写了这段代码:

#!/bin/bash
do_url() {
  url="$1"
  wget -q -nc  --method HEAD "$url" && touch ./images/${url##*/}   
  #get filename from $url
  url2=${url##*/}
  wget -q -nc  --method HEAD ${url%.jpg}_{001..005}.jpg && touch ./images/${url2%.jpg}_{001..005}.jpg
}
export -f do_url

parallel --progress -a urls.txt do_url {}

它可以工作,但是对于某些文件会失败。我找不到一致性,为什么它适用于某些文件,为什么它对其他文件失败。也许它与最后一个文件名有关。第二个 wget 尝试访问当前的 url,但之后的 touch 命令根本不会创建所需的文件。首先 wget 总是(正确地)下载没有 _001.jpg、_002.jpg 的主图像。

示例 urls.txt:

http://host.com/092401.jpg(正常工作,_001.jpg.._005.jpg 已下载) http://host.com/HT11019.jpg(不行,只下载主图)

【问题讨论】:

  • 使用--method HEAD 发送HEAD 请求而不是GET 请求。
  • @iamauser 你是认真的吗?在那个问题中,关于在远程端检查文件序列的词在哪里?
  • 是的,我是。我认为您的问题应该是如何遍历一系列文件,因为这是wget/curl 每次调用的输入。
  • 在提供了一些答案之后完全改变您的问题并不好。这使得这里提供的大多数答案看起来都是错误的。但是,问题是您在提供问题后更改了问题。

标签: bash wget gnu-parallel


【解决方案1】:

很难理解你真正想要完成什么。让我试着改写你的问题。

我有 urls.txt 包含:

http://example.com/dira/foo.jpg
http://example.com/dira/bar.jpg
http://example.com/dirb/foo.jpg
http://example.com/dirb/baz.jpg
http://example.org/dira/foo.jpg

example.com 上存在这些 URL:

http://example.com/dira/foo.jpg
http://example.com/dira/foo_001.jpg
http://example.com/dira/foo_003.jpg
http://example.com/dira/foo_005.jpg
http://example.com/dira/bar_000.jpg
http://example.com/dira/bar_002.jpg
http://example.com/dira/bar_004.jpg
http://example.com/dira/fubar.jpg
http://example.com/dirb/foo.jpg
http://example.com/dirb/baz.jpg
http://example.com/dirb/baz_001.jpg
http://example.com/dirb/baz_005.jpg

example.org 上存在这些 URL:

http://example.org/dira/foo_001.jpg

给定urls.txt,除了原始 URL 之外,我还想生成带有 _001.jpg .. _005.jpg 的组合。例如:

http://example.com/dira/foo.jpg

变成:

http://example.com/dira/foo.jpg
http://example.com/dira/foo_001.jpg
http://example.com/dira/foo_002.jpg
http://example.com/dira/foo_003.jpg
http://example.com/dira/foo_004.jpg
http://example.com/dira/foo_005.jpg

然后我想在不下载文件的情况下测试这些 URL 是否存在。由于有许多 URL,我想并行执行此操作。

如果 URL 存在,我希望创建一个空文件。

(版本 1):我希望在 dir images 中的类似目录结构中创建空文件。这是必需的,因为一些图像具有相同的名称,但在不同的目录中。

所以创建的文件应该是:

images/http:/example.com/dira/foo.jpg
images/http:/example.com/dira/foo_001.jpg
images/http:/example.com/dira/foo_003.jpg
images/http:/example.com/dira/foo_005.jpg
images/http:/example.com/dira/bar_000.jpg
images/http:/example.com/dira/bar_002.jpg
images/http:/example.com/dira/bar_004.jpg
images/http:/example.com/dirb/foo.jpg
images/http:/example.com/dirb/baz.jpg
images/http:/example.com/dirb/baz_001.jpg
images/http:/example.com/dirb/baz_005.jpg
images/http:/example.org/dira/foo_001.jpg

(版本 2):我想要在目录 images 中创建的空文件。可以这样做,因为所有图像都有唯一的名称。

所以创建的文件应该是:

images/foo.jpg
images/foo_001.jpg
images/foo_003.jpg
images/foo_005.jpg
images/bar_000.jpg
images/bar_002.jpg
images/bar_004.jpg
images/baz.jpg
images/baz_001.jpg
images/baz_005.jpg

(版本 3):我想要在目录 images 中创建的空文件,其名称来自 urls.txt。可以这样做是因为 _001.jpg .. _005.jpg 中只有一个存在。

images/foo.jpg
images/bar.jpg
images/baz.jpg
#!/bin/bash

do_url() {
  url="$1"

  # Version 1:
  # If you want to keep the folder structure from the server (similar to wget -m):
  wget -q --method HEAD "$url" && mkdir -p images/"$2" && touch images/"$url"

  # Version 2:
  # If all the images have unique names and you want all images in a single dir
  wget -q --method HEAD "$url" && touch images/"$3"

  # Version 3:
  # If all the images have unique names when _###.jpg is removed and you want all images in a single dir
  wget -q --method HEAD "$url" && touch images/"$4"

}
export -f do_url

parallel do_url {1.}{2} {1//} {1/.}{2} {1/} :::: urls.txt ::: .jpg _{001..005}.jpg

GNU Parallel 每个作业需要几毫秒。当你的工作这么短时,开销会影响时间。如果您的所有 CPU 内核都没有 100% 运行,您可以并行运行更多作业:

parallel -j0 do_url {1.}{2} {1//} {1/.}{2} {1/} :::: urls.txt ::: .jpg _{001..005}.jpg

您还可以“展开”循环。这将为每个 URL 节省 5 开销:

do_url() {
  url="$1"
  # Version 2:
  # If all the images have unique names and you want all images in a single dir
  wget -q --method HEAD "$url".jpg && touch images/"$url".jpg
  wget -q --method HEAD "$url"_001.jpg && touch images/"$url"_001.jpg
  wget -q --method HEAD "$url"_002.jpg && touch images/"$url"_002.jpg
  wget -q --method HEAD "$url"_003.jpg && touch images/"$url"_003.jpg
  wget -q --method HEAD "$url"_004.jpg && touch images/"$url"_004.jpg
  wget -q --method HEAD "$url"_005.jpg && touch images/"$url"_005.jpg
}
export -f do_url

parallel -j0 do_url {.} :::: urls.txt

最后你可以运行超过 250 个作业:https://www.gnu.org/software/parallel/man.html#EXAMPLE:-Running-more-than-250-jobs-workaround

【讨论】:

  • 图片不能全部保存到images/目录下吗?我有一个很长的 URL,这个脚本创建了一个奇怪的文件夹结构。
  • 已添加images
  • 我需要“版本 2”。它工作正常,谢谢。我做了一个小基准测试,我对速度感到失望。它比下载文件慢得多,如果你有兴趣,这里是结果:pastebin.ca/3971248。你怎么看,瓶颈在哪里?
  • 250 个作业 (-j0) 的运行时间现在减半,但不幸的是它仍然比 wget --no-clog 慢(如果存在则不要下载)。但这是一个很好的答案,我将来肯定会使用它。最新示例有些奇怪:$ ls images/ _001.jpg _002.jpg _003.jpg _004.jpg _005.jpg。
【解决方案2】:

您可以使用curl 来检查您正在解析的网址是否存在,而无需下载任何文件:

if curl --head --fail --silent "$url" >/dev/null; then
    touch .images/"${url##*/}"
fi

解释:

  • --fail 将在请求失败时使退出状态非零。
  • --head 将避免下载文件内容
  • --silent 将避免检查本身发出状态或错误。

要解决“循环”问题,您可以这样做:

urls=( "${url%.jpg}"_{001..005}.jpg )
for url in "${urls[@]}"; do
    if curl --head --silent --fail "$url" > /dev/null; then
        touch .images/${url##*/}
    fi
done

【讨论】:

    【解决方案3】:

    据我所知,您的问题实际上并不是关于如何使用wget 来测试文件是否存在,而是关于如何在 shell 脚本中执行正确的循环。

    这是一个简单的解决方案:

    urls=( "${url%.jpg}"_{001..005}.jpg )
    for url in "${urls[@]}"; do
        if wget -q --method=HEAD "$url"; then
            touch .images/${url##*/}
        fi
    done
    

    它的作用是使用--method=HEAD 选项调用Wget。使用HEAD 请求,服务器将简单地报告文件是否存在,而不返回任何数据。

    当然,对于大型数据集,这是非常低效的。您正在为您尝试的每个文件创建与服务器的新连接。相反,正如另一个答案中所建议的,您可以使用 GNU Wget2。使用 wget2,您可以并行测试所有这些,并使用新的--stats-server 选项查找所有文件的列表以及服务器提供的特定返回代码。例如:

    $ wget2 --spider --progress=none -q --stats-site example.com/{,1,2,3}                                                             
    Site Statistics:
    
      http://example.com:
        Status    No. of docs
           404              3
             http://example.com/3  0 bytes (identity) : 0 bytes (decompressed), 238ms (transfer) : 238ms (response)
             http://example.com/1  0 bytes (gzip) : 0 bytes (decompressed), 241ms (transfer) : 241ms (response)
             http://example.com/2  0 bytes (identity) : 0 bytes (decompressed), 238ms (transfer) : 238ms (response)
           200              1
             http://example.com/  0 bytes (identity) : 0 bytes (decompressed), 231ms (transfer) : 231ms (response)
    

    您甚至可以将此数据打印为 CSV 或 JSON,以便于解析

    【讨论】:

    • @Finally 我能够编译 Wget2。为了进行快速测试,我运行了:wget2 --spider --progress=none --stats-site=csv:stat.csv ${url%.jpg}_{001..005}.jpg。它查询 URL 很好(example.com/hello_001.jpg 等),但在 stat.csv 中只有一个,最后一个查询 + 我认为是主图像(exampe.com/hello.jpg))我仍然需要运行Wget2 一次。
    • 我在想象 Wget2 是否应该比 Wget&Parallel 工作得更快。目前 Wget&Parallel&TouchDummyFile 比 Wget&Parallel&DownloadFiles 慢。基准测试结果在@OleTange 答案下。
    • 如果图像非常小(~5kB),并行+触摸可能比仅下载文件慢。这是因为您仍然需要为您正在测试的每个文件建立与服务器的新连接,然后开始一个新进程。这有时比仅下载所述文件要慢。在这种情况下,Wget2 确实应该更快,因为它只需要建立一次连接。
    • 您看到的统计数据问题是一个错误。我会做一个报告,它应该会在一两天内修复。到那时,如果你不使用 json 或 csv,你仍然可以看到完整的统计数据
    • 谢谢,修复后我会反馈的。
    【解决方案4】:

    只是循环遍历名称?

    for uname in ${url%.jpg}_{001..005}.jpg
    do
      if wget --spider $uname 2>/dev/null; then
        touch ./images/${uname##*/}
      fi
    done
    

    【讨论】:

    • 我问了这个问题,因为我不想下载任何文件,只需在远程端检查并制作一个本地虚拟文件(同名)(如果存在)。
    【解决方案5】:

    您可以通过 ssh 发送命令以查看远程文件是否存在,如果存在则对其进行 cat 它:

    ssh your_host 'test -e "somefile" && cat "somefile"' > somefile
    

    也可以试试支持 glob 表达式和递归的 scp。

    【讨论】:

    • 不,远程主机只有 http。
    • curl - 我可以告诉你文件是否存在
    猜你喜欢
    • 2011-07-24
    • 1970-01-01
    • 1970-01-01
    • 2011-05-07
    • 2015-01-29
    • 1970-01-01
    • 1970-01-01
    • 2011-02-06
    相关资源
    最近更新 更多