ทดสอบว่าไฟล์ถูกแก้ไขหลังจากวันที่ในชื่อไฟล์


11

ฉันมีไฟล์ชื่อYYYYMMDDในชื่อไฟล์เช่น

file-name-20151002.txt

ฉันต้องการตรวจสอบว่าไฟล์นี้ถูกแก้ไขหลังจาก 2015-10-02

หมายเหตุ:

  • ฉันสามารถทำได้โดยดูที่ผลลัพธ์ของlsแต่ฉันรู้ว่าการแยกวิเคราะห์ผลลัพธ์lsเป็นความคิดที่ไม่ดี
  • ฉันไม่จำเป็นต้องค้นหาไฟล์ทั้งหมดที่ลงวันที่หลังจากวันที่ที่ระบุเพียงแค่ต้องทดสอบไฟล์หนึ่งไฟล์ในแต่ละครั้ง
  • ฉันไม่กังวลเกี่ยวกับไฟล์ที่ถูกแก้ไขในวันเดียวกันหลังจากที่ฉันสร้างมันขึ้นมา นั่นคือฉันแค่อยากรู้ว่าไฟล์นี้ด้วย20151002ในชื่อมีการแก้ไขในวันที่ 3 ตุลาคม 2015 หรือหลังจากนั้น
  • ฉันใช้ MacOs 10.9.5

ขออภัยที่ไม่รวมระบบปฏิบัติการก่อนหน้านี้
Peter Grill

คำตอบ:


4

นี่เป็นวิธีที่เป็นไปได้ด้วย:

  • 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 เท่านั้น:

    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}"
    }
    

การใช้งาน:

ไฟล์ใหม่กว่า

ตัวอย่างผลลัพธ์:

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

หรือ

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

9

สคริปต์ต่อไปนี้จะตรวจสอบวันที่ของไฟล์ทั้งหมดที่ระบุในบรรทัดคำสั่ง:

มันต้องมีรุ่น GNU ของsed, dateและstat

$ 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

นี่เป็นรุ่นที่ยังไม่ทดลอง แต่ควรใช้กับ Mac (และใน FreeBSD เป็นต้น) หากฉันอ่าน man man แบบออนไลน์อย่างถูกต้อง:

#! /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

ทางออกที่ดียิ่งขึ้นคือจะไม่ต้องการส่วนขยาย 4-5 ส่วนกับ POSIX
Thomas Dickey

2
อย่าลังเลที่จะเขียนเวอร์ชันของคุณเอง ฉันเขียนสิ่งที่ฉันต้องการเขียนไม่ใช่สิ่งที่คุณต้องการให้ฉันเขียน .... และรวมถึงการใช้เครื่องมือเวอร์ชัน GNU IMO, GNU เป็นมาตรฐานความเป็นจริง และจำนวนสัมพัทธ์ของลินุกซ์ distros ที่ใช้งานเทียบกับ non-linux, non-gnu * nixes รองรับ
cas

6
@cas: คำตอบที่ค่อนข้างรุนแรงต่อความคิดเห็นของโทมัสซึ่งผมเชื่อว่าเป็นเพียงข้อเสนอแนะในการปรับปรุงวิธีการแก้ปัญหาของคุณ ... ในโลกแห่งความเป็นจริงการมีสคริปต์ "มาตรฐานที่สุดเท่าที่จะทำได้" ไม่เพียง แต่โบนัสเท่านั้น ค่อนข้างบังคับ) สถานที่หลายแห่งไม่สามารถติดตั้งเครื่องมือเวอร์ชัน GNU ได้ (ฉันรู้สถานที่แตกต่างกัน 3 แห่งที่ซึ่งทุบตีล่าสุดของระบบบางรุ่นคือ 2.x และ awk นั้นเก่ามากมันใกล้เคียงกับต้นฉบับมาก) ความต้องการของคำถามนี้อาจไม่ใช่ "สำคัญ" (แต่อาจเป็น) และมักจะไม่ถูกค้นหา / ใช้โดยคนอื่น แต่โดยทั่วไปการมีโซลูชันแบบพกพาคือ +
Olivier Dulac

