'Date -d @xxxxxx' ve 'find ./' komutlarını nasıl zincirleyebilirim?


14

1970-01-01'den bu yana milisaniye olarak verilen adları zaman damgası olan dizinlerim var:

1439715011728
1439793321429
1439879712214
.
.

Ve şöyle bir çıktıya ihtiyacım var:

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
.
.

Tüm dizinleri komutla listeleyebilirim:

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

Ama çıktıyı bir sonraki komuta koyamıyorum: date -d @xxxxxxve çıktıyı değiştiremiyorum.

Bunu nasıl yapabilirim?


2
Bu zaman damgaları çağa nasıl çevrilir? Sayılarınız çok uzun olduğu için ... (Bu birincisi - öyle Fri Oct 2 05:35:28 47592)
Sobrique

1
@Sorique Dönemden beri açıkça milisaniye.
Gilles 'SO- kötü olmayı kes'

Yanıtlar:


10

Doğru yoldasınız (daha basit bir çözüm için, sadece 2 veya 3 komut çalıştırır, aşağıya bakın). Geçerli dizinden kurtulmak *yerine kullanmalısınız¹ ./ve bu, milisaniyelerin kesilmesini biraz kolaylaştırır, ardından sonucu GNU parallelveya xargs²'ye ekleyin:

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

almak

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

ve örneğinizin de belirttiği gibi bundan önceki saniye ofsetini eklemek için:

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

veya:

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

almak:

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

Ancak bunu yapmak daha kolaydır³:

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

bu size aynı istenen çıktıyı bir kez daha verir.

Kullanmanın dezavantajı *, genişlemesi için komut satırınızla sınırlı olmanızdır, ancak avantaj, dizinlerinizi zaman damgası değerine göre sıralamanızdır. Dizin sayısı -mindepth 1sorunluysa, ancak siparişi kaybederseniz:

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

ve sortgerekirse takın :

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

¹ Bu, örneğin örneğinde olduğu gibi yuvalanmış alt dizin olmadığını varsayar. Ayrıca kullanabilirsiniz ./ -mindepth 1yerine*
² Sen yerini alabilir parallelile xargs -I{}sadece daha ayrıntılı, @hobbs olarak burada ve @don_crissti önerdi. ³ kullanmak Gilles' Yanıta göre dates dosya okuma yetenekleri


Ya xargsda sahip değilseniz parallel, muhtemelen birçok insanda yoktur.
Ocaklar

@hobbs AFAIK xargsargümanı gibi nereye gittiğini belirtme seçeneği yoktur parallelile olan {}.
Anthon

4
find ./ -type d | cut -c 3-12 | xargs -I{} date --d @{} +'%Y-%m-%d'
Yapar

@ -ISeçeneği kullanırsanız yapar .
Ocaklar

1
@Anhon, GNU uzun seçenekleri belirsiz olmadıkları sürece kısaltılabilir. --dveya --daGNU güncel sürümleri ile çalışacak date, ama bu gün çalıştıktan durdurabilir datetanıtır bir --dalek(Dalek takviminde tarihlerde) seçeneği.
Stéphane Chazelas

10

Bir döngüde dosya başına birkaç komut çalıştırmaktan kaçınırım. Zaten GNUisms kullandığınız için:

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

Hangi sadece iki komut çalıştırır. strftime()GNU'ya özgüdür date -d.


Bu, dizin adlarının milisaniyesini kesmez, ancak istenen ilk 10 yerine tam 13 karakteri görüntüler
Anthon

@Anthon, ah evet, bu şartı kaçırdı. Şimdi iyi olmalı.
Stéphane Chazelas

8

Zaten sahipsin:

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

ki bu muhtemelen size zaman damgası biçiminde zaman damgalarını getirir. Şimdi bir while döngüsü ekleyin:

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

Bununla birlikte, bazı kabuklarda, sözdiziminin bir alt kabukta while döngüsünü aldığını, yani orada bir değişken ayarlamaya çalışırsanız, döngüden ayrıldıktan sonra görünmeyeceğini unutmayın. Bunu düzeltmek için, başlarındaki şeyleri hafifçe çevirmeniz gerekir:

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

findalt kabuğa koyar ve while döngüsünü ana kabukta tutar. Yani sözdizimi (AT & T ksh, zshve bashsen de, döngü içinden bir sonuç yeniden arıyorsanız spesifik) sadece ihtiyaç vardır.


