Bagaimana cara menghubungkan perintah 'date -d @xxxxxx' dan 'find ./'?

14

Saya memiliki direktori yang namanya cap waktu, diberikan dalam milidetik sejak 1970-01-01:

1439715011728
1439793321429
1439879712214
.
.

Dan saya butuh output seperti:

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

Saya dapat mendaftar semua direktori dengan perintah:

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

Tapi saya tidak bisa menempatkan output ke perintah berikutnya: date -d @xxxxxxdan memanipulasi output.

Bagaimana saya bisa melakukan ini?

BlaMa
sumber
2
Bagaimana cap waktu itu diterjemahkan ke zaman? Karena nomor Anda terlalu panjang ... (Yang pertama - adalah Fri Oct 2 05:35:28 47592)
Sobrique
1
@Obrique Jelas milidetik sejak zamannya.
Gilles 'SO- stop being evil'

Jawaban:

10

Anda berada di jalur yang benar (untuk solusi yang lebih sederhana, hanya menjalankan 2 atau 3 perintah, lihat di bawah). Anda harus menggunakan *alih-alih ./untuk menyingkirkan direktori saat ini somewhat dan ini menyederhanakan pemotongan milidetik, kemudian cukup pipirkan hasilnya ke GNU parallelatau xargs²:

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

mendapatkan

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

dan menambahkan detik offset sebelum itu sebagai contoh Anda menunjukkan:

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

atau:

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

mendapatkan:

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

Namun lebih mudah dilakukan³:

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

yang memberi Anda hasil yang diminta sekali lagi.

Kerugian menggunakan *adalah bahwa Anda dibatasi oleh perintah Anda untuk ekspansi, namun keuntungannya adalah Anda mendapatkan direktori Anda diurutkan berdasarkan nilai timestamp. Jika jumlah direktori bermasalah digunakan -mindepth 1, tetapi kehilangan urutan:

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

dan masukkan sortjika perlu:

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

¹ Ini mengasumsikan tidak ada subdirektori bertingkat, seperti contoh dari contoh Anda. Anda juga dapat menggunakan ./ -mindepth 1alih-alih*
² Anda dapat menggantinya paralleldengan di xargs -I{}sini seperti yang disarankan @hobbs dan @don_crissti, itu hanya lebih bertele-tele. ³ berdasarkan jawaban Gilles untuk menggunakan datekemampuan membaca file

Anthon
sumber
Atau xargsjika Anda tidak punya parallel, yang mungkin tidak dimiliki banyak orang.
hobbs
@hobbs AFAIK xargstidak memiliki pilihan untuk menentukan di mana argumen itu seperti parallelmemiliki dengan {}.
Anthon
4
Itu tidak:find ./ -type d | cut -c 3-12 | xargs -I{} date --d @{} +'%Y-%m-%d'
don_crissti
@Anton melakukannya jika Anda menggunakan -Iopsi.
hobbs
1
@Anthon, opsi panjang GNU dapat disingkat selama tidak ambigu. --datau --daakan bekerja dengan versi GNU saat ini date, tetapi itu bisa berhenti bekerja hari datememperkenalkan --dalekopsi (untuk tanggal dalam kalender Dalek).
Stéphane Chazelas
10

Saya akan menghindari menjalankan beberapa perintah per file dalam satu lingkaran. Karena Anda sudah menggunakan GNUisms:

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

Yang hanya menjalankan dua perintah. strftime()khusus untuk GNU date -d.

Stéphane Chazelas
sumber
Ini tidak memotong milidetik dari nama direktori tetapi menampilkan 13 karakter penuh alih-alih yang diminta pertama 10
Anthon
@ Anthon, ah ya, melewatkan persyaratan itu. Seharusnya tidak apa-apa sekarang.
Stéphane Chazelas
8

Anda sudah memiliki:

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

yang mungkin memberi Anda cap waktu dalam format zaman. Sekarang tambahkan loop sementara:

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

Perhatikan bahwa dalam beberapa shell, sintaks itu mendapatkan loop sementara dalam subkulit, yang berarti bahwa jika Anda mencoba mengatur variabel di sana, itu tidak akan terlihat setelah Anda meninggalkan loop. Untuk memperbaikinya, Anda perlu sedikit memutar kepala:

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

yang menempatkan finddi subkulit, dan membuat loop sementara di shell utama. Sintaks itu (AT&T ksh, zshdan bashspesifik) hanya diperlukan jika Anda ingin menggunakan kembali hasil dari dalam loop.