ฉันsed: illegal option -- rใช้ MacOS 10.9.5
Peter Grill

-rเป็นsedตัวเลือกGNU เพื่อบอกให้ใช้นิพจน์ทั่วไปแบบขยาย คุณสามารถเปลี่ยนสคริปต์ sed เพื่อใช้ regexp ขั้นพื้นฐานแทนที่จะขยาย (เช่นsed -e 's/^.*\(2015\)/\1/')ส่วนที่เหลือของสคริปต์อาจยังคงล้มเหลวเพราะต้องใช้เวอร์ชัน GNU dateและstatและฉันไม่คิดว่า Mac มาพร้อมกับพวกเขาเป็นมาตรฐาน (แม้ว่าจะเป็น ใช้ได้กับ Mac ในแพ็คเกจที่คอมไพล์แล้ว)
cas

4

การใช้ bash และstatและexprเพื่อให้ได้วันที่เป็นตัวเลขและทำการเปรียบเทียบ:

#!/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

นี่เป็นคำตอบเฉพาะของ Linux ก่อนหน้าของฉัน

#!/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

ตัวดำเนินการ bash =~regexp จับ 8 หลักของชื่อไฟล์ในอาร์เรย์ bash BASH_REMATCH [[ ]]เปรียบเทียบสตริง [ -gt ]แต่คุณเพียงแค่เปรียบเทียบพวกเขาเป็นตัวเลขแทนใน


MacOS 10.9.5 ไม่ได้ดูเหมือนจะมีตัวเลือกในการ-c stat
Peter Grill

ความผิดฉันเอง. stat -f %m -t %F "$file"เทียบเท่าน่าจะเป็น ขออภัยฉันไม่สามารถทดสอบได้
meuh

ด้วยการเปลี่ยนแปลงในstatตามความคิดเห็นของคุณสิ่งที่ดูเหมือนจะดำเนินการ แต่ไม่มีผลลัพธ์สำหรับกรณีทดสอบทั้งหมด แต่"$file" =~ ([0-9]{8}).txt$กำลังทำอะไรอยู่?
Peter Grill

มันจะค้นหาตัวเลข 8 หลักตามด้วย.txtท้ายชื่อไฟล์ วงเล็บ()จับส่วน 8 ${BASH_REMATCH[1]}หลักและคุณสามารถหาได้ใน
meuh

ถ้าไม่ได้ทำงานของคุณด้วยการทุบตีif [[=~]]คุณสามารถลองขั้นพื้นฐานif filedate=$(expr "$file" : '.*-\([0-9]*\).txt')แล้วแทนที่โดย${BASH_REMATCH[1]} $filedate
meuh

4

คุณสามารถทำได้:

  • แยกค่าปี / เดือน / วันลงในตัวแปรเชลล์
  • สร้างไฟล์ชั่วคราว
  • ใช้touchคำสั่ง (เพิ่ม 0 วินาทีสำหรับชั่วโมง / นาที / วินาที) เพื่อตั้งวันที่แก้ไขสำหรับไฟล์ชั่วคราว

เนื่องจากคำถามของคุณเกี่ยวกับbashคุณน่าจะใช้ Linux testโปรแกรมที่ใช้ในลินุกซ์ (ส่วนหนึ่งของcoreutils ) มีส่วนขยายสำหรับการเปรียบเทียบการประทับเวลา ( -ntและ-ot) ไม่พบในPOSIXtest

POSIX แสดงความคิดเห็นเกี่ยวกับสิ่งนี้ในเหตุผลสำหรับtest:

