【问题标题】:Find a file that was created within five days of another file in shell在 shell 中查找另一个文件的五天内创建的文件
【发布时间】:2013-07-18 13:57:23
【问题描述】:

我对脚本还是很陌生,所以请继续关注我,如果您有任何问题,请随时提问。

好的,所以: 我有一个文件,比如说 file.txt

file.txt存在于目录/this/is/the/directory/file.txt

在一个单独的目录中存在 .log 文件,这些文件告诉我创建 file.txt 时会发生什么。

fuubar.log、fuu.log、bar.log、this.log、that.log、some.log、other.log...这些日志的数量未知。

我需要收集创建 file.txt 文件后 +-5 天发生的所有日志文件。

例如: file.txt 创建于 2013 年 7 月 7 日(不要注意日期格式) 我需要 2013 年 7 月 2 日至 2013 年 7 月 12 日期间发生的日志文件。

有什么想法吗?

编辑:我对比较文件的日期以获得正确的日期感到更加困惑,我知道如何复制文件。

Perl 和 stat 对我不可用

【问题讨论】:

  • 执行此操作的标准方法是创建具有日期范围的文件(使用touch),然后将它们用作-newer! -newer 参数到find

标签: shell sh qnx qnx-neutrino


【解决方案1】:

除非有人发布单行,否则您可以开始使用!

# Get the date in you want to start listing files from using your
# sample file. This will assign the variable in the format of MMDDYYYY
$ start=$(date -d "-5 days" '+%m%d%Y' < <(date -r /this/is/the/directory/file.txt))

# Get the date in you want to end listing files from. using your
# sample file. This will assign the variable in the format of MMDDYYYY
$ end=$(date -d "+5 days" '+%m%d%Y' < <(date -r /this/is/the/directory/file.txt))

# Create two temp files. touch -t will force the timestamp on
# these files to match the content of variables
$ touch -t "$start" /tmp/s$$
$ touch -t "$end" /tmp/e$$

# Use these temp files timestamp as a pivot mechanism by find commands
# newer option. This will list files whose timestamp is in between our 
# temp files timestamp which we captured from your sample file
$ find /path/to/files -type f -newer /tmp/s$$ -and -not -newer /tmp/e$$

【讨论】:

  • 我讨厌听起来很无知,但是,请你给我一个解释。我不只是复制它,而是试图理解它,以便我可以以最适合我的项目的格式实现它。
  • @js 非常好,但不要忘记在完成后删除你的 tmpfiles s$$e$$
  • @umläute 是的,要么手动删除它们,要么重新启动机器,这样/tmp 就会被清除! ;)
  • 这是为 GNU 编写的,因此在没有 GNU coreutils 的 POSIX BSD OS/X AIX Solaris 上“开箱即用”将无法工作......只是为了了解人们在这些上剪切和粘贴的信息平台:)
  • 标记为有用,因为在大多数情况下这应该有效。我没有完全解释我使用的是 QNX,所以你无法知道。
【解决方案2】:

由于您使用的是 QNX,因此您无法在解决此问题时使用现成的 POSIX/Linux/Unix/BSD 脚本。如果您可以安装可以帮助您执行此操作的编程语言,或者安装更新的系统实用程序(希望来自 QNX)或 shell 工具来实现相同的目标,它可能会更容易,但我意识到这并不总是可能的。

您可以尝试以下可怕的骇人听闻的方法来快速“立即工作”解决方案:

首先使用find /path/to/log/file.txt -printf %Cj 为您提供1-366 中创建日志文件的年份,并使用-printf %CY 为您提供年份值。接下来像以前一样使用expr +/- 5 来查找 $day_of_year_start 和 $day_of_year_end 的值,然后您可以使用它们来设置 $start$end 变量,就像在 JP 的脚本或我的变体中一样。

现在您需要一种方法来获取与date -s 一起使用的纪元时间,这样您就可以从纪元转换为touch -t 将使用的时间格式。如果没有可以在纪元时间之间转换的date,也没有可以输出纪元时间的strftime,那么您需要一个“hack”。一种混乱的方法是在年初为纪元时间设置一个常数(比如 2013 年 1 月 1 日的 1356998400 ;如果需要,加上其他年份的值),然后添加 ($day_of_year_start x 86400) 和 ($day_of_year_end x 86400 ) 秒到该常数以获得两个纪元时间值以在 QNX 上运行 date -s 并分别为 $start$end 设置日期/时间戳。

