วิธีเชื่อมโยงคำสั่ง 'date -d @xxxxxx' และ 'find ./' ได้อย่างไร


14

ฉันมีไดเรกทอรีที่ชื่อ timestamps ให้ในมิลลิวินาทีตั้งแต่ 1970-01-01:

1439715011728
1439793321429
1439879712214
.
.

และฉันต้องการผลลัพธ์เช่น:

1442039711    Sat Sep 12 08:35:11 CEST 2015
1442134211    Sun Sep 13 10:50:11 CEST 2015
1442212521    Mon Sep 14 08:35:21 CEST 2015
.
.

ฉันสามารถแสดงรายการไดเรกทอรีทั้งหมดตามคำสั่ง:

find ./ -type d | cut -c 3-12

แต่ฉันไม่สามารถใส่เอาต์พุตไปที่คำสั่งถัดไป: date -d @xxxxxxและจัดการกับเอาต์พุต

ฉันจะทำสิ่งนี้ได้อย่างไร


2
เวลาบันทึกเหล่านั้นแปลไปเป็นอย่างไร เพราะตัวเลขของคุณยาวเกินไป ... (อันแรก - คือFri Oct 2 05:35:28 47592)
Sobrique

1
@Sobrique เห็นได้ชัดว่ามิลลิวินาทีนับตั้งแต่ยุค
Gilles 'หยุดความชั่วร้าย'

คำตอบ:


10

คุณอยู่ในเส้นทางที่ถูกต้อง (สำหรับโซลูชันที่ง่ายกว่าโดยเรียกใช้เพียง 2 หรือ 3 คำสั่งดูด้านล่าง) คุณควรใช้*แทนที่จะ./กำจัดไดเรกตอรีปัจจุบันและทำให้การตัดมิลลิวินาทีง่ายขึ้นเล็กน้อยจากนั้นเพียงส่งผลลัพธ์เป็น GNU parallelหรือxargs²:

find * -type d | cut -c 1-10 | parallel date --date=@{} +%c

เพื่อรับ

Sat 12 Sep 2015 08:35:11 CEST
Sun 13 Sep 2015 10:50:11 CEST
Mon 14 Sep 2015 08:35:21 CEST

และเพื่อเพิ่มวินาทีชดเชยก่อนหน้าดังที่ตัวอย่างของคุณระบุ:

find * -type d | cut -c 1-10 | parallel 'echo "{} "  $(date --date=@{} +%c)'

หรือ:

find * -type d | cut -c 1-10 | xargs -I{} bash -c 'echo "{} "  $(date --date=@{} +%c)'

ที่จะได้รับ:

1442039711  Sat 12 Sep 2015 08:35:11 CEST
1442134211  Sun 13 Sep 2015 10:50:11 CEST
1442212521  Mon 14 Sep 2015 08:35:21 CEST

อย่างไรก็ตามมันง่ายกว่าที่จะทำ:

find * -type d -printf "@%.10f\n" | date -f - +'%s  %c'

ซึ่งทำให้คุณได้ผลลัพธ์ที่ร้องขอเดียวกันอีกครั้ง

ข้อเสียของการใช้*คือคุณถูก จำกัด โดย commandline ของคุณสำหรับการขยายตัวข้อดีคือคุณได้รับไดเรกทอรีของคุณเรียงตามค่าการประทับเวลา หากจำนวนไดเรกทอรีเป็นปัญหาการใช้งาน-mindepth 1แต่เสียการสั่งซื้อ:

find ./ -mindepth 1 -type d -printf "@%.10f\n" | date -f - +'%s  %c'

และแทรกsortหากจำเป็น:

find ./ -mindepth 1 -type d -printf "@%.10f\n" | sort | date -f - +'%s  %c'

¹ สมมติว่าไม่มีไดเรกทอรีย่อยซ้อนกันซึ่งเป็นกรณีจากตัวอย่างของคุณ นอกจากนี้คุณยังสามารถใช้./ -mindepth 1แทน*
² คุณสามารถแทนที่parallelด้วยxargs -I{}ที่นี่ตามที่ @hobbs และ @don_crissti แนะนำมันเป็น verbose มากขึ้น ³ ขึ้นอยู่กับคำตอบของ Gilles ในการใช้dateความสามารถในการอ่านไฟล์


หรือxargsถ้าคุณไม่มีparallelซึ่งหลายคนอาจไม่
ฮอบส์

@hobbs AFAIK xargsไม่มีตัวเลือกในการระบุว่าอาร์กิวเมนต์parallelนั้นเป็นแบบ{}ไหน
Anthon

4
มันทำ:find ./ -type d | cut -c 3-12 | xargs -I{} date --d @{} +'%Y-%m-%d'
don_crissti

@ แล้วมันจะเกิดขึ้นถ้าคุณใช้-Iตัวเลือก
ฮอบส์

1
@ แอนทอนตัวเลือกแบบยาวของ GNU สามารถย่อได้หากไม่ชัดเจน --dหรือ--daจะทำงานกับ GNU รุ่นปัจจุบันdateแต่อาจหยุดทำงานในวันนั้นจะdateแนะนำ--dalekตัวเลือก (สำหรับวันที่ในปฏิทิน Dalek)
Stéphane Chazelas