ne olursa olsun, bash-özgü olduğunu söylemek doğru değil :)
Wouter Verhelst

Aslında, başlangıçta yazdığınız gibi, done <(find)bunun yerine done < <(find)doğruydu yash( <(...)süreç yönlendirmesi nerede , süreç ikamesi değil), bu yüzden benim düzenlemem, demek istediğin kabuk olabileceği için biraz daha süvari idi.
Stéphane Chazelas

6

GNU tarihiniz varsa, bir giriş dosyasından okunan tarihleri ​​dönüştürebilir. Zaman damgalarına biraz masaj yapmanız yeterlidir, böylece onları tanıyabilir. Unix dönemini temel alan bir zaman damgasının giriş sözdizimini @, ondalık nokta içerebilen saniye sayısı izler.

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

dateS dosyasını okumak için +1 . Bu date: invalid date ‘@’, geçerli dizinin ( ./) çevirisinden dolayı verecektir . Milisaniyeyi atabileceğiniz sediçin, son düzenlemeyi sadece son 3 karakteri bırakmak için basitleştirebilirsiniz . Veya hepsini kaldırın ve kullanınfind * -type d -printf "@%.10f" | date ...
Anthon

5

Ben bunu yapardı - bir zaman damgaları listesinde beslemek:

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

Bu çıktılar:

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

Belirli bir çıktı biçimi istiyorsanız, strftimeörneğin:

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

Bunu borunuzdaki tek bir astar haline getirmek için:

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

Ama muhtemelen bunun yerine File::Findmodülü kullanmayı ve her şeyi perl'de yapmayı öneriyorum . Dizin yapınızı kesmeden önce bir örnek verirseniz, size bir örnek vereceğim. Ama şöyle bir şey olurdu:

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

İle zshve strftime yerleşik:

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

bu, geçerli dizindeki tüm dizin adlarınızın gerçekte devir süreleri olduğunu varsayar.
Örneğinizdeki bu sayıların nasıl işlenmesi gerektiğini (Prenses Leia ve Luke Skywalker'ın doğum tarihlerine karşılık gelen çağ sürelerine benziyor) açıklamak şartıyla daha fazla filtreleme / işleme mümkündür, örneğin en azından eşleşen dizin adlarını tekrar tekrar arama 10 hane ve tarihi ilk 10 hane temel alınarak hesaplayın:

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

2

GNU Paralel Kullanımı:

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

Alan yerine \ t kabul edebiliyorsanız:

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

Not parallelyazılır perl. Bu perl, bir strftime()operatörü olduğu düşünülürse aşırıya kaçmış gibi görünüyor . Beğenperl -MPOSIX -lpe '$_.=strftime(" %c", localtime substr $_, 2, 10)'
Stéphane Chazelas

2
1. Daha kısadır. 2. Perl öğrenmenize gerek yoktur.
Ole Tange

1
% 27 daha kısa, ancak birkaç büyüklük daha az verimli (yaptığım testte yaklaşık 800 kat daha yavaş; bir kabuk oluşturması gerektiğini düşünün (kabuğunuz, / bin / sh değil) ve her satır için bir tarih komutu) ve tüm CPU'ları aynı anda yüklediği için sisteme düşmanca yaklaşıyor. Ve hala öğrenmen gerekiyor parallel. IMO, parallelCPU yoğun görevlerini paralelleştirmek için harika bir araçtır, ancak bu tür görevler için gerçekten uygun değildir.
Stéphane Chazelas

Verimliliğin bir endişe olmadığı birçok bağlam vardır, bu yüzden hala kabul edilebilir bir çözümdür, ancak yine de performans sorunundan bahsetmeye değer, özellikle de paralelin genellikle insanların zihninde yüksek performansla kafiyeli olduğunu düşünürken .
Stéphane Chazelas

0

Normalde find komutu, execargüman kullanılarak herhangi bir komutla zincirlenebilir .

Sizin durumunuzda bunu yapabilirsiniz:

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

0

Python kullanmak (en yavaş çözümdür)

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

verir:

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

Neden hepsini python'da yapmıyorsunuz? Bir grup boruyu zincirlemek yerine?
Sobrique

Daha mantıklı olurdu. Katılıyorum.
lukaz
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.