混乱,但它可能真的有效 :-) 如果你尝试这个,请告诉我们。

干杯, ps:这是脚本的开头,您需要检查 QNX 的命令参数和日期格式以确保它们正确。它适用于我使用 gfind(GNU 查找)和 date -r(而不是 QNX 的 date -s)的 BSD 盒子。

#!/bin/sh
# Find files created within 5 days before/after
# the creation of another file on QNX.
# 
#  ./rangesearch /path/to/logfile.txt
# 
# NB: 
# QNX shell environment lacks some POSIX/GNU features:
# 1. 'stat', 'date -r' are not available (use find to get file ACM dates)
# 2. use GNU 'find' extensions (since -printf is needed for output of dates)
# 3. 'date' cannot modify dates using day/month/year values so we convert
#    to epoch values in a roundabout way. 'find' cannot output epoch dates
#    due to limitations of QNX strftime so we compare year of file creation
#    against a set of constants and then add/subtract daily amounts of 
#    seconds from this value and use QNX 'date -s' to convert these dates to 
#    formats usable by QNX 'touch'. 
#
# TODO: improve and extend year <--> epoch time constant matching with
#       an array and for loop (Bourne shell 'sh' does not really have
#       an "array" feature per se). 
#
#       detect version of find/gfind date/gdate so this runs on Linux OSX BSD 
#
#       add more precise units (hours minutes seconds) to epoch time
#       calculation (exercise for reader)
#

year2013="1357016400"
year2012="1325394000"
year2011="1293858000"
year2010="1262322000"
# .... etc etc,  some kind of vector or array would be nice ...

fileyear=`find $1 -printf %CY`
filedoyr=`find $1 -printf %Cj`

if [ $fileyear -eq "2013" ]; then
  logfile_epoch=$(( $year2013 + ($filedoyr * 86400) ))
  echo "file is from $fileyear or $logfile_epoch SSL"
elif [ $fileyear -eq "2012" ]; then
  logfile_epoch=$(( $year2012 +($filedoyr * 86400) ))
  echo "file is from $fileyear or $logfile_epoch SSL"
elif [ $fileyear -eq "2011" ]; then
  logfile_epoch=$(( $year2011 + ($filedoyr *86400) ))
  echo "file is from $fileyear or $logfile_epoch SSL"
elif [ $fileyear -eq "2010" ]; then
  logfile_epoch=$(( $year2010 + ($filedoyr *86400) ))
  echo "file is from $fileyear or $logfile_epoch SSL"
else
  echo "there is a problem"
  exit
fi

echo "file is from day $filedoyr of $fileyear"

epochal_start=$(( $logfile_epoch - (5*86400) ))
epochal_end=$(( $logfile_epoch + (5*86400) ))
start=`date -s $epochal_start +%Y%m%d%H%S`
end=`date -s $epochal_end +%Y%m%d%H%S`

echo "epochal_start $epochal_start"
echo "epochal_end $epochal_end"

echo -e "Searching for files from $start to $end in age \n...\n"

touch -t "$start" /tmp/s$$
touch -t "$end" /tmp/e$$

# -print0 and xargs -0 allow for file names with whitepace
find . -type f -newer /tmp/s$$ -and ! -newer /tmp/e$$ -print0 |xargs -0 ls -tal

# clean temporary files:
rm /tmp/s$$
rm /tmp/e$$

这里的 SSL 是指很久以前的秒数 :-)

【讨论】:

  • 对不起,我花了这么长时间才回来。我很快就会尝试这种方法,并会告诉你结果。
  • 这行得通!非常感谢。我可能会进一步冒险并尝试在某个时候尝试一些“待办事项”,但现在我还有其他项目在我的盘子里。再次感谢!干杯!
【解决方案3】:

一般没有simple portable (POSIX) way to get file modification times

请注意,如果您的 Unix 具有包含 GNU 扩展的 find 版本(如 -printf),您可以使用 find 获取原始日志文件的日期。如果您的date 版本不包括-v 用于及时向前和向后调整日期字符串,那么您必须找到某种方法将日期转换为纪元日期(很久以前的秒数)并使用调整日期expr (+/- 86400*5) 并将其转换为可用于touch 的格式。

