Bash函数可以找到最新的文件匹配模式

时间:2011-05-04 15:31:16

标签: linux bash

在Bash中,我想创建一个函数,它返回与某个模式匹配的最新文件的文件名。例如,我有一个文件目录,如:

Directory/
   a1.1_5_1
   a1.2_1_4
   b2.1_0
   b2.2_3_4
   b2.3_2_0

我想要以'b2'开头的最新文件。我怎么在bash中这样做?我需要在我的~/.bash_profile脚本中使用此功能。

9 个答案:

答案 0 :(得分:180)

ls命令有一个参数-t可按时间排序。然后,您可以使用head -1抓住第一个(最新的)。

ls -t b2* | head -1

但要注意:Why you shouldn't parse the output of ls

我的个人观点:解析ls只有在文件名可以包含空格或换行等有趣字符时才会有危险。如果您可以保证文件名不包含有趣的字符,那么解析ls是非常安全的。

如果您正在开发一个脚本,该脚本在许多不同情况下由许多系统上的许多人运行,那么我非常建议不要解析ls

以下是如何“正确”:How can I find the latest (newest, earliest, oldest) file in a directory?

unset -v latest
for file in "$dir"/*; do
  [[ $file -nt $latest ]] && latest=$file
done

答案 1 :(得分:10)

findls的组合适用于

  • 没有换行符的文件名
  • 文件数量不是很大
  • 文件名不是很长

解决方案:

find . -name "my-pattern" ... -print0 |
    xargs -r -0 ls -1 -t |
    head -1

让我们分解一下:

使用find,我们可以匹配所有有趣的文件:

find . -name "my-pattern" ...

然后使用-print0我们可以将所有文件名安全地传递到ls,如下所示:

find . -name "my-pattern" ... -print0 | xargs -r -0 ls -1 -t

ls -t将按修改时间(最新的第一个)对文件进行排序,并在一行中打印一个。您可以使用-c按创建时间排序。 注意:这会破坏包含换行符的文件名。

最后head -1获取排序列表中的第一个文件。

注意: xargs对参数列表的大小使用系统限制。如果此尺寸超出,xargs将多次调用ls。这将打破排序,也可能打破最终输出。运行

xargs  --show-limits

检查系统的限制。

注2:如果您不想通过子文件夹搜索文件,请使用find . -maxdepth 1 -name "my-pattern" -print0

注3:正如@starfry所指出的那样 - -r的{​​{1}}参数阻止xargs的调用,如果没有匹配的文件ls -1 -t。谢谢你的建议。

答案 2 :(得分:5)

这是所需Bash功能的可能实现:

# Print the newest file, if any, matching the given pattern
# Example usage:
#   newest_matching_file 'b2*'
# WARNING: Files whose names begin with a dot will not be checked
function newest_matching_file
{
    # Use ${1-} instead of $1 in case 'nounset' is set
    local -r glob_pattern=${1-}

    if (( $# != 1 )) ; then
        echo 'usage: newest_matching_file GLOB_PATTERN' >&2
        return 1
    fi

    # To avoid printing garbage if no files match the pattern, set
    # 'nullglob' if necessary
    local -i need_to_unset_nullglob=0
    if [[ ":$BASHOPTS:" != *:nullglob:* ]] ; then
        shopt -s nullglob
        need_to_unset_nullglob=1
    fi

    newest_file=
    for file in $glob_pattern ; do
        [[ -z $newest_file || $file -nt $newest_file ]] \
            && newest_file=$file
    done

    # To avoid unexpected behaviour elsewhere, unset nullglob if it was
    # set by this function
    (( need_to_unset_nullglob )) && shopt -u nullglob

    # Use printf instead of echo in case the file name begins with '-'
    [[ -n $newest_file ]] && printf '%s\n' "$newest_file"

    return 0
}

它只使用Bash内置函数,并且应该处理名称中包含换行符或其他异常字符的文件。

答案 3 :(得分:2)

不寻常的文件名(例如包含有效\n字符的文件可能会对这种解析造成严重破坏。以下是在Perl中执行此操作的方法:

perl -le '@sorted = map {$_->[0]} 
                    sort {$a->[1] <=> $b->[1]} 
                    map {[$_, -M $_]} 
                    @ARGV;
          print $sorted[0]
' b2*

那是Schwartzian transform那里使用的。

答案 4 :(得分:2)

使用find命令。

假设您使用的是Bash 4.2+,请使用-printf '%T+ %p\n'作为文件时间戳值。

find $DIR -type f -printf '%T+ %p\n' | sort -r | head -n 1 | cut -d' ' -f2

示例:

find ~/Downloads -type f -printf '%T+ %p\n' | sort -r | head -n 1 | cut -d' ' -f2

有关更有用的脚本,请参见以下 find-latest 脚本:https://github.com/l3x/helpers

答案 5 :(得分:1)

有一种更有效的方法来实现这一目标。请考虑以下命令:

find . -cmin 1 -name "b2*"

此命令通过“b2 *”上的通配符搜索找到一分钟前生成的最新文件。如果您想要过去两天的文件,那么最好使用以下命令:

find . -mtime 2 -name "b2*"

“。”代表当前目录。 希望这会有所帮助。

答案 6 :(得分:0)

您可以将stat与文件文件一起使用,并将decorate-sort-undecorate与前面添加的文件时间一起使用:

$ stat -f "%m%t%N" b2* | sort -rn | head -1 | cut -f2-

答案 7 :(得分:0)

暗黑魔法功能咒语,适用于那些希望使用上述find ... xargs ... head ...解决方案但使用简单易用的功能形式的人,因此您无需考虑:

#define the function
find_newest_file_matching_pattern_under_directory(){
    echo $(find $1 -name $2 -print0 | xargs -0 ls -1 -t | head -1)
}

#setup:
#mkdir /tmp/files_to_move
#cd /tmp/files_to_move
#touch file1.txt
#touch file2.txt

#invoke the function:
newest_file=$( find_newest_file_matching_pattern_under_directory /tmp/files_to_move/ bc* )
echo $newest_file

打印:

file2.txt

哪个是

在给定目录下,文件的修改时间戳最旧的文件名与给定模式匹配。

答案 8 :(得分:0)

对于谷歌员工:

ls -t | head -1

  • -t 按上次修改日期时间排序
  • head -1 只返回第一个结果

(Don't use in production)