【问题标题】:Count only successful move attempts只计算成功的移动尝试
【发布时间】:2019-06-07 17:34:23
【问题描述】:

问题是我无法弄清楚如何仅计算移动文件的“成功”尝试。

我的脚本将 $PATH 中的目录作为目标,并尝试从指定目录中获取文件(直到目录为空),以随机顺序将它们打乱并移至 $PATH 中的目录。

x=0
while [[ $(ls -A Directory/jpg | wc -l) -gt 0 ]]
do
  for i in "${destination[@]}"
  do
    shuf -zn1 -e Directory/jpg/*.jpg | xargs -0 mv -vt $i/somedir 2>/dev/null
    x=$((x + 1))
  done
done

我的“x”计数器实际上大于“目录/jpg”中的文件数,我想“x”仅代表被复制文件的确切数量。

【问题讨论】:

    标签: bash


    【解决方案1】:

    为了测试,我稍微修改了脚本:

    #!/bin/bash                                                                                                             
    destination[0]=dest1
    destination[1]=dest2
    destination[2]=dest3
    x=0
    while [[ $(ls -A source | wc -l) -gt 0 ]]
    do
      for i in "${destination[@]}"
      do
        shuf -zn1 -e source/* | xargs -0 mv -vt $i
        x=$((x + 1))
      done
    done
    
    echo $x
    

    因此,source 中有三个目标目录和不同数量的文件,我注意到 x 始终是 destination 数组元素数量的倍数。

    删除2>/dev/null 揭示了一个隐藏的问题:

    ... # other mv output
    renamed 'source/file14' -> 'dest2/file14'
    renamed 'source/file16' -> 'dest3/file16'
    renamed 'source/file5' -> 'dest1/file5'
    mv: cannot stat 'source/*': No such file or directory
    mv: cannot stat 'source/*': No such file or directory
    

    计算最后两次失败的动作!

    for i in "${destination[@]}" 的循环次数总是等于destination 数组中元素的数量。也就是说,如果destination 具有三个元素,则循环将始终使用i = 1i = 2i = 3 执行,即使没有那么多文件。 for 循环将始终将x 增加destination 中的元素数。因此,x 将大于移动的文件数。 (除非文件数是目标数组中元素数的倍数)

    这可以通过在 for 循环内部添加一个测试来检查是否存在更多要移动的文件来解决:

    #!/bin/bash                                                                                                             
    destination[0]=dest1
    destination[1]=dest2
    destination[2]=dest3
    x=0
    while [[ $(ls -A source | wc -l) -gt 0 ]]
    do
      for i in "${destination[@]}"
      do
        shuf -zn1 -e source/* | xargs -0 mv -vt $i
        x=$((x + 1))
        [[ $(ls -A source | wc -l) -eq 0 ]] && break # exit the loop if there are no more files to move
      done
    done
    

    【讨论】:

    • 太棒了。它完美地工作。我知道在我的代码中错误是导致“x”增加的原因,但不知道如何测试成功条件。再次,对我来说很有价值的一课!
    • -a source/*实际上做的是测试source目录下是否有一个名为*的文件,很可能是false。结合!,它使它成为一个true 语句,并且您的for 循环在第一次迭代后总是会中断,有效地将所有文件移动到第一个目标文件夹。
    • 正确,我认为 shell 会扩展 * 并且在测试中没有仔细查看 mv 输出。一个有效的破坏测试是$(ls -A source | wc -l) -eq 0。我删除了我之前的评论并更新了代码以反映这一点。
    【解决方案2】:

    考虑使用 && 来检查 mv 命令的结果并有条件地执行递增。

    这是 bash 的成语:

    command && other-command-if-command-was successful
    

    在上面的示例中,仅当命令返回零退出状态时才会执行 other-command。这是我对您的程序的编辑:

    x=0
    while [[ $(ls -A Directory/jpg | wc -l) -gt 0 ]]
    do
      for i in "${destination[@]}"
      do
        shuf -zn1 -e Directory/jpg/*.jpg | xargs -0 mv -vt $i/somedir 2>/dev/null && x=$((x + 1))
      done
    done
    

    上面的代码可能错误地假设 xargs 将返回 mv 的退出状态。我认为它不会。相反,在这种情况下最好完全避免 xargs 。毕竟, shuf 只返回一个条目。下面是对代码的较大修改,效果会更好:

    x=0
    while [[ $(ls -A Directory/jpg | wc -l) -gt 0 ]]
    do
      for i in "${destination[@]}"
      do
        random_file=$(shuf -zn1 -e Directory/jpg/*.jpg) 
        mv "$random_file" "$i/somedir" && x=$((x + 1))
      done
    done
    

    【讨论】:

    • 话虽如此,报告的退出状态很可能是 xargs 的退出状态。我需要验证 xargs 是否会传播 mv 状态。不太可能。我想我会建议我们完全避免使用 xargs。我将添加新代码并留下此评论,以便对讨论有所帮助。
    猜你喜欢
    • 2011-03-19
    • 1970-01-01
    • 2020-10-20
    • 1970-01-01
    • 1970-01-01
    • 2017-12-13
    • 1970-01-01
    • 2016-03-16
    • 1970-01-01
    相关资源
    最近更新 更多