Wouter Verhelst
sumber
bagaimanapun, mengatakan bahwa itu spesifik bash tidak benar :)
Wouter Verhelst
Sebenarnya, seperti yang Anda tulis pada awalnya, done <(find)alih-alih done < <(find), itu benar untuk yash(di mana <(...)proses redirection, bukan proses substitusi), jadi edit saya agak angkuh karena itu mungkin shell yang Anda maksudkan untuk itu.
Stéphane Chazelas
6

Jika Anda memiliki tanggal GNU, itu dapat mengonversi tanggal dibaca dari file input. Anda hanya perlu memijat cap waktu sedikit sehingga dapat mengenali mereka. Sintaks input untuk stempel waktu berdasarkan zaman Unix @diikuti oleh jumlah detik, yang dapat berisi titik desimal.

find ./ -type d ! -name '*[!0-9]*' |
sed -e 's~.*/~@~' -e 's~[0-9][0-9][0-9]$~.&~' |
date -f - +'%s  %c'
Gilles 'SANGAT berhenti menjadi jahat'
sumber
+1 untuk menggunakan datepembacaan file. Ini akan memberikan date: invalid date ‘@’karena terjemahan direktori saat ini ( ./). Dan karena Anda dapat membuang milidetik, Anda dapat menyederhanakan sedpengeditan kedua untuk hanya menjatuhkan 3 karakter terakhir. Atau hapus semua itu dan gunakanfind * -type d -printf "@%.10f" | date ...
Anthon
5

Saya akan melakukannya perlishly - memberi makan dalam daftar cap waktu:

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

Output ini:

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

Jika Anda menginginkan format output tertentu, Anda dapat menggunakan strftimemis:

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

Yang mengubah ini menjadi satu liner di pipa Anda:

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

Tapi saya mungkin menyarankan bukannya melihat menggunakan File::Findmodul dan melakukan semuanya dalam perl sebagai gantinya. Jika Anda memberikan contoh struktur direktori Anda sebelum memotongnya, saya akan memberikan Anda sebuah contoh. Tapi itu akan menjadi sesuatu seperti:

#!/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, "." ); 
Sobrique
sumber
2

Dengan zshdan strftime builtin:

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

ini mengasumsikan semua nama direktori Anda di direktori saat ini sebenarnya zaman zaman.
Pemfilteran / pemrosesan lebih lanjut dimungkinkan asalkan Anda mengklarifikasi bagaimana angka-angka dalam contoh Anda harus diproses (angka-angka itu lebih mirip dengan zaman zaman yang berkaitan dengan tanggal kelahiran Puteri Leia dan Luke Skywalker ...) mis. Pencarian secara rekursif untuk nama-nama direktori yang cocok dengan setidaknya 10 digit dan hitung tanggal berdasarkan 10 digit pertama:

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

Menggunakan GNU Parallel:

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

Jika Anda dapat menerima alih-alih ruang:

find ./ -type d | cut -c 3-12 | parallel -k --tag date -d @{}
Ole Tange
sumber
Catatan yang paralleltertulis dalam perl. Ini tampaknya berlebihan mengingat perlmemiliki strftime()operator. Sepertiperl -MPOSIX -lpe '$_.=strftime(" %c", localtime substr $_, 2, 10)'
Stéphane Chazelas
2
1. Lebih pendek. 2. Anda tidak perlu belajar Perl.
Ole Tange
1
Ini 27% lebih pendek, tetapi beberapa urutan besarnya kurang efisien (sekitar 800 kali lebih lambat dalam tes yang saya buat; anggap perlu memunculkan shell (shell Anda, bukan / bin / sh) dan perintah tanggal untuk setiap baris) dan tidak ramah pada sistem karena membebani semua CPU sekaligus. Dan Anda masih perlu belajar parallel. IMO, paralleladalah alat yang hebat untuk memparalelkan tugas-tugas intensif CPU, tetapi tidak benar-benar sesuai untuk tugas semacam ini di sini.
Stéphane Chazelas
Ada banyak konteks di mana efisiensi tidak menjadi masalah, jadi itu masih merupakan solusi yang dapat diterima, tetapi masih layak menyebutkan masalah kinerja, terutama ketika mempertimbangkan bahwa paralel biasanya berirama dengan kinerja tinggi dalam pikiran orang.
Stéphane Chazelas
0

Biasanya perintah find dapat dirantai dengan perintah apa pun menggunakan execargumen.

Dalam kasus Anda, Anda dapat melakukannya seperti ini:

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

Menggunakan Python (itu solusi yang paling lambat)

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

memberi:

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
lukaz
sumber
Mengapa tidak melakukan semuanya dengan python? Daripada merantai banyak pipa?
Sobrique
Itu akan lebih masuk akal. Saya setuju.
lukaz