您已经告诉我们您正在使用 QNX,因此对于 find 的 QNX 版本,您将拥有 -printf 扩展名。这意味着您可以使用find 创建您的值,但无法使用date -v 将它们调整为+/- 5 天,或者使用expr 将它们转换为纪元时间以进行修改。 QNX 文档似乎没有说明您如何以一种简单明了的方式做到这一点,说明了为什么simple portable (POSIX shell) way to get file modification times, set the date, convert date formats, etc. etc. 在现实世界中会如此美好。您需要向 QNX 专家寻求更多帮助。对不起!

为了补充 Jaypal Singh 的 GNU coreutils 解决方案,BSD/POSIX 和 Perl 方法如下。

对于 BSD 派生系统(FreeBSD、DragonFly,可能是 OS/X),将在 @JS 的解决方案中创建 $start$end 变量的命令替换为以下可能有效:

#!/bin/sh
#
middle=`stat -r file.txt | cut -d" " -f10` # file.txt could 
                                           # come from a script 
                                           # positional argument like $1



# the number date range could also come from a positional argument like $2

start=`date -v-5d -r $middle +%Y%m%d%H%S`
end=`date -v+5d -r $middle +%Y%m%d%H%S`

touch -t "$start" /tmp/s$$
touch -t "$end" /tmp/e$$

find . -type f -newer /tmp/s$$ -and ! -newer /tmp/e$$

脚本的最后一部分如@JS 已经描述过。

另一种选择是骑跨平台骆驼进行救援......就像这样:-)

当然,这可能会更好,但这是从find2perl 输出中抄袭的perl 方法:

#!/usr/bin/env perl  
# findrange  Usage: cd /dir/to/search ; findrange 5 /file/to/compare.txt

use strict;  use warnings;                                            
use File::Find ();                                                          
use vars qw/*name/;                                                         

*name   = *File::Find::name;                                                

sub wanted;                                                                 

# some values to use                                                        
my $RANGE = $ARGV[0] ;  # get date range                                    
my $REF_FILE = $ARGV[1] ;  # get file to compare date                       
my $AGE_REF = -M $REF_FILE ;                                                
my $start = $AGE_REF - $RANGE ; # +/- days from @ARGV[0] 
my $end = $AGE_REF +  $RANGE ;                                              

# Descend file system searching/finding.                                     
# from current directory  "."                                               
File::Find::find({wanted => \&wanted}, '.');                                
exit;                                                                       

sub wanted {                                                                
  ( lstat($_)) &&  #caches results in "_" for -M to use   
    -f _ &&                                                                 
    (-M _ > $start) &&                                                      
    ! (-M _ > $end)                                                         
    && print("$name\n");                                                    
}                                                                           

如果是交互式使用,您可以添加:

if ($#ARGV != 1) {  # require 2 args (last array index +1)
 usage() ;  
 }

sub wanted 的上方/之前运行和sub usage { print "whatever \n"; exit;} 之类的东西,让它更漂亮。

干杯,

【讨论】:

  • 我的问题是 stat 无法被我正在使用的虚拟机识别,我不知道它是否安装在将要部署此脚本的机器上。跨度>
  • 那是因为,如前所述,通常没有simple portable (POSIX) way to get file modification times。您的 stat 可能是 GNU,它(几乎)做了完全不同的事情。我将发布perl 方法 - 至少这应该适用于任何地方。
  • 我继续做其他事情并完成了它,所以现在这个问题又回来困扰我了。 Perl 和stat 不是我的选择。还有其他方法可以获取文件的日期,然后使用touch 比较文件吗?
  • 如果statdate -rperl 不可用,您使用的是什么UNIX? Linux 大部分使用 GNU 用户空间,stat 有一个不同的野兽(与文件系统有关),所以这是可以理解的。如果您确实拥有 Linux,那么您应该拥有 GNU coreutils,它确实有一个 date -r 命令...(难以置信)。我想你可以尝试解析ls 的输出,这通常是不受欢迎的......你需要告诉我们更多关于你的平台/操作系统等的信息。
  • 这是 QNX;类 Unix 操作系统。脚本本身将在多个设备上(一个是控制中心,另一个是控制中心的支持设备)...它们都使用相同的操作系统并且具有几乎相同的 bin 文件,但是 - 就像以前一样提到的,出于某种原因,不包括任何有用的东西-_-
猜你喜欢
  • 2013-02-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多