Kiểm tra nếu tệp đã được sửa đổi sau ngày trong tên tệp


11

Tôi có một tệp có tên YYYYMMDDtrong tên tệp, chẳng hạn như

file-name-20151002.txt

Tôi muốn xác định xem tập tin này đã được sửa đổi sau 2015-10-02.

Ghi chú:

  • Tôi có thể làm điều này bằng cách nhìn vào đầu ra của ls, nhưng tôi biết rằng phân tích cú pháp đầu ra lslà một ý tưởng tồi.
  • Tôi không cần tìm tất cả các tệp ngày sau một ngày cụ thể, chỉ cần kiểm tra một tệp cụ thể tại một thời điểm.
  • Tôi không quan tâm đến việc tập tin được sửa đổi vào cùng ngày sau khi tôi tạo nó. Đó là, tôi chỉ muốn biết liệu tập tin này với 20151002tên đã được sửa đổi vào ngày 03 tháng 10 năm 2015 trở đi.
  • Tôi đang dùng MacO 10.9.5.

Xin lỗi vì không bao gồm HĐH trước đó.
Peter Grill

Câu trả lời:


4

Dưới đây là một số cách có thể với:

  • OSX stat:

    newer () {
    tstamp=${1:${#1}-12:8}
    mtime=$(stat -f "%Sm" -t "%Y%m%d" "$1")
    [[ ${mtime} -le ${tstamp} ]] && printf '%s\n' "$1 : NO: mtime is ${mtime}" || printf '%s\n' "$1 : YES: mtime is ${mtime}"
    }
    
  • GNU date:

    newer () {
    tstamp=${1:${#1}-12:8}
    mtime=$(date '+%Y%m%d' -r "$1")
    [[ ${mtime} -le ${tstamp} ]] && printf '%s\n' "$1 : NO: mtime is ${mtime}" || printf '%s\n' "$1 : YES: mtime is ${mtime}"
    }
    
  • zsh chỉ có:

    zmodload zsh/stat
    newer () {
    tstamp=${1:${#1}-12:8}
    mtime=$(zstat -F '%Y%m%d' +mtime -- $1)
    [[ ${mtime} -le ${tstamp} ]] && printf '%s\n' "$1 : NO: mtime is ${mtime}" || printf '%s\n' "$1 : YES: mtime is ${mtime}"
    }
    

Sử dụng:

FILE mới hơn

Ví dụ đầu ra:

file-name-20150909.txt : YES: mtime is 20151026

hoặc là

file-name-20151126.txt : NO: mtime is 20151026

9

Kịch bản sau đây sẽ kiểm tra ngày của tất cả các tệp được chỉ định trên dòng lệnh:

Nó đòi hỏi phiên bản GNU của sed, datestat

$ cat check-dates.sh 
#! /bin/bash

for f in "$@" ; do
  # get date portion of filename
  fdate=$(basename "$f" .txt | sed -re 's/^.*(2015)/\1/')

  # convert it to seconds since epoch + 1 day
  fsecs=$(echo $(date +%s -d "$fdate") + 86400 | bc )

  # get modified timestamp of file in secs since epoch
  ftimestamp=$(stat -c %Y "$f")

  [ "$ftimestamp" -gt "$fsecs" ] && echo "$f has been modified after $fdate"
done

$ ./check-dates.sh file-name-20151002.txt 
file-name-20151002.txt has been modified after 20151002
$ ls -l file-name-20151002.txt 
-rw-rw-r-- 1 cas cas 0 Oct 26 19:21 file-name-20151002.txt

Đây là phiên bản chưa được kiểm tra nhưng sẽ hoạt động trên máy Mac (và trên FreeBSD, v.v.) nếu tôi đọc chính xác các trang hướng dẫn trực tuyến:

#! /bin/bash

for f in "$@" ; do
  # get date portion of filename
  fdate=$(basename "$f" .txt | sed -e 's/^.*\(2015\)/\1/')

  # convert it to seconds since epoch + 1 day
  fsecs=$(echo $(date -j -f %Y%m%d "$fdate" +%s) + 86400 | bc )

  # get modified timestamp of file in secs since epoch
  ftimestamp=$(stat -f %m "$f")

  [ "$ftimestamp" -gt "$fsecs" ] && echo "$f has been modified after $fdate"
done

Một giải pháp thậm chí tốt hơn sẽ là một giải pháp không yêu cầu 4-5 tiện ích mở rộng so với POSIX.
Thomas Dickey

2
Hãy viết phiên bản của riêng bạn, sau đó. Tôi viết những gì tôi muốn viết, không phải những gì bạn muốn tôi viết .... và bao gồm sử dụng các phiên bản công cụ GNU. IMO, GNU IS là tiêu chuẩn thực tế. và số lượng tương đối của các bản phân phối linux được sử dụng so với các bản không phải của linux, các bản không phải là gnu * hỗ trợ điều đó.
cas

6
@cas: tôi trả lời khá gay gắt với nhận xét của Thomas, đó là một gợi ý để cải thiện giải pháp của bạn ... Trong thế giới thực, việc có kịch bản "chuẩn nhất có thể" không chỉ là một phần thưởng, mà là cần thiết (hoặc đúng hơn, bắt buộc). Nhiều nơi không thể cài đặt phiên bản công cụ GNU (tôi tình cờ biết 3 địa điểm khác nhau trong đó một số bash mới nhất của hệ thống là 2.x và awk rất cũ, nó rất gần với phiên bản gốc). Nhu cầu của câu hỏi này có lẽ không "quan trọng" (nhưng có thể) và cũng không thường được người khác tìm kiếm / sử dụng, nhưng nói chung, có một giải pháp di động là +
Olivier Dulac

Tôi đang nhận được sed: illegal option -- rvới MacOS 10.9.5.
Peter Grill

-rlà một sedtùy chọn GNU để bảo nó sử dụng các biểu thức chính quy mở rộng. Bạn có thể thay đổi tập lệnh sed để sử dụng regrec cơ bản thay vì mở rộng (ví dụ: sed -e 's/^.*\(2015\)/\1/')phần còn lại của tập lệnh có thể vẫn thất bại vì nó yêu cầu các phiên bản GNU datestat, và tôi không nghĩ máy Mac đi kèm với chúng là tiêu chuẩn (mặc dù chúng là có sẵn cho Mac trong các gói được biên dịch sẵn)
cas

4

Sử dụng bash và statexprđể có được những ngày như số và so sánh chúng:

#!/bin/bash
for file
do  moddate=$(stat -f %m -t %F "$file") # MacOS stat
    moddate=${moddate//-/} # 20151026
    if filedate=$(expr "$file" : '.*-\([0-9]*\).txt')
    then  if [ $moddate -gt $filedate ]
          then echo "$file: modified $moddate"
          fi
    fi
done

Đây là câu trả lời dành riêng cho Linux trước đây của tôi.

#!/bin/bash
for file
do  moddate=$(stat -c %y "$file")
    moddate=${moddate%% *} # 2015-10-26
    moddate=${moddate//-/} # 20151026
    if [[ "$file" =~ ([0-9]{8}).txt$ ]]
    then  if [[ $moddate > ${BASH_REMATCH[1]} ]]
          then echo "$file: modified $moddate"
          fi
    fi
done

Các bash =~hành regexp nắm bắt được 8 chữ số của tên tập tin vào mảng bash BASH_REMATCH. [[ ]]so sánh các chuỗi, mặc dù bạn chỉ đơn giản so sánh chúng như các số thay vào đó [ -gt ].


MacOS 10.9.5 dường như không có -ctùy chọn stat.
Peter Grill

Lỗi của tôi. Tương đương có vẻ là stat -f %m -t %F "$file". Xin lỗi, tôi không thể kiểm tra nó.
meuh

Với sự thay đổi stattheo nhận xét của bạn, mọi thứ dường như được thực thi nhưng không có đầu ra cho tất cả các trường hợp thử nghiệm. Nhưng, đang "$file" =~ ([0-9]{8}).txt$làm gì vậy?
Peter Grill

Nó tìm kiếm 8 chữ số theo sau .txtở cuối tên tệp. Các dấu ngoặc đơn ()nắm bắt phần 8 chữ số và bạn có thể tìm thấy nó trong ${BASH_REMATCH[1]}.
meuh

Nếu bash của bạn không hoạt động với if [[=~]]bạn, bạn có thể thử cơ bản if filedate=$(expr "$file" : '.*-\([0-9]*\).txt')và sau đó thay thế ${BASH_REMATCH[1]}bằng $filedate.
meuh

4

Bạn có thể làm điều này:

  • trích xuất các giá trị năm / tháng / ngày vào một biến shell,
  • tạo một tập tin tạm thời
  • sử dụng touchlệnh (thêm 0 giây cho giờ / phút / giây) để đặt ngày sửa đổi cho tệp tạm thời

Bởi vì câu hỏi của bạn là về bash, bạn có thể đang sử dụng Linux. Các testchương trình được sử dụng trong Linux (một phần của coreutils ) có phần mở rộng để so sánh dấu thời gian ( -nt-ot) không tìm thấy trong POSIXtest .

Nhận xét POSIX về điều này trong lý do cho test:

Một số nguyên tắc bổ sung mới được phát minh hoặc từ KornShell đã xuất hiện trong một đề xuất ban đầu như là một phần của lệnh điều kiện ( [[]]): s1> s2, s1 <s2, str = mẫu, str! = Mẫu, f1 -nt f2, f1 -ot f2, và F1 -ef f2. Chúng không được chuyển tiếp vào tiện ích thử nghiệm khi lệnh điều kiện được gỡ bỏ khỏi shell vì chúng không được bao gồm trong tiện ích thử nghiệm được tích hợp trong các triển khai lịch sử của tiện ích sh.

Sử dụng tiện ích mở rộng đó, sau đó bạn có thể

  • tạo một tệp tạm thời khác với ngày bạn muốn so sánh với
  • sử dụng -nttoán tử trong testđể thực hiện so sánh bạn yêu cầu.

Đây là một ví dụ. Một sự làm rõ của OP đã đề cập đến nền tảng này, vì vậy người ta có thể sử dụng statthay thế cho các tệp tạm thời (so sánh OSXLinux ):

#!/bin/bash
# inspect each file...

with_tempfile() {
    echo "** with temporary file $name"
    test=$MYTEMP/$name
    touch -t $date $test
    [ $name -nt $test ] && echo "...newer"
}

# alternatively (and this is system-dependent)
with_stat() {
    echo "** with stat command $name"
    stat=$(stat -t "%Y%m%d%H%M" -f "%Sm" $name)
    [ $stat -gt $date ] && echo "...newer"
}

MYTEMP=$(mktemp -d /var/tmp/isnewer.XXXXXX)
trap "rm -rf $MYTEMP" EXIT
for name in file-name-[0-9][0-9]*.txt
do
    if [ -f "$name" ];
    then
            date=${name%%.txt}
            date=${date/file-name-}
            date=${date//-/}2359
            with_tempfile $name
            with_stat $name
    fi
done

Khi tạo các tệp tạm thời, đó là thông lệ tiêu chuẩn để loại bỏ các tệp bị bỏ qua bằng một traplệnh.
Thomas Dickey

Tôi đang dùng MacOS 10.9.5.
Peter Grill

Cảm ơn bạn đã làm rõ. Các tệp tạm thời không thanh lịch như một số cách tiếp cận có thể dựa vào các tiện ích mở rộng bổ sung, nhưng tôi sẽ cung cấp một tập lệnh mẫu ngắn, để thống nhất.
Thomas Dickey

1

Một zshcách tiếp cận khác :

zmodload zsh/stat # best in ~/.zshrc or
zmodload -F zsh/stat +b:zstat # to avoid overriding an eventual
                              # system stat command.
setopt extendedglob # best in ~/.zshrc

ls -ld -- **/*[0-9](#c8)*(De@'zstat -F %Y%m%d -A m +mtime $REPLY &&
  [[ ${(SM)${REPLY:t}#[0-9](#c8)} != $m ]]'@)

Sẽ báo cáo các tệp có cái gì đó trông giống dấu thời gian trong tên của chúng nhưng không tương ứng với thời gian sửa đổi cuối cùng của chúng.

zshcó rất nhiều toán tử như thế để thao túng các khối và biến. Rất thuận tiện để hoàn thành bất kỳ công việc nào một cách nhanh chóng (và nói chung là đáng tin cậy) nhưng thật khó để có được đầu óc của bạn xung quanh tất cả và nó thường tạo ra cái mà chúng ta thường gọi chỉ viết (uyển ngữ cho mã không đọc được, mặc dù cũng ngụ ý rằng bạn không ' Bạn không cần phải đọc nó khi bạn viết nó cho một lần sử dụng tại dấu nhắc lệnh)

Chạy nhanh qua:

  • **/pattern(qualifier): đệ quy toàn cầu với vòng loại toàn cầu.
  • [0-9](#c8)khớp 8 chữ số. Đó là zsh extendedglob tương đương với ksh's {8}([0-9]).
  • D: bao gồm các tập tin ẩn
  • e@...@: vòng loại toàn cầu eval để chạy một số văn bản cho các tệp đủ điều kiện hơn nữa. Đường dẫn tệp được truyền vào$REPLY
  • zstat...lấy mtime được định dạng là YYYYMMDD trong $mmảng.
  • ${REPLY:t}: mở rộng đến đuôi t (tên cơ sở) của tệp.
  • ${(SM)something#pattern}. Trích xuất mô hình khớp phần từ một cái gì đó . Những phần tử đó S( tìm kiếm chuỗi S ) và M(mở rộng thành phần M ) được gọi là cờ mở rộng tham số .
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.