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

jlc*_*lin 123 linux bash

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

Directory/
   a1.1_5_1
   a1.2_1_4
   b2.1_0
   b2.2_3_4
   b2.3_2_0
Run Code Online (Sandbox Code Playgroud)

我想要以'b2'开头的最新文件.我怎么在bash中这样做?我需要在我的~/.bash_profile脚本中有这个.

les*_*ana 199

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

ls -t b2* | head -1
Run Code Online (Sandbox Code Playgroud)

但要注意:为什么你不应该解析ls的输出

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

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

以下是如何"正确":如何在目录中找到最新(最新,最早,最旧)的文件?

unset -v latest
for file in "$dir"/*; do
  [[ $file -nt $latest ]] && latest=$file
done
Run Code Online (Sandbox Code Playgroud)

  • 别人注意:如果你是为目录做这个,你可以在ls中添加-d选项,比如'ls -td <pattern> | 头-1' (7认同)
  • [解析LS](http://mywiki.wooledge.org/ParsingLs)链接说不要这样做,并推荐[BashFAQ 99](http://mywiki.wooledge.org/BashFAQ/099)中的方法.我正在寻找一个1-liner而不是防弹包含在脚本中,所以我将继续像@lesmana那样不安全地解析ls. (4认同)
  • @DavidOngaro问题并不是说文件名是版本号.这是关于修改时间.即使使用文件名假设`b2.10_5_2`也会杀死此解决方案. (2认同)

Bor*_*ski 12

结合find并且ls效果很好

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

解决方案:

find . -name "my-pattern" ... -print0 |
    xargs -r -0 ls -1 -t |
    head -1
Run Code Online (Sandbox Code Playgroud)

让我们分解一下:

随着find我们可以匹配所有有趣的文件是这样的:

find . -name "my-pattern" ...
Run Code Online (Sandbox Code Playgroud)

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

find . -name "my-pattern" ... -print0 | xargs -r -0 ls -1 -t
Run Code Online (Sandbox Code Playgroud)

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

最后head -1得到排序列表中的第一个文件.

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

xargs  --show-limits
Run Code Online (Sandbox Code Playgroud)

检查你的系统限制.

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

  • 这比基于 ls 的解决方案更好,因为它适用于包含大量文件的目录,其中 ls 窒息。 (2认同)
  • 我发现如果没有与模式匹配的文件,这会返回与模式不匹配的文件。发生这种情况是因为find没有将任何内容传递给xargs,然后xargs调用了没有文件列表的ls,从而使它可以在所有文件上工作。解决方案是在xargs命令行中添加-r,如果xargs在其标准输入上未收到任何内容,则告诉xargs不要运行其命令行。 (2认同)

pjh*_*pjh 6

这是必需的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
}
Run Code Online (Sandbox Code Playgroud)

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


daw*_*awg 6

您可以stat与文件 glob 和装饰排序取消装饰一起使用,并在前面添加文件时间:

$ stat -f "%m%t%N" b2* | sort -rn | head -1 | cut -f2-
Run Code Online (Sandbox Code Playgroud)

正如评论中所述,最好的跨平台解决方案可能是使用 Python、Perl、Ruby 脚本。

对于此类事情,我倾向于使用 Ruby,因为它非常像 awk,可以轻松地编写小型、可丢弃的脚本,但可以直接从命令行获得 Python 或 Perl 的强大功能。

这是红宝石:

ruby -e '
# index [0] for oldest and [-1] for newest
newest=Dir.glob("*").
    reject { |f| File.directory?(f)}.
    sort_by { |f| File.birthtime(f) rescue File.mtime(f) 
    }[-1]
p newest'
Run Code Online (Sandbox Code Playgroud)

这会获取当前工作目录中的最新文件。

**/*您还可以通过使用inglob或 limit 到匹配的文件b2*等来使 glob 递归

  • 如果我没记错的话,我认为这可能适用于 Mac/FreeBSD 版本的 `stat`。要在其他平台上获得类似的输出,您可以使用 `stat -c $'%Y\t%n' b2* | 排序-rn | 头 -n1 | 切-f2-` (3认同)

Eri*_*ski 6

一个 Bash 函数,用于在与模式匹配的目录下查找最新文件

#1.  Make a bash function:
newest_file_matching_pattern(){ 
    find $1 -name "$2" -print0 | xargs -0 ls -1 -t | head -1  
} 
 
#2. Setup a scratch testing directory: 
mkdir /tmp/files_to_move;
cd /tmp/files_to_move;
touch file1.txt;
touch file2.txt; 
touch foobar.txt; 
 
#3. invoke the function: 
result=$(newest_file_matching_pattern /tmp/files_to_move "file*") 
printf "result: $result\n"
Run Code Online (Sandbox Code Playgroud)

印刷:

result: /tmp/files_to_move/file2.txt
Run Code Online (Sandbox Code Playgroud)

或者,如果脆弱的 bash parlor 技巧分包给 python 解释器更符合你的角度,那么这会做同样的事情:

#!/bin/bash 
 
function newest_file_matching_pattern { 
python - <<END 
import glob, os, re  
print(sorted(glob.glob("/tmp/files_to_move/file*"), key=os.path.getmtime)[0]); 
END 
} 
 
result=$(newest_file_matching_pattern) 
printf "result: $result\n" 
Run Code Online (Sandbox Code Playgroud)

印刷:

result: /tmp/files_to_move/file2.txt
Run Code Online (Sandbox Code Playgroud)


l3x*_*l3x 5

使用查找命令。

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

find $DIR -type f -printf '%T+ %p\n' | sort -r | head -n 1 | cut -d' ' -f2
Run Code Online (Sandbox Code Playgroud)

例子:

find ~/Downloads -type f -printf '%T+ %p\n' | sort -r | head -n 1 | cut -d' ' -f2
Run Code Online (Sandbox Code Playgroud)

有关更有用的脚本,请参阅此处的find-latest脚本:https : //github.com/l3x/helpers

  • Bash 的版本并不重要。您需要 GNU“find”,因为“-printf”选项是非标准的(因此通常开箱即用,这仅适用于 Linux)。 (2认同)