在目录中找到最新的文件

时间:2015-05-18 19:16:20

标签: bash file

我需要在目录中找到最新的文件 我目前使用的是:

cd /mypath/ && ls -t | head -n1

问题是,我经常创建文件,有时, 在'相同'时间创建了2个文件,这意味着当我 执行上面的行我会错过其中一个文件。

有人可以帮忙吗?

2 个答案:

答案 0 :(得分:0)

抱歉原来的混音。这是一个简短的脚本,可以在目录中找到最新的文件(作为第一个参数给出)。使用stat -c %Y查找自纪元以来的秒数。然后与目录中的所有其他文件进行比较,如果时差(td)小于10秒,则输出文件名(您可以调整差异以满足您的需求)

尝试一下:

#!/bin/bash

path="${1:-.}"  ## set path (default .)

[ "${path:(-1):1}" = '/' ] && path="${path%/}"  ## strip trailing '/'

oldifs="$IFS"   ## save old IFS, set to break on newline
IFS=$'\n'
fnarray=( $(ls -rt "$path" | tail -n5) )        ## read newest 5 into array
IFS="$oldifs"

for ((i=$((${#fnarray[@]} - 1)); i >= 0; i--)); do  ## make sure newest is file
    [ -f  "${path}/${fnarray[i]}" ] && fn="${path}/${fnarray[i]}" && break
done
[ -d "$fn" ] && { echo "error: no file found in newest 5 entries"; exit 1; }

for i in "${path}"/*; do                ## for each file in path
    [ -f "$i" ] || continue             ## if not a file skip
    td=$(( $(stat -c %Y "$i") - $(stat -c %Y "$fn") ))  ## time difference in sec
    (( td < 0 )) && td=$(( td * -1 ))   ## takes abs value
    (( td < 10 )) && echo "$i"          ## if less than 10 seconds, output file
done

exit 0

注意:我遇到的原始问题是由于目录中的最新条目可能是子目录(不需要)。由于ls ..| tail..返回结果的方式,这提出了挑战。所以基本上我把最后5个作为黑客攻击(你应该设置一个5的初始尝试,如果fn仍然是一个目录,将它增加两倍并再试一次......直到你用尽所有文件为止目录..或首先做一个简单的find /path/ -type f以确保存在文件叹息,但这会完全模糊对最旧代码的测试。

我不喜欢解决方案的另一个问题是,在bash中比较stat次并不是天生的快。所以这在大型目录上不会闪电般快。 (适用于&lt; 1000条目)。以完全相同的时间返回可能的最新文件是一个很好的挑战,没有我能找到的快速现成的解决方案。

完整脚本

对目标目录中所有条目进行迭代测试的完整实现,从最新的向后工作,找到第一个文件(不是dir)来比较时间,类似于以下内容。 注意:代码已经过测试,但可能仍有需要修复的极端情况:

#!/bin/bash

path="${1:-.}"          ## set path (default .)

[ "${path:(-1):1}" = '/' ] && path="${path%/}"  ## strip trailing '/'

[ -d "$path" ] || {     ## validate path
    echo "error: invalid path provided. '$path' not found."
    exit 1;
}

declare -i n=5      ## initial entries to read into array if fn is directory
declare -i oldn=1   ## save index for increased loop efficiency

## get number of files in target path
nfiles=$(wc -l < <( find "$path" -maxdepth 1 -type f ) )

(( $nfiles < 1 )) && {  ## test files present in path or exit
    echo "error: no files found in target directory"
    exit 1
}

oldifs="$IFS"   ## save old IFS, set to break on newline
IFS=$'\n'

fn="${path}/$(ls -rt "$path" | tail -n1)"        ## find most recent file

while [ -d "$fn" ]; do                           ## while fn is a directory
    fnarray=( $(ls -rt "$path" | tail -n$n) )    ## read newest n entries into array
    for ((i=$((${#fnarray[@]} - oldn)); i >= 0; i--)); do  ## make sure newest is file
        [ -f  "${path}/${fnarray[i]}" ] && fn="${path}/${fnarray[i]}" && break
    done
    (( n == nfiles )) && break                  ## if all entries tried, bail
    oldn=$n                                     ## update oldn to skip next try
    [ -f "$fn" ] && break || n=$(( n * 2 ))     ## if file, done, else n=n*2
    (( n > nfiles )) && n=nfiles                ## if n > nfiles, n=nfiles
done
[ -d "$fn" ] && {
    echo "error: something bad happened, no files found in target directory"
    exit 1
}
IFS="$oldifs"

for i in "${path}"/*; do                ## for each file in path
    [ -f "$i" ] || continue             ## if not a file skip
    td=$(( $(stat -c %Y "$fn") - $(stat -c %Y "$i") ))  ## time difference in sec
    (( td < 0 )) && td=$(( td * -1 ))   ## takes abs value
    (( td < 10 )) && echo "$i"          ## td less than X seconds, output filename
done

exit 0

不知何故,这似乎有点过分......

答案 1 :(得分:0)

似乎您不仅需要最新的文件,而且您希望仅报告每个新文件一次。以下脚本通过保留跟踪上次报告内容的$reftime文件来实现此目的:

cd "$1"
reftime=/tmp/newest-file-name

next_file=
prev_file=$(cat "$reftime" 2> /dev/null)
while true; do
  read -r filename
  if [[ "$prev_file" ]]; then
    [[ "$filename" = "$prev_file" ]] && break # we've gotten to reported files
  else
    [[ -z "$filename" ]] && break # we've read past the last file
  fi
  next_file="$filename"
done < <(ls -t)

if [[ "$next_file" ]]; then
    # Update file $reftime with time and name of the file we're about to offer
    echo "$next_file" > "$reftime"
    touch -r "$next_file" "$reftime"

    echo "$next_file"
fi