บางส่วนเพิ่มเติมที่คิดค้นขึ้นใหม่หรือจาก KornShell ปรากฏในข้อเสนอเริ่มต้นเป็นส่วนหนึ่งของคำสั่งตามเงื่อนไข ( [[]]): s1> s2, s1 <s2, str = รูปแบบ, str! = pattern, f1 -nt f2, f1 -ot f2, และ f1 -ef f2 พวกเขาไม่ได้ถูกส่งต่อไปยังยูทิลิตี้ทดสอบเมื่อคำสั่งแบบมีเงื่อนไขถูกลบออกจากเชลล์เนื่องจากไม่ได้รวมอยู่ในยูทิลิตี้ทดสอบที่สร้างไว้ในการใช้งานเชิงประวัติของยูทิลิตี sh

คุณสามารถใช้ส่วนขยายนั้นได้

  • สร้างไฟล์ชั่วคราวอีกไฟล์พร้อมวันที่ที่คุณต้องการเปรียบเทียบ
  • ใช้-ntโอเปอเรเตอร์ในtestเพื่อทำการเปรียบเทียบที่คุณต้องการ

นี่คือตัวอย่าง ชี้แจงโดย OP กล่าวถึงแพลตฟอร์มดังนั้นหนึ่งสามารถใช้statเป็นทางเลือกแทนไฟล์ชั่วคราว (เปรียบเทียบOSXและLinux ):

#!/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

เมื่อสร้างไฟล์ชั่วคราวเป็นวิธีปฏิบัติมาตรฐานในการลบไฟล์ที่ถูกมองข้ามด้วยtrapคำสั่ง
Thomas Dickey

ฉันใช้ MacOS 10.9.5
Peter Grill

ขอขอบคุณสำหรับการชี้แจง. ไฟล์ชั่วคราวนั้นไม่สวยงามเท่าวิธีที่เป็นไปได้ที่ต้องอาศัยส่วนขยายเพิ่มเติม แต่ฉันจะให้สคริปต์ตัวอย่างสั้น ๆ เพื่อความสอดคล้อง
Thomas Dickey

1

zshวิธีอื่น:

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 ]]'@)

จะรายงานไฟล์ที่มีสิ่งที่ดูเหมือนประทับเวลาในชื่อของพวกเขา แต่ไม่ตรงกับเวลาแก้ไขล่าสุดของพวกเขา

zshมีตัวดำเนินการมากมายเช่นนั้นเพื่อจัดการ globs และตัวแปร มันมีประโยชน์มากในการทำงานให้เสร็จเร็ว (และโดยทั่วไปได้อย่างน่าเชื่อถือ) แต่มันค่อนข้างยากที่จะทำให้คุณเข้าใจได้ทั้งหมดและมันมักจะผลิตสิ่งที่เรามักจะเรียกว่าโค้ดแบบเขียนอย่างเดียวไม่จำเป็นต้องอ่านมันในขณะที่คุณเขียนสำหรับการใช้งานเพียงครั้งเดียวที่พรอมต์คำสั่ง)

วิ่งเร็วผ่าน:

  • **/pattern(qualifier): glob แบบเรียกซ้ำพร้อมตัวระบุแบบหมุนได้
  • [0-9](#c8)ตรงกับ 8 หลัก นั่นคือเทียบเท่า extendedglob zsh ของ'sksh{8}([0-9])
  • D: รวมไฟล์ที่ซ่อนอยู่
  • e@...@: eval glob qualifier เพื่อเรียกใช้ข้อความเพื่อให้มีคุณสมบัติไฟล์เพิ่มเติม เส้นทางของไฟล์ที่ส่งผ่านมา$REPLY
  • zstat...ดึง mtime ที่จัดรูปแบบเป็น YYYYMMDD ใน$mอาร์เรย์
  • ${REPLY:t}: ขยายเป็นt tail (ชื่อไฟล์ฐาน) ของไฟล์
  • ${(SM)something#pattern}. สารสกัดจากส่วนที่ตรงกับรูปแบบจากบางสิ่งบางอย่าง บรรดาS( Sค้นหา ubstring) และM(ขยายไปยังM atched ส่วน) จะเรียกว่าพารามิเตอร์ธงขยายตัว
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.