10

ฉันจะหลีกเลี่ยงการรันหลายคำสั่งต่อไฟล์ในลูป เนื่องจากคุณใช้ GNUisms อยู่แล้ว:

find . ! -name . -prune -type d |
  awk '{t = substr($0, 3, 10); print t, strftime("%a %b %d %T %Z %Y", t)}'

ซึ่งเพิ่งรันสองคำสั่ง strftime()เป็น GNU date -dเฉพาะเช่น


สิ่งนี้ไม่ตัดมิลลิวินาทีของชื่อไดเรกทอรี แต่แสดงอักขระ 13 ตัวเต็มแทนการร้องขอ 10 ครั้งแรก
Anthon

@ อันธพาลใช่แล้วพลาดข้อกำหนดนั้นไป ควรจะตกลงตอนนี้
Stéphane Chazelas

8

คุณมีอยู่แล้ว:

find ./ -type d | cut -c 3-12

ซึ่งสันนิษฐานว่าคุณได้รับการประทับเวลาในรูปแบบยุค ตอนนี้เพิ่มวงขณะที่:

find ./ -type d | cut -c 3-12 | while read datestamp
do
    printf %s "$datestamp"
    date -d "@$datestamp"
done

โปรดทราบว่าในบางเชลล์ไวยากรณ์นั้นจะได้รับ while loop ใน subshell ซึ่งหมายความว่าหากคุณพยายามตั้งค่าตัวแปรที่นั่นจะไม่สามารถมองเห็นได้เมื่อคุณออกจากลูปแล้ว ในการแก้ไขปัญหานั้นคุณจะต้องพลิกสิ่งต่าง ๆ บนหัวของพวกเขาเล็กน้อย:

while read datestamp
do
    printf %s "$datestamp"
    date -d "@$datestamp"
done < <(find ./ -type d | cut -c 3-12)

ซึ่งทำให้การfindใน subshell และเก็บห่วงในขณะที่ในเปลือกหลัก นั่นไวยากรณ์ (AT & T ksh, zshและbashเฉพาะเจาะจง) เป็นสิ่งจำเป็นเท่านั้นถ้าคุณกำลังมองหาเพื่อนำมาใช้ผลจากภายในวงที่แม้ว่า


โดยไม่คำนึงถึงบอกว่ามันเป็นทุบตีเฉพาะไม่ถูกต้อง :)
Wouter Verhelst

อันที่จริงตามที่คุณเขียนไว้ในตอนแรกdone <(find)แทนที่จะdone < <(find)เป็นสิ่งที่ถูกต้องสำหรับyash( <(...)การเปลี่ยนเส้นทางกระบวนการไม่ใช่การทดแทนกระบวนการ) ดังนั้นการแก้ไขของฉันจึงเป็นนักรบหน่อยเพราะมันอาจเป็นเปลือกที่คุณต้องการ
Stéphane Chazelas

6

หากคุณมีวันที่ของ GNU ก็สามารถแปลงวันที่อ่านจากไฟล์อินพุต คุณเพียงแค่ต้องนวดเวลาประทับเล็กน้อยเพื่อให้สามารถจดจำได้ ไวยากรณ์อินพุตสำหรับการประทับเวลาที่อ้างอิงตาม Unix epoch @ตามด้วยจำนวนวินาทีซึ่งสามารถมีจุดทศนิยม

find ./ -type d ! -name '*[!0-9]*' |
sed -e 's~.*/~@~' -e 's~[0-9][0-9][0-9]$~.&~' |
date -f - +'%s  %c'

+1 สำหรับการใช้dateการอ่านไฟล์ สิ่งนี้จะให้date: invalid date ‘@’เพราะการแปลของไดเรกทอรีปัจจุบัน ( ./) และเนื่องจากคุณสามารถทิ้งมิลลิวินาทีคุณสามารถทำให้การsedแก้ไขครั้งที่สองง่ายขึ้นเพียงแค่วางอักขระ 3 ตัวสุดท้าย หรือลบทั้งหมดและใช้find * -type d -printf "@%.10f" | date ...
Anthon

5

ฉันจะทำมันอย่างถาวร - ฟีดในรายการของการประทับเวลา:

#!/usr/bin/perl
use strict;
use warnings;
use Time::Piece;

while ( my $ts = <DATA> ) { 
   chomp ( $ts );
   my $t = Time::Piece->new();
   print $t->epoch, " ", $t,"\n";
}

__DATA__
1442039711  
1442134211  
1442212521

ผลลัพธ์นี้:

1442039711 Sat Sep 12 07:35:11 2015
1442134211 Sun Sep 13 09:50:11 2015
1442212521 Mon Sep 14 07:35:21 2015

หากคุณต้องการรูปแบบผลลัพธ์เฉพาะคุณสามารถใช้strftimeเช่น:

print $t->epoch, " ", $t->strftime("%Y-%m-%d %H:%M:%S"),"\n";

