根据列表检查文件是否存在

根据列表检查文件是否存在

我有一个“命令”文本文件,它在每一行发出数据文件下载命令。我将命令文件发送到 bash。但是,一小部分下载会失败。这是我用来找出缺失内容的算法:

  1. 下载后,我返回命令文件并检查每个下载文件是否存在。
  2. 如果下载不存在,我会将命令行复制到新的命令文件中。
  3. 我留下了一个新的命令文件用于剩余的下载。

这是我实现该算法的 bash 脚本:

  1 #!/bin/bash
  2 while read line
  3 do
  4         for item in $line
  5         do
  6                 if [[ $item == *out_fname* ]]; then 
  7                         splitline=(${item//=/ })   
  8                         target_file=${splitline[1]}
  9                         if [ ! -f $target_file ]; then
 10                                 echo $line >> stillneed.txt
 11                         fi
 12                 fi
 13         done
 14 done < "$@"

问题:这很好用,但是有没有更好的算法或实现(也许使用 bash 以外的东西)?我所做的只是让 bash 做人类必须做的事情。但 Unix 似乎总是有更好的做事方式......

答案1

看来您正在寻找“out_fname=”,而不仅仅是“out_fname”。

我要么混合使用 awk 和 shell,要么使用 python。在 awk/shell 中:

awk '{for(i=0;i<NF;i++) {if (index($i, 'out_fname=')) {split($i,A,/=/);print A[i]}}' "$@" |
    while read filename; do
        if [ ! -f $filename ]; then echo $filename; fi
    done > stillneed.txt

在Python中:

import fileinput, os
stillneed = open("stillneed.txt", "w")
for line in fileinput.input():
    for filename in [l.split('=')[1] for l in line.split() if l.find('out_fname=')!=-1]:
        if not os.path.exists(filename):
            print >>stillneed, filename

答案2

不确定它是否有帮助,但我有一个函数可以重试命令,直到它们返回成功:

retry () {
    local delay=1 n

    if ! [[ $1 = *[^0-9]* ]]; then
        # TODO allow delay=0 (prevents Ctrl-C)
        if (($1 > 0)); then
            delay=${1:1}
        fi
        shift
    fi

    # run command
    while ! "$@"; do
        echo "retrying in ${delay}s"
        for ((n=delay; n>0; n--)); do
            sleep 1 || return
        done
    done
}; export -f retry

答案3

不要在初始下载脚本完成后检查缺少的内容,而是考虑向所述下载脚本添加一些检查。我没有测试以下内容,我只是把它写在我的脑海中:

cat files_to_download|while read file; 
do
    SUCCESS="False"
    while [[ $SUCCESS == "False" ]];
    do
        wget $file;
        if [[ $? -eq 0 ]];
        then
            SUCCESS="True"
        fi
   done
done

答案4

我建议echo在下载失败时复制该行,而不是随后通过解析文件中的每一行来检查文件名:

[[ -f $1 ]] || { echo "$1 not found" >&2; exit 1; }
while read -r line; do
    $line || echo "$line" >> stillneed
done < "$1"

这会更有效,也意味着您将来无需担心任何奇怪的文件名(例如,其中有空格)。

如果您想改进现有方法,可以使用标准参数扩展:

for f; do
    while read -r line; do
        for item in $line; do
            [[ $item = out_fname=* ]] || continue
            [[ -f ${item#out_fname=} ]] || echo "$line"
            break # assuming one fname per line
        done
    done < "$f"
done > stillneed

..但考虑一下会发生什么:out_fname='foo bar.ext'.另外,请记住,这会在事件发生后检查每一行,而我们可以在运行该命令时检查该命令是否有效。

stillneed整个循环打开一次,效率更高;我没有在第一个代码片段中这样做,因为我们很可能希望看到下载命令的输出。这里只有测试,没有运行外部命令,因此打开文件一次是有意义的。 (请注意,使用>将在开始时截断文件;我曾经for f允许多个输入文件作为位置参数:如果需要,添加上面相同的内容应该很容易。)

我必须强调的一件事是 quoting:echo "$line"echo $line.一般来说,报价全部参数扩展(这包括变量),除非您确定要进行字段分割。

相关内容