ซึ่งจะเปลี่ยนเป็นซับในท่อของคุณ:

 perl -MTime::Piece -nle '$t=Time::Piece->new($_); print $t->epoch, "  ", $t, "\n";'

แต่ฉันอาจแนะนำแทนที่จะมองใช้File::Findโมดูลและทำสิ่งทั้งหมดใน Perl แทน หากคุณยกตัวอย่างโครงสร้างไดเรกทอรีของคุณก่อนที่จะตัดมันฉันจะยกตัวอย่างให้คุณ แต่มันจะเป็นสิ่งที่ชอบ:

#!/usr/bin/env perl

use strict;
use warnings;
use Time::Piece;
use File::Find; 

sub print_timestamp_if_dir {
   #skip if 'current' item is not a directory. 
   next unless -d; 
   #extract timestamp (replicating your cut command - I think?)
   my ( $timestamp ) = m/.{3}(\d{9})/; #like cut -c 3-12;

   #parse date
   my $t = Time::Piece->new($timestamp);
   #print file full path, epoch time and formatted time; 
   print $File::Find::name, " ", $t->epoch, " ", $t->strftime("%Y-%m-%d %H:%M:%S"),"\n";
}

find ( \&print_timestamp_if_dir, "." ); 

2

ด้วยzshและstrftime builtin:

zmodload zsh/datetime
for d (*(/))
strftime '%s %a %b %d %T %Z %Y' $d

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

setopt extendedglob
zmodload zsh/datetime
for d (**/[0-9](#c10,)(/))
strftime '%s %a %b %d %T %Z %Y' ${${d:t}:0:10}

2

ใช้ GNU Parallel:

find ./ -type d | cut -c 3-12 | parallel -k 'echo {} `date -d @{}`'

หากคุณสามารถยอมรับ \ t แทนการเว้นวรรค:

find ./ -type d | cut -c 3-12 | parallel -k --tag date -d @{}

โปรดทราบว่าจะถูกเขียนในparallel perlดูเหมือนว่า overkill พิจารณาว่าperlมีstrftime()ผู้ประกอบการ ชอบperl -MPOSIX -lpe '$_.=strftime(" %c", localtime substr $_, 2, 10)'
Stéphane Chazelas

2
1. มันสั้นกว่า 2. คุณไม่จำเป็นต้องเรียนรู้ Perl
Ole Tange

1
มันสั้นกว่า 27% แต่มันมีขนาดเล็กกว่าคำสั่งหลายคำสั่งที่มีประสิทธิภาพน้อยกว่า (ประมาณ 800 เท่าในการทดสอบที่ช้าฉันคิดว่ามันต้องวางไข่เชลล์ (เชลล์ของคุณไม่ใช่ / bin / sh) และคำสั่ง date สำหรับแต่ละบรรทัด) และไม่เป็นมิตรกับระบบเนื่องจากภาระทั้งหมดของ CPU ในครั้งเดียว parallelและคุณยังต้องเรียนรู้ IMO parallelเป็นเครื่องมือที่ยอดเยี่ยมในการทำขนานกับ CPU ที่มีงานมาก แต่ไม่เหมาะสำหรับงานประเภทนี้ที่นี่
Stéphane Chazelas

มีบริบทมากมายที่ไม่ได้คำนึงถึงประสิทธิภาพดังนั้นจึงยังคงเป็นโซลูชันที่ได้รับการยอมรับ แต่ก็ยังมีมูลค่าที่จะกล่าวถึงปัญหาด้านประสิทธิภาพโดยเฉพาะอย่างยิ่งเมื่อพิจารณาว่าขนานมักจะคล้องจองกับจิตใจของผู้คนที่มีประสิทธิภาพสูง
Stéphane Chazelas

0

ปกติคำสั่ง find สามารถถูกโยงด้วยคำสั่งใดก็ได้โดยใช้execอากิวเมนต์

ในกรณีของคุณคุณสามารถทำสิ่งนี้:

find . -type d | cut -c 3-12 | while read line
do
       echo -n "${line}  "
       date -d $line
done

0

ใช้ Python (เป็นวิธีแก้ปัญหาที่ช้าที่สุด)

for i in $(ls -A); do echo $i | xargs python -c "from sys import argv;from time import strftime;from datetime import datetime;print datetime.fromtimestamp(float(argv[1][:-3])).strftime('%Y-%m-%d %H:%M:%S'),'---',argv[1]"; done

ให้:

2015-08-30 08:48:59 --- 1440917339340
2015-08-31 08:00:22 --- 1441000822458
2015-09-01 08:00:32 --- 1441087232437
2015-09-01 16:48:43 --- 1441118923773
2015-09-02 08:00:11 --- 1441173611869
2015-09-03 08:00:32 --- 1441260032393
2015-09-04 08:00:21 --- 1441346421651

ทำไมไม่ทำทั้งหมดในหลาม? แทนที่จะผูกมัดเป็นท่อ?
Sobrique

มันจะทำให้รู้สึกดีขึ้น ฉันเห็นด้วย.
lukaz
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.