分享脚本工具
m24h2021/12/23原创 软件综合 IP:上海

项目均在 XXXXXXXXXXXXXXXXXX/m24h/scripts

.................................................

使用正则表达式对目录下(或及子目录下的文件进行重命名), 需要安装perl, 文件名为XXXXXXXXXt

用法:

Usage:  [-itrd] <old file name to match> <new name to apply>
 rename files use regular express
 i:ignore case
 t:test only
 r:recur subdir
 d:change directory name only
 a:add number when conflicted

脚本:

@rem = '--*-Perl-*--
@echo off
if "%OS%" == "Windows_NT" goto WinNT
perl -x -S "%0" %1 %2 %3 %4 %5 %6 %7 %8 %9
goto endofperl
:WinNT
perl -x -S %0 %*
if NOT "%COMSPEC%" == "%SystemRoot%\system32\cmd.exe" goto endofperl
if %errorlevel% == 9009 echo You do not have Perl in your PATH.
if errorlevel 1 goto script_failed_so_exit_with_non_zero_val 2>nul
goto endofperl
@rem ';
#!perl
#line 15

local ($OLD,$NEW,$I,$T,$R,$A,$D);

sub getfiles($)
{
    my $dir=shift;
    my $f;
    my @file=();
    my @subdir;
    my $t;

    opendir ($f, $dir) || die "can't opendir $dir: $!\n";
    @subdir=grep { !/^\.$/ && !/^\.\.$/ } readdir($f);
    closedir $f;

    foreach $t(@subdir) {
        if (-d "$dir/$t") {
            push @file, getfiles("$dir/$t") if ($R);
            push @file, "$dir/$t" if ($D);
        } elsif (-f "$dir/$t") {
            push @file, "$dir/$t" unless ($D);
        }
    }

    return @file;
}

sub init()
{
	my $arg;
	while (defined($arg=shift @ARGV)) {
	    if (substr($arg,0,1) eq '-') {
	   		$T=1 if ($arg=~/t/);
	        $I=1 if ($arg=~/i/);
	        $R=1 if ($arg=~/r/);
	        $A=1 if ($arg=~/a/);
	       	$D=1 if ($arg=~/d/);
	    }  elsif (not defined $OLD) {
	        $OLD=$arg;
	    }  elsif (not defined $NEW) {
	        $NEW=$arg;
	    }
	}
	
	die "Usage: $0 [-itrd] <old file name to match> <new name to apply>\n rename files use regular express\n i:ignore case\n t:test only\n r:recur subdir\n d:change directory name only\n a:add number when conflicted\n"
		if (!defined($OLD) || !defined($NEW) || $OLD eq "")
}

sub ren($) 
{
	my $f=shift;
	my $d='';
	if ($f=~/^(.*[\\\/])([^\\\/]*)$/) {
		$d=$1 || '';
		$f=$2;
	}
	
	eval "\$f=~s/$OLD/$NEW/g".($I?'i':'').';';
    die "$@" if ($@);
	return $d.$f;
} 

sub addnum($$)
{
	my ($f,$i)=@_;
	$f=~s/(\.[^\\\/\.]*)?$/ ($i)$1/;
	return $f;
}

init();
my %chg;
my %all;
foreach (getfiles('.')) {
	$all{$_}='original in directory';
	my $d=ren($_);
	next if ($d eq $_);
	if (exists $all{$d}) {
		if ($A) {
			my $i=1;
			my $n;
			$i++ while (exists $all{addnum($d,$i)});
			$d=addnum($d,$i);
		} else {
			die "name conflicted when change '$_' -> '$d' ($all{$d})\n";
		}
	}
	$chg{$_}=$d;
	delete $all{$_};
	$all{$d}="renamed from '$_'";
}	
my $failed=0;
my $total=0;
foreach (keys %chg) {
    print "$_\n -> $chg{$_}\n";
    $total++;
    unless ($T) {
    	eval {$failed++ unless (rename $_,$chg{$_});};
        print "$@\n";
    } 
}
print STDERR "total:$total, failed:$failed\n"; 

exit 0;

__END__
:endofperl


[修改于 3时25分前 - 2024/03/28 14:31:54]

来自:计算机科学 / 软件综合
3
已屏蔽 原因:{{ notice.reason }}已屏蔽
{{notice.noticeContent}}
~~空空如也
m24h 作者
2年3个月前 修改于 3时25分前 IP:上海
899771

根据(包括子目录里面的)照片exif时间对照片重命名和排序 需要安装perl和Image::ExifTool

Usage: photoname.bat <dir>
rename .jpg files to YYYYMMDD-###-where-what [.jpg/tiff/tiff/dng/nef], sub-folders is included
@REM ='
@perl -x -S %0 %*
@goto endofperl
@REM ';
#!perl
use Image::ExifTool qw(:Public);

die "Usage: photoname.pl <dir>\nrename .jpg files to YYYYMMDD-###-where-what [.jpg/tiff/tiff/dng/nef], sub-folders is included\n" if ($#ARGV<0);

sub getfiles($)
{
    my $dir=shift;
    my $F;
    my @file=();
    my @subdir;
    my $t;

    opendir ($F, $dir) || die "can't opendir $dir: $!";
    @subdir=grep { !/^\.$/ && !/^\.\.$/ } readdir($F);
    closedir $F;

    foreach $t(@subdir) {
        if (-d "$dir/$t") {
            push @file, getfiles("$dir/$t") ;
        }
        elsif ($t=~/\.(jpg)|(tif)|(tiff)|(dng)|(nef)$/i && -f "$dir/$t") {
            push @file, "$dir/$t";
        }
    }

    return @file;
}

local %NUM;
sub getindex($)
{
	my $d=shift;
	$NUM{$d}=0 unless exists($NUM{$d}); 
      $NUM{$d}=$NUM{$d}+1;
      return sprintf("%03d",$NUM{$d});	
}

my %newname;
my $t;
my $f;
my $exif;
foreach $f (getfiles($ARGV[0])) {
	if ($f=~/^(.*[\\\/])?(\d\d\d\d\d\d\d\d)\-(\d\d\d)\-(.*)(\.[^.]+)$/i) {
		$newname{$2.'-000000-'.$3.'-'.$f}=[$f,$1,$2,$4,lc($5)];
	} elsif ($f=~/^(.*[\\\/])?(\d\d\d\d)\D(\d\d)\D(\d\d)\D(\d\d)\D(\d\d)\D(\d\d)(.*)(\.[^.]+)$/i) {
		$newname{$2.$3.$4.'-'.$5.$6.$7.'-000-'.$f}=[$f,$1,$2.$3.$4,$8,lc($9)];
	} elsif ($f=~/^(.*[\\\/])?(.*)(\.[^.]+)$/i) {
		my ($t1,$t2,$t3)=($1,$2,$3);
		$exif=ImageInfo($f);
#		$t=$exif->{'GPSTimeStamp'};
#		if ($t=~/^(\d\d\d\d)\D(\d\d)\D(\d\d)\D(\d\d)\D(\d\d)\D(\d\d)\D*$/) {
#			$newname{$1.$2.$3.'-'.$4.$5.$6.'-000-'.$f}=[$f,$t1,$1.$2.$3,$t2,lc($t3)];
#			next;
#		}
		
		$t=$exif->{'DateTimeOriginal'};
		if ($t=~/^(\d\d\d\d)\D(\d\d)\D(\d\d)\D(\d\d)\D(\d\d)\D(\d\d)\D*$/) {
			$newname{$1.$2.$3.'-'.$4.$5.$6.'-000-'.$f}=[$f,$t1,$1.$2.$3,$t2,lc($t3)];
			next;
		} 
		
		print "can't get EXIF of $f\n";
	}  else {
		print "bad $f\n";
	}
}	

for my $k(sort keys %newname) {
	$t=$newname{$k};
	$f=$t->[1].$t->[2].'-'.getindex($t->[2]).'-'.$t->[3].$t->[4];
	next if (lc($f) eq lc($t->[0]));
	print ($t->[0],'->',$f,"\n");
	if (-f $f) {
    	print "File name exists: $f\n";
    }
    else {
        eval {rename $t->[0],$f;};
        print "$@\n";
    }
}

__END__
:endofperl


引用
评论
加载评论中,请稍候...
200字以内,仅用于支线交流,主线讨论请采用回复功能。
折叠评论
m24h作者
5个月15天前 修改于 5个月14天前 IP:上海
926256

今天需要对测试数据进行滤波处理 没有matlab工具箱 本来想用scipy写代码 忽然想起以前做的东西有这部分 干脆修改提取出来 做成独立的 可从文件读取一列数据 查看fft 进行IIR滤波(可选择多种滤波方式) 然后有绘图 可以保存滤波后的数据 就一个独立文件 保存为bat文件可以直接运行 (当然要装python 及numpy scipy matplotlib tk等部件

---------------2023/10/16-------------

修改 加上FIR滤波设计 这下程序里做点滤波 就不用局限在平滑滤波了 有更高效的 对数据点预先实验 实验好了 直接采用系数就可以了 

0<0# : ^
''' 
@python %~f0 %* 
@goto :eof
'''

import tkinter as tk
from tkinter import ttk 
import tkinter.messagebox
import tkinter.filedialog
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2Tk
from matplotlib.figure import Figure
from matplotlib import pyplot
import numpy
from scipy import signal, fft

# main window
win = tk.Tk()
# data values
data=None

# main window
validate_float=win.register(lambda v: (v:=v.replace('.','',1))=='' or (v:=v[1:] if v[0]=='-' else v)=='' or v.isdigit())
validate_int=win.register(lambda v:  v=='' or (v:=v[1:] if v[0]=='-' else v)=='' or v.isdigit())
win.geometry('+%d+100'%((win.winfo_screenwidth()-900)/2))
win.title('Data Filter')
control_bar=tk.Frame(win)
control_bar.pack(side='left', anchor='n', fill='y', expand=True)
btn_trim=tk.Button(control_bar, text="Load Data", command=lambda : load())
btn_trim.pack(side='top', anchor='s', fill='x', pady=(0, 5))
btn_trim=tk.Button(control_bar, text="Trim/Invert", state="disabled", command=lambda : trim())
btn_trim.pack(side='top', anchor='s', fill='x', pady=(0, 5))
btn_fft=tk.Button(control_bar, text="FFT", state="disabled", command=lambda : plot_fft())
btn_fft.pack(side='top', anchor='s', fill='x', pady=(0, 5))
btn_filter=tk.Button(control_bar, text="Filter (IIR)", command=lambda : filter_iir())
btn_filter.pack(side='top', anchor='s', fill='x', pady=(0, 5))
btn_filter=tk.Button(control_bar, text="Filter (FIR)", command=lambda : filter_fir())
btn_filter.pack(side='top', anchor='s', fill='x', pady=(0, 5))
tkvar_msg=tk.StringVar(value='')
tk.Label(control_bar, textvariable=tkvar_msg, anchor='nw', justify='left', wraplength=150).pack(side='top', anchor='nw', fill='y', expand=True)
win.protocol("WM_DELETE_WINDOW", lambda: pyplot.close('all') is win.destroy())

# matplotlib plot in tk
def on_scroll(evt):
	if thread is None and (axe:=evt.inaxes) is not None and (evt.button=='up' or evt.button=='down'):
		xmin,xmax=axe.get_xlim()
		ymin,ymax=axe.get_ylim()
		scale=0.9 if evt.button=='up' else 1.1
		axe.set(xlim=(evt.xdata-(evt.xdata-xmin)*scale, evt.xdata+(xmax-evt.xdata)*scale),
		            ylim=(evt.ydata-(evt.ydata-ymin)*scale, evt.ydata+(ymax-evt.ydata)*scale))
		evt.canvas.draw_idle()
figure=Figure(figsize=(6, 5), dpi=100)
figure.subplots_adjust(left=0.2, top=0.95, right=0.95)
canvas=FigureCanvasTkAgg(figure, master=win)
canvas.get_tk_widget().pack(side='top', anchor='ne', fill='x', expand=True)
canvas.mpl_connect('scroll_event', on_scroll)
navbar=NavigationToolbar2Tk(canvas, win, pack_toolbar=False)
tk.Frame(navbar, bd=1, width=2, highlightthickness=0, relief='groove').pack(side='left', anchor='w', padx=5, pady=3, fill='y')
btn_save=tk.Button(navbar, text="SAVE DATA", state="disabled", command=lambda : save())
btn_save.pack(side='left')
navbar.update()
navbar.pack(side='bottom', anchor='nw', fill='x')
axe1=figure.add_subplot(111) # only 1 plot in 1x1 table now

# when 'load' button clicked, load data from file
def load():
	global data
	try:
		if (file:=tk.filedialog.askopenfilename(title='Load Data File', defaultextension='.txt', filetypes=[('Text','*.txt'),('All files','*')])):
			data=numpy.loadtxt(file, dtype=numpy.float64)
			btn_save['state']='normal' if len(data)>0 else 'disabled'
			btn_trim['state']='normal' if len(data)>0 else 'disabled'
			btn_fft['state']='normal' if len(data)>0 else 'disabled'
			btn_filter['state']='normal' if len(data)>0 else 'disabled'
			plot()
	except Exception as e:
		tk.messagebox.showerror(title='Error', message=e)
	
#when 'save' button clicked
def save():
	try:
		if (file:=tk.filedialog.asksaveasfile(title='Save Data File', defaultextension='.txt', filetypes=[('Text','*.txt'),('All files','*')])):
			with file:
				for t in data:
					file.write(str(t)+'\n')
	except Exception as e:
		tk.messagebox.showerror(title='Error', message=e)

# wait for complete and render the canvas
_last_draw=None
def plot():
	axe1.cla()
	axe1.set_ylabel("Data Value")
	axe1.set_xlabel("Points : Total "+str(len(data)))
	axe1.grid(True)
	global _last_draw
	if _last_draw is not None:
		_last_draw.remove()
	_last_draw, =axe1.plot(data, color='orange', linewidth=1)
	canvas.draw_idle()
	navbar.update()
	navbar.push_current() 

#when trim button is clicked
def trim():
	def _trim():
		global data
		try:
			h=ctrl_head.get().strip()
			t=ctrl_tail.get().strip()
			h=0 if h=='' else max(0, min(len(data)-1, int(h)))
			t=len(data) if t=='' else min(max(int(t)+1, h+1), len(data))
			invt=var_invt.get()
		except ValueError as e:
			tk.messagebox.showerror(title='Error', message=e, parent=dlg)
		else:
			if h>0 or t<len(data):
				data=data[h:t]
			if invt:
				data=data*-1.0
			plot()
			dlg.destroy()
	dlg=tk.Toplevel(win)
	wingeo=win.geometry().split('+')
	dlg.geometry('+'+str(int(wingeo[1])+250)+'+'+str(int(wingeo[2])+200))
	dlg.grab_set()
	dlg.title('Trim')
	tbl=tk.Frame(dlg)
	tbl.pack(side='top', fill='x', padx=10, pady=10, ipadx=10)
	tk.Label(tbl, anchor='e', text='   Keep data from index').grid(row=0, column=0, sticky='we')
	ctrl_head=tk.Entry(tbl, width=5, validate='key', validatecommand=(validate_int,'%P'))
	ctrl_head.grid(row=0, column=1, sticky='we')
	tk.Label(tbl, anchor='e', text='   Keep data to index').grid(row=1, column=0, sticky='we')
	ctrl_tail=tk.Entry(tbl, width=5, validate='key', validatecommand=(validate_int,'%P'))
	ctrl_tail.grid(row=1, column=1, sticky='we')
	tk.Label(tbl, anchor='e', text='   Invert').grid(row=2, column=0, sticky='we')
	var_invt=tk.BooleanVar(value=False)
	tk.Checkbutton(tbl, variable=var_invt, onvalue=True, offvalue=False).grid(row=2, column=1, sticky='w')
	tk.Button(dlg, text="  OK  ", command=_trim).pack(side='right', padx=5, anchor='n', pady=5)
	tk.Button(dlg, text="CANCEL", command=dlg.destroy).pack(side='right', anchor='n', pady=5)

#plot fft
def plot_fft():
	pyplot.close('all')
	yf = fft.rfft(data)
	xf = fft.rfftfreq(len(data), 1)
	fig, ax1=pyplot.subplots()
	fig.subplots_adjust(right=0.85)
	ax1.set_title('FFT')
	ax1.set_xlabel('Freq. ')
	ax1.set_ylabel('Amplitude')
	ax1.grid()
	ax1.plot(xf, numpy.abs(yf), linewidth=1, color='orange')
	pyplot.show()

#when 'Filter (IIR)' button is clicked
def filter_iir():
	sr=1
	def dofilter():
		global data
		try:
			b=tuple(float(t.strip()) for t in var_coef_b.get().split(','))
			a=tuple(float(t.strip()) for t in var_coef_a.get().split(','))
			data=signal.filtfilt(b, a, data)
			plot()
			dlg.destroy()
		except Exception as e:
			tk.messagebox.showerror(title='Error', message=e, parent=dlg)
	def show_freq():
		try:
			b=tuple(float(t.strip()) for t in var_coef_b.get().split(','))
			a=tuple(float(t.strip()) for t in var_coef_a.get().split(','))
			w,h=signal.freqz(b, a, worN=250, fs=sr)
			pyplot.close('all')
			fig, ax1=pyplot.subplots()
			fig.subplots_adjust(right=0.85)
			ax1.set_title('Filter frequency response')
			ax1.set_xlabel('Freq. (Hz)')
			ax1.set_ylabel('Response', color='blue')
			ax1.grid()
			ax1.plot(w, numpy.abs(h), linewidth=1, color='blue')
			ax2=ax1.twinx()
			ax2.set_ylabel('Phase (degree)', color='orange')
			ax2.plot(w, numpy.unwrap(numpy.angle(h))*180/3.141592653589793, linewidth=1, color='orange')
			pyplot.show()
		except Exception as e:
			tk.messagebox.showerror(title='Error', message=e, parent=dlg)
	def choose_bessel():
		try:
			type=('lowpass','highpass','bandpass','bandstop')[ctrl_bessel_type.current()]
			order=int(ctrl_bessel_order.get().strip())
			freqs=tuple(float(f.strip()) for f in ctrl_bessel_freq.get().split(','))
			for f in freqs:
				assert f<sr/2 and f>0.0, f'frequency must be in (0,{sr/2})'
			b,a=signal.bessel(order, freqs, type, fs=sr)
			var_coef_b.set(','.join(str(x) for x in b))
			var_coef_a.set(','.join(str(x) for x in a))
		except Exception as e:
			tk.messagebox.showerror(title='Error', message=e, parent=dlg)
	def choose_butt():
		try:
			type=('lowpass','highpass','bandpass','bandstop')[ctrl_butt_type.current()]
			order=int(ctrl_butt_order.get().strip())
			freqs=tuple(float(f.strip()) for f in ctrl_butt_freq.get().split(','))
			for f in freqs:
				assert f<sr/2 and f>0.0, f'frequency must be in (0,{sr/2})'
			b,a=signal.butter(order, freqs, type, fs=sr)
			var_coef_b.set(','.join(str(x) for x in b))
			var_coef_a.set(','.join(str(x) for x in a))
		except Exception as e:
			tk.messagebox.showerror(title='Error', message=e, parent=dlg)
	def choose_cheby1():
		try:
			type=('lowpass','highpass','bandpass','bandstop')[ctrl_cheby1_type.current()]
			order=int(ctrl_cheby1_order.get().strip())
			ripple=int(ctrl_cheby1_ripple.get().strip())
			freqs=tuple(float(f.strip()) for f in ctrl_cheby1_freq.get().split(','))
			for f in freqs:
				assert f<sr/2 and f>0.0, f'frequency must be in (0,{sr/2})'
			b,a=signal.cheby1(order, ripple, freqs, type, fs=sr)
			var_coef_b.set(','.join(str(x) for x in b))
			var_coef_a.set(','.join(str(x) for x in a))
		except Exception as e:
			tk.messagebox.showerror(title='Error', message=e, parent=dlg)
	def choose_cheby2():
		try:
			type=('lowpass','highpass','bandpass','bandstop')[ctrl_cheby2_type.current()]
			order=int(ctrl_cheby2_order.get().strip())
			attenuation=float(ctrl_cheby2_attenuation.get().strip())
			freqs=tuple(float(f.strip()) for f in ctrl_cheby2_freq.get().split(','))
			for f in freqs:
				assert f<sr/2 and f>0.0, f'frequency must be in (0,{sr/2})'
			b,a=signal.cheby2(order, attenuation, freqs, type, fs=sr)
			var_coef_b.set(','.join(str(x) for x in b))
			var_coef_a.set(','.join(str(x) for x in a))
		except Exception as e:
			tk.messagebox.showerror(title='Error', message=e, parent=dlg)			
	def choose_npc():
		try:
			type=ctrl_npc_type.current()
			assert type>=0 and type<=3
			q=float(ctrl_npc_q.get().strip())
			f=float(ctrl_npc_freq.get().strip())
			assert f<sr/2 and f>0.0, f'frequency must be in (0,{sr/2})'
			if type==0:
				b,a=signal.iirnotch(f, q, fs=sr)
			elif type==1:
				b,a=signal.iirpeak(f, q, fs=sr)
			elif type==2:
				b,a=signal.iircomb(f, q, ftype='notch', fs=sr)
			elif type==3:
				b,a=signal.iircomb(f, q, ftype='peak', fs=sr)
			var_coef_b.set(','.join(str(x) for x in b))
			var_coef_a.set(','.join(str(x) for x in a))
		except Exception as e:
			tk.messagebox.showerror(title='Error', message=e, parent=dlg)			
	dlg=tk.Toplevel(win)
	wingeo=win.geometry().split('+')
	dlg.geometry('+'+str(int(wingeo[1])+50)+'+'+str(int(wingeo[2])+50))
	dlg.grab_set()
	dlg.title('Filter')
	tk.Label(dlg, justify='left', text='''\
Choose filter needed with right parameters for it, to generation the appropriate coefficients,
then click 'OK' button. the coefficients are ',' separated, and the Z-domain transfer function is :
	Y(z)/X(z)=(b[0]+b[1]/z+b[2]/z^2+...)/(a[0]+a[1]/z+a[2]/z^2...)
''').pack(side='top', anchor='w')
	tbl_gen=tk.Frame(dlg)
	tbl_gen.pack(side='top', fill='x', ipadx=5, ipady=5)
	irow=0
	tk.Label(tbl_gen, text='Bessel/Thomson : for Band-Pass/Band-Stop, input 2 Freq. separated by ","').grid(columnspan=9, row=irow, column=0, sticky='w')
	tk.Label(tbl_gen, anchor='w', text='Type :').grid(row=(irow:=irow+1), column=0, sticky='we')
	ctrl_bessel_type=ttk.Combobox(tbl_gen, justify='left', values=('Low-Pass','High-Pass','Band-Pass','Band-Stop'), state='readonly')
	ctrl_bessel_type.current(0)
	ctrl_bessel_type.grid(row=irow, column=1, sticky='we')
	tk.Label(tbl_gen, anchor='e', text='  Freq. : ').grid(row=irow, column=2, sticky='we')
	ctrl_bessel_freq=tk.Entry(tbl_gen, width=10)
	ctrl_bessel_freq.grid(row=irow, column=3, sticky='w')
	tk.Label(tbl_gen, anchor='e', text='  Order : ').grid(row=irow, column=4, sticky='we')
	ctrl_bessel_order=tk.Entry(tbl_gen, width=5, validate='key', validatecommand=(validate_int,'%P'))
	ctrl_bessel_order.grid(row=irow, column=5, sticky='w')
	tk.Button(tbl_gen, text="CHOOSE", command=choose_bessel).grid(row=irow, column=8, padx=5, sticky='e')
	tk.Label(tbl_gen, text='Butterworth : for Band-Pass/Band-Stop, input 2 Freq. separated by ","').grid(columnspan=9, row=(irow:=irow+1), column=0, sticky='w')
	tk.Label(tbl_gen, anchor='w', text='Type :').grid(row=(irow:=irow+1), column=0, sticky='we')
	ctrl_butt_type=ttk.Combobox(tbl_gen, justify='left', values=('Low-Pass','High-Pass','Band-Pass','Band-Stop'), state='readonly')
	ctrl_butt_type.current(0)
	ctrl_butt_type.grid(row=irow, column=1, sticky='we')
	tk.Label(tbl_gen, anchor='e', text='  Freq. : ').grid(row=irow, column=2, sticky='we')
	ctrl_butt_freq=tk.Entry(tbl_gen, width=10)
	ctrl_butt_freq.grid(row=irow, column=3, sticky='w')
	tk.Label(tbl_gen, anchor='e', text='  Order : ').grid(row=irow, column=4, sticky='we')
	ctrl_butt_order=tk.Entry(tbl_gen, width=5, validate='key', validatecommand=(validate_int,'%P'))
	ctrl_butt_order.grid(row=irow, column=4, sticky='w')
	tk.Button(tbl_gen, text="CHOOSE", command=choose_butt).grid(row=irow, column=8, padx=5, sticky='e')
	tk.Label(tbl_gen, text='Chebyshev I : for Band-Pass/Band-Stop, input 2 Freq. separated by ","').grid(columnspan=9, row=(irow:=irow+1), column=0, sticky='w')
	tk.Label(tbl_gen, anchor='w', text='Type :').grid(row=(irow:=irow+1), column=0, sticky='we')
	ctrl_cheby1_type=ttk.Combobox(tbl_gen, justify='left', values=('Low-Pass','High-Pass','Band-Pass','Band-Stop'), state='readonly')
	ctrl_cheby1_type.current(0)
	ctrl_cheby1_type.grid(row=irow, column=1, sticky='we')
	tk.Label(tbl_gen, anchor='e', text='  Freq. : ').grid(row=irow, column=2, sticky='we')
	ctrl_cheby1_freq=tk.Entry(tbl_gen, width=10)
	ctrl_cheby1_freq.grid(row=irow, column=3, sticky='w')
	tk.Label(tbl_gen, anchor='e', text='  Order : ').grid(row=irow, column=4, sticky='we')
	ctrl_cheby1_order=tk.Entry(tbl_gen, width=5, validate='key', validatecommand=(validate_int,'%P'))
	ctrl_cheby1_order.grid(row=irow, column=5, sticky='w')
	tk.Label(tbl_gen, anchor='e', text=' Number of Ripples : ').grid(row=irow, column=6, sticky='we')
	ctrl_cheby1_ripple=tk.Entry(tbl_gen, width=5, validate='key', validatecommand=(validate_int,'%P'))
	ctrl_cheby1_ripple.grid(row=irow, column=7, sticky='w')
	tk.Button(tbl_gen, text="CHOOSE", command=choose_cheby1).grid(row=irow, column=8, padx=5, sticky='e')	
	tk.Label(tbl_gen, text='Chebyshev II : for Band-Pass/Band-Stop, input 2 Freq. separated by ","').grid(columnspan=9, row=(irow:=irow+1), column=0, sticky='w')
	tk.Label(tbl_gen, anchor='w', text='Type :').grid(row=(irow:=irow+1), column=0, sticky='we')
	ctrl_cheby2_type=ttk.Combobox(tbl_gen, justify='left', values=('Low-Pass','High-Pass','Band-Pass','Band-Stop'), state='readonly')
	ctrl_cheby2_type.current(0)
	ctrl_cheby2_type.grid(row=irow, column=1, sticky='we')
	tk.Label(tbl_gen, anchor='e', text='  Freq. : ').grid(row=irow, column=2, sticky='we')
	ctrl_cheby2_freq=tk.Entry(tbl_gen, width=10)
	ctrl_cheby2_freq.grid(row=irow, column=3, sticky='w')
	tk.Label(tbl_gen, anchor='e', text='  Order : ').grid(row=irow, column=4, sticky='we')
	ctrl_cheby2_order=tk.Entry(tbl_gen, width=5, validate='key', validatecommand=(validate_int,'%P'))
	ctrl_cheby2_order.grid(row=irow, column=5, sticky='w')
	tk.Label(tbl_gen, anchor='e', text='  Attenuation (dB) : ').grid(row=irow, column=6, sticky='we')
	ctrl_cheby2_attenuation=tk.Entry(tbl_gen, width=5, validate='key', validatecommand=(validate_float,'%P'))
	ctrl_cheby2_attenuation.grid(row=irow, column=7, sticky='w')
	tk.Button(tbl_gen, text="CHOOSE", command=choose_cheby2).grid(row=irow, column=8, padx=5, sticky='e')	
	tk.Label(tbl_gen, text='Notch/Peak/Comb : Q=Freq/Bandwidth(-3dB)').grid(columnspan=9, row=(irow:=irow+1), column=0, sticky='w')
	tk.Label(tbl_gen, anchor='w', text='Type :').grid(row=(irow:=irow+1), column=0, sticky='we')
	ctrl_npc_type=ttk.Combobox(tbl_gen, justify='left', values=('Notch','Peak','Comb-Notch','Comb-Peak'), state='readonly')
	ctrl_npc_type.current(0)
	ctrl_npc_type.grid(row=irow, column=1, sticky='we')
	tk.Label(tbl_gen, anchor='e', text='  Freq. : ').grid(row=irow, column=2, sticky='we')
	ctrl_npc_freq=tk.Entry(tbl_gen, width=10,  validate='key', validatecommand=(validate_float,'%P'))
	ctrl_npc_freq.grid(row=irow, column=3, sticky='w')
	tk.Label(tbl_gen, anchor='e', text='  Q value : ').grid(row=irow, column=4, sticky='we')
	ctrl_npc_q=tk.Entry(tbl_gen, width=5, validate='key', validatecommand=(validate_float,'%P'))
	ctrl_npc_q.grid(row=irow, column=5, sticky='w')
	tk.Button(tbl_gen, text="CHOOSE", command=choose_npc).grid(row=irow, column=8, padx=5, sticky='e')	
	tbl_coef=ttk.Frame(dlg, padding=(0,10,0,0))
	tbl_coef.pack(side='top', fill='x', ipadx=5, ipady=5)
	var_coef_b=tk.StringVar(value='')
	var_coef_a=tk.StringVar(value='')
	tk.Label(tbl_coef, anchor='w', text='Coef. B :').grid(row=0, column=0, sticky='we')
	tk.Entry(tbl_coef, width=100, textvariable=var_coef_b).grid(row=0, column=1, sticky='we')
	tk.Label(tbl_coef, anchor='w', text='Coef. A :').grid(row=1, column=0, sticky='we')
	tk.Entry(tbl_coef, width=100, textvariable=var_coef_a).grid(row=1, column=1, sticky='we')
	btn_ok=tk.Button(dlg, text="  OK  ", command=dofilter)
	btn_ok.pack(side='right', padx=5, anchor='n', pady=5)
	tk.Button(dlg, text="  Show Freq. Response  ", command=show_freq).pack(side='right', padx=(5,0), anchor='n', pady=5)
	tk.Button(dlg, text="CANCEL", command=lambda: pyplot.close('all') is dlg.destroy()).pack(side='right', anchor='n', pady=5)
	global data
	btn_ok['state']='normal' if data is not None and len(data)>0 else 'disabled'
	dlg.protocol("WM_DELETE_WINDOW", lambda: pyplot.close('all') is dlg.destroy())

#when 'Filter (IIR)' button is clicked
def filter_fir():
	sr=1
	def dofilter():
		global data
		try:
			c=tuple(float(t.strip()) for t in var_coef.get().split(','))
			data=signal.filtfilt(c, 1.0, data)
			plot()
			dlg.destroy()
		except Exception as e:
			tk.messagebox.showerror(title='Error', message=e, parent=dlg)
	def show_freq():
		try:
			c=tuple(float(t.strip()) for t in var_coef.get().split(','))
			w,h=signal.freqz(c, 1.0, worN=250, fs=sr)
			pyplot.close('all')
			fig, ax1=pyplot.subplots()
			fig.subplots_adjust(right=0.85)
			ax1.set_title('Filter frequency response')
			ax1.set_xlabel('Freq. (Hz)')
			ax1.set_ylabel('Response', color='blue')
			ax1.grid()
			ax1.plot(w, numpy.abs(h), linewidth=1, color='blue')
			ax2=ax1.twinx()
			ax2.set_ylabel('Phase (degree)', color='orange')
			ax2.plot(w, numpy.unwrap(numpy.angle(h))*180/3.141592653589793, linewidth=1, color='orange')
			pyplot.show()
		except Exception as e:
			tk.messagebox.showerror(title='Error', message=e, parent=dlg)
	def choose_win1a():
		try:
			type=('lowpass','highpass','bandpass','bandstop')[ctrl_win1a_type.current()]
			width=float(ctrl_win1a_width.get().strip())
			assert width<sr/2 and width>0.0, f'width must be in (0,{sr/2})'
			attenuation=float(ctrl_win1a_attenuation.get().strip())
			freqs=tuple(float(f.strip()) for f in ctrl_win1a_freq.get().split(','))
			for f in freqs:
				assert f<sr/2 and f>0.0, f'frequency must be in (0,{sr/2})'
			n, beta =signal.kaiserord(attenuation, width/0.5)
			if type!='lowpass' and n%2==0:
				n=n+1
			c = signal.firwin(n, freqs, window=('kaiser', beta), pass_zero=type, fs=sr)
			var_coef.set(','.join(str(x) for x in c))
		except Exception as e:
			tk.messagebox.showerror(title='Error', message=e, parent=dlg)
	def choose_win1b():
		try:
			type=('lowpass','highpass','bandpass','bandstop')[ctrl_win1b_type.current()]
			order=int(ctrl_win1b_order.get().strip())
			win=ctrl_win1b_win.get()
			freqs=tuple(float(f.strip()) for f in ctrl_win1b_freq.get().split(','))
			for f in freqs:
				assert f<sr/2 and f>0.0, f'frequency must be in (0,{sr/2})'
			c = signal.firwin(order+1, freqs, window=win, pass_zero=type, fs=sr)
			var_coef.set(','.join(str(x) for x in c))
		except Exception as e:
			tk.messagebox.showerror(title='Error', message=e, parent=dlg)
	def choose_win2():
		try:
			order=int(ctrl_win2_order.get().strip())
			win=ctrl_win2_win.get()
			freqs=tuple(float(f.strip()) for f in ctrl_win2_freq.get().split(','))
			for f in freqs:
				assert f<=sr/2 and f>=0.0, f'frequency must be in [0,{sr/2}]'
			gains=tuple(float(f.strip()) for f in ctrl_win2_gain.get().split(','))
			c = signal.firwin2(order+1, freqs, gains, window=win, fs=sr)
			var_coef.set(','.join(str(x) for x in c))
		except Exception as e:
			tk.messagebox.showerror(title='Error', message=e, parent=dlg)
	dlg=tk.Toplevel(win)
	wingeo=win.geometry().split('+')
	dlg.geometry('+'+str(int(wingeo[1])+50)+'+'+str(int(wingeo[2])+50))
	dlg.grab_set()
	dlg.title('Filter')
	tk.Label(dlg, justify='left', text='''\
Choose filter needed with right parameters for it, to generation the appropriate coefficients,
then click 'OK' button. the coefficients are ',' separated, and the Z-domain transfer function is :
	Y(z)/X(z)=c[0]+c[1]/z+c[2]/z^2+...
''').pack(side='top', anchor='w')
	tbl_gen=tk.Frame(dlg)
	tbl_gen.pack(side='top', fill='x', ipadx=5, ipady=5)
	irow=0
	tk.Label(tbl_gen, text='Win 1.a (Kaiser window) : input Freq. (separated by ",") , width and attenuation').grid(columnspan=9, row=irow, column=0, sticky='w')
	tk.Label(tbl_gen, anchor='w', text='Type :').grid(row=(irow:=irow+1), column=0, sticky='we')
	ctrl_win1a_type=ttk.Combobox(tbl_gen, justify='left', values=('Low-Pass','High-Pass','Band-Pass','Band-Stop'), state='readonly')
	ctrl_win1a_type.current(0)
	ctrl_win1a_type.grid(row=irow, column=1, sticky='we')
	tk.Label(tbl_gen, anchor='e', text='Freq :').grid(row=irow, column=2, sticky='we')
	ctrl_win1a_freq=tk.Entry(tbl_gen, width=10)
	ctrl_win1a_freq.grid(row=irow, column=3, sticky='w')
	tk.Label(tbl_gen, anchor='e', text='Width :').grid(row=irow, column=4, sticky='we')
	ctrl_win1a_width=tk.Entry(tbl_gen, width=10)
	ctrl_win1a_width.grid(row=irow, column=5, sticky='w')
	tk.Label(tbl_gen, anchor='e', text='  Attenuation (dB) : ').grid(row=irow, column=6, sticky='we')
	ctrl_win1a_attenuation=tk.Entry(tbl_gen, width=5, validate='key', validatecommand=(validate_float,'%P'))
	ctrl_win1a_attenuation.grid(row=irow, column=7, sticky='w')
	tk.Button(tbl_gen, text="CHOOSE", command=choose_win1a).grid(row=irow, column=8, padx=5, sticky='e')
	tk.Label(tbl_gen, text='Win 1.b : input Freq. (separated by ",") , order (even num. for other than Low-Pass), window').grid(columnspan=9, row=(irow:=irow+1), column=0, sticky='w')
	tk.Label(tbl_gen, anchor='w', text='Type :').grid(row=(irow:=irow+1), column=0, sticky='we')
	ctrl_win1b_type=ttk.Combobox(tbl_gen, justify='left', values=('Low-Pass','High-Pass','Band-Pass','Band-Stop'), state='readonly')
	ctrl_win1b_type.current(0)
	ctrl_win1b_type.grid(row=irow, column=1, sticky='we')
	tk.Label(tbl_gen, anchor='e', text='Freq :').grid(row=irow, column=2, sticky='we')
	ctrl_win1b_freq=tk.Entry(tbl_gen, width=10)
	ctrl_win1b_freq.grid(row=irow, column=3, sticky='w')
	tk.Label(tbl_gen, anchor='e', text='Order :').grid(row=irow, column=4, sticky='we')
	ctrl_win1b_order=tk.Entry(tbl_gen, width=10)
	ctrl_win1b_order.grid(row=irow, column=5, sticky='w')
	tk.Label(tbl_gen, anchor='e', text=' Window : ').grid(row=irow, column=6, sticky='we')
	ctrl_win1b_win=ttk.Combobox(tbl_gen, justify='left', values=('boxcar','triang','blackman','hamming','hann','bartlett','flattop','parzen','bohman','blackmanharris','nuttall','barthann','cosine','exponential','tukey','taylor'), state='readonly')
	ctrl_win1b_win.current(3)
	ctrl_win1b_win.grid(row=irow, column=7, sticky='we')
	tk.Button(tbl_gen, text="CHOOSE", command=choose_win1b).grid(row=irow, column=8, padx=5, sticky='e')
	tk.Label(tbl_gen, text='Win 2 : input Freq. (0 and '+str(sr/2)+' must be included) and gain pairs (both are separated by ","), order , window').grid(columnspan=9, row=(irow:=irow+1), column=0, sticky='w')
	tk.Label(tbl_gen, anchor='w', text='Freq :').grid(row=(irow:=irow+1), column=0, sticky='we')
	ctrl_win2_freq=tk.Entry(tbl_gen, width=100)
	ctrl_win2_freq.grid(row=irow, column=1, columnspan=8, sticky='w')
	tk.Label(tbl_gen, anchor='w', text='Gain :').grid(row=(irow:=irow+1), column=0, sticky='we')
	ctrl_win2_gain=tk.Entry(tbl_gen, width=100)
	ctrl_win2_gain.grid(row=irow, column=1, columnspan=8, sticky='w')
	tk.Label(tbl_gen, anchor='e', text='Order :').grid(row=(irow:=irow+1), column=4, sticky='we')
	ctrl_win2_order=tk.Entry(tbl_gen, width=10)
	ctrl_win2_order.grid(row=irow, column=5, sticky='w')
	tk.Label(tbl_gen, anchor='e', text=' Window : ').grid(row=irow, column=6, sticky='we')
	ctrl_win2_win=ttk.Combobox(tbl_gen, justify='left', values=('boxcar','triang','blackman','hamming','hann','bartlett','flattop','parzen','bohman','blackmanharris','nuttall','barthann','cosine','exponential','tukey','taylor'), state='readonly')
	ctrl_win2_win.current(3)
	ctrl_win2_win.grid(row=irow, column=7, sticky='we')
	tk.Button(tbl_gen, text="CHOOSE", command=choose_win2).grid(row=irow, column=8, padx=5, sticky='e')
	tbl_coef=ttk.Frame(dlg, padding=(0,10,0,0))
	tbl_coef.pack(side='top', fill='x', ipadx=5, ipady=5)
	var_coef=tk.StringVar(value='')
	tk.Label(tbl_coef, anchor='w', text='Coef :').grid(row=0, column=0, sticky='we')
	tk.Entry(tbl_coef, width=100, textvariable=var_coef).grid(row=0, column=1, sticky='we')
	btn_ok=tk.Button(dlg, text="  OK  ", command=dofilter)
	btn_ok.pack(side='right', padx=5, anchor='n', pady=5)
	tk.Button(dlg, text="  Show Freq. Response  ", command=show_freq).pack(side='right', padx=(5,0), anchor='n', pady=5)
	tk.Button(dlg, text="CANCEL", command=lambda: pyplot.close('all') is dlg.destroy()).pack(side='right', anchor='n', pady=5)
	global data
	btn_ok['state']='normal' if data is not None and len(data)>0 else 'disabled'
	dlg.protocol("WM_DELETE_WINDOW", lambda: pyplot.close('all') is dlg.destroy())

if __name__=='__main__':
	win.mainloop()

aaa.png

引用
评论
加载评论中,请稍候...
200字以内,仅用于支线交流,主线讨论请采用回复功能。
折叠评论
m24h作者
2时28分前 修改于 2时9分前 IP:上海
930574

前两天不幸遇到NAS硬盘损坏事件  糟糕的是 虽然我做了RAID1 但是除了一个硬盘完全不可读 另一个硬盘也出现SMART警告

幸运的是我平时的文件存储比较讲究 文件不光有活动备份 还有归档  还有这两者的离线再备份 经过努力 可以说没有损失 今天新硬盘到了 只要重建RAID 应该就可以继续逍遥一段时间了

因为这次NAS的两个盘已经被认定均不可靠 而且为了尽量保证文件版本是最新的 所以这次修复 我用winmerge对所有数据及其归档/备份进行了完整的二进制对比 然后人工决定版本 在对比中发现 有3个文件是完全损坏 2个文件有少量字节差异(但是修改时间是一致的) 这就产生了文件本身的可靠性问题 这是纯粹通过备份没法解决的 当文件与备份不同时候 相信哪个?

md5sum可以用于解决类似问题  但是它不够灵活 每次都做完整检查的话 运行代价太大 更主要用在固定的文件上(比如发行的版本)  对于活动的文件 我希望能对文件正常的新增/删除/修改与损坏区分开来 而且我希望30天内检查过的文件 不必再进行完整的MD5计算 以减少运行维护的成本 所以我写了个脚本 (如果有再次修改 只更新github了 最好从github下载) 功能如下

----------------------------------------

Check files in a directory and its recursive subdirectories using MD5 digest algorithm. This script will use a file to record MD5 digest of all the files under the directory, and following rules is adopted.


If a new file is detected, a new record is generated, and is marked as "NEW".

If a file in records is deleted, the corresponding record is not deleted, but is marked as "DEL".

If modify time or size of a file was changed, the corresponding record is updated, and is marked as "UPD".

If the MD5 digest of a file is changed, the corresponding record is not upadated, but is marked as "BAD".

Records of other files are marked as "GUD".

If I/O error occurs when calculating MD5 digest, the corresponding MD5 digest is 32 times of "-"


Usage: XXXXXXXXXt [Options] <directory to be checked, which must be explicitly specified>

Example: XXXXXXXXXt d:\data

Options:

  -f <the record file contains MD5 digest> : defaultly a file named '._dirchk' in the directory is used  if not specified here

  -o <the output file contains MD5 digest> : defaultly using the same file as "-f" specified

  -d <days> : defaultly 30. Not-modified files that have just been checked in the these days are excluded from the MD5 re-calculation

  -B : do not backup records when output file name is same as original record file name

  -P : do not print progress information

  -x : delete records marked as 'DEL'

0<0# : ^
''' 
@python %~f0 %* 
@goto :eof
'''

import os
import sys
import getopt
import hashlib
import datetime
import time
import re

md5_err='-'*32
def md5(fname):
	m = hashlib.md5()
	try:
		with open(fname,'rb') as f :
			while d:=f.read(32768) :
				m.update(d)
		return m.hexdigest().upper()
	except Exception as e:
		print('{0} : {1}'.format(e, fname), file=sys.stderr)
		return md5_err

#record:(check_date, mark, modify_time, MD5, size)
pattern=re.compile(r'^(\d+)-(\d+)-(\d+)\s+(\w+)\s+(\d+)-(\d+)-(\d+):(\d+):(\d+):(\d+)\.(\d+)\s+(\d+)\s+([0-9A-Fa-f]{32})\s+(.+)$')
def record_parse(line):
	if m:=pattern.match(line) :
		return (m.group(14), \
		        (datetime.date(int(m.group(1)), int(m.group(2)), int(m.group(3))), \
						m.group(4), \
						datetime.datetime(int(m.group(5)), int(m.group(6)), int(m.group(7)),int(m.group(8)), int(m.group(9)), int(m.group(10)), int(m.group(11))), \
						int(m.group(12)), \
						m.group(13).upper() \
						) \
					)
	return (None, None)

format='{0:04d}-{1:02d}-{2:02d} {3:<3} {4:04d}-{5:02d}-{6:02d}:{7:02d}:{8:02d}:{9:02d}.{10:06d} {11:>16} {12:<32} {13}' 
def record_format(fname, rec):
	return format.format(rec[0].year, rec[0].month, rec[0].day, \
	                     rec[1], \
	                     rec[2].year, rec[2].month, rec[2].day, rec[2].hour, rec[2].minute, rec[2].second, rec[2].microsecond, \
	                     rec[3], \
	                     rec[4], \
	                     fname)
												  
if __name__ != '__main__':
	exit(0)

try:
	opts, args = getopt.getopt(sys.argv[1:], 'f:o:d:BPx')
except Exception as e:
	print(e, file=sys.stderr)
	exit(1)
	
if not args or len(args)!=1:
	print('''\
Check files in a directory and its recursive subdirectories using MD5 digest algorithm. \
This script will use a file to record MD5 digest of all the files under the directory, and following rules is adopted.

If a new file is detected, a new record is generated, and is marked as "NEW". 
If a file in records is deleted, the corresponding record is not deleted, but is marked as "DEL". 
If modify time or size of a file was changed, the corresponding record is updated, and is marked as "UPD". 
If the MD5 digest of a file is changed, the corresponding record is not upadated, but is marked as "BAD".
Records of other files are marked as "GUD".
If I/O error occurs when calculating MD5 digest, the corresponding MD5 digest is 32 times of "-"

Usage: python {0} [Options] <directory to be checked, which must be explicitly specified>
Example: python {0} d:\\data
Options:
  -f <the record file contains MD5 digest> : defaultly a file named '._dirchk' in the directory is used  if not specified here
  -o <the output file contains MD5 digest> : defaultly using the same file as "-f" specified
  -d <days> : defaultly 30. Not-modified files that have just been checked in the these days are excluded from the MD5 re-calculation
  -B : do not backup records when output file name is same as original record file name
  -P : do not print progress information
  -x : delete records marked as 'DEL'\
	'''.format(sys.argv[0]), file=sys.stderr)
	exit(1)

#check dir
directory=args[0].strip()
if len(directory)==0:
	directory='.'
elif len(directory)>1:
	directory=directory[0:1]+directory[1:].rstrip('\\').rstrip('/')
if not os.path.exists(directory) :
	print('Directory "{0}" does not exist'.format(directory), file=sys.stderr)
	exit(1)
elif not os.path.isdir(directory) :
	print('"{0}" is not a directory'.format(directory), file=sys.stderr)
	exit(1)

#get other parameters
rec_file=os.path.join(directory, '._dirchk')
out_file=None
days=30
backup=1
progress=1
clean=0
for n, v in opts:
	if n in ('-B',):
		backup=0
	elif n in ('-P',):
		progress=0
	elif n in ('-x',):
		clean=1
	elif n in ("-f",): 
		rec_file=v
	elif n in ("-o",): 
		out_file=v
	elif n in ('-d',):
		try:
			days=int(v)
		except:
			print('"{0}" is not a number for "-d"'.format(v), file=sys.stderr)
			exit(1)
if not out_file:
	out_file=rec_file

#check output file permission
out_dir=os.path.dirname(out_file)
if not out_dir:
	out_dir='.'
if not os.access(out_dir, os.W_OK):
	print('Output directory is not writable : {0}'.format(out_dir), file=sys.stderr)
	exit(1)

#check date
now=datetime.datetime.now()
today=now.date()
print('Check date: {0:04d}-{1:02d}-{2:02d}'.format(today.year, today.month, today.day), file=sys.stderr)

#read records from specifiled file
#format: check_date mark modify_time size md5_hex file_name
records=dict()
rec_file_exists=0
if os.path.exists(rec_file) :
	rec_file_exists=1
	if not os.path.isfile(rec_file) :
		print('"{0}" is not a file'.format(rec_file), file=sys.stderr)
		exit(1)
	num=0
	t1=time.time()
	try:
		print('Read record file: {0}'.format(rec_file), file=sys.stderr)		
		with open(rec_file, 'r', encoding='utf-8') as f:
			for line in f:
				if not (line:=line.strip()) :
					continue
				fname, rec=record_parse(line)
				if not fname or not rec:
					print('Bad record line {0} : {1}'.format(num+1, line), file=sys.stderr)
					exit(1)
				records[fname]=rec
				num=num+1
				if progress and (t2:=time.time())-t1>1:
					t1=t2
					sys.stderr.write('Records read : {0}/{1}\r'.format(len(records), num))
					sys.stderr.flush()
		print('Records read : {0}/{1}'.format(len(records), num), file=sys.stderr)		
	except Exception as e:
		print(e)
		exit(1)

#scan directory, get mtime (or ctime if failed to get mtime)
scanned=dict()
print('Scan directory : {0}'.format(directory), file=sys.stderr)
num=0
t1=time.time()
try:
	for root, dirs, files in os.walk(directory):
		for file in files:
			f=os.path.join(root, file)
			st=os.stat(f)
			t=st.st_mtime
			if t<0:
				t=st.st_ctime
			if t<0:
				t=0
			scanned[f]=(datetime.datetime.fromtimestamp(t), st.st_size)
			num=num+1
			if progress and (t2:=time.time())-t1>1:
				t1=t2
				sys.stderr.write('Files scanned : {0}/{1}\r'.format(len(scanned), num))
				sys.stderr.flush()
	print('Files scanned : {0}/{1}'.format(len(scanned), num), file=sys.stderr)
except Exception as e:
	print(e, file=sys.stderr)
	exit(1)

#backup
if backup and rec_file_exists and os.path.abspath(rec_file)==os.path.abspath(out_file):
	try:
		newname='{0}.{1:04d}{2:02d}{3:02d}.{4:02d}{5:02d}{6:02d}'.format(rec_file, now.year, now.month, now.day, now.hour, now.minute, now.second)
		os.rename(rec_file, newname)
		print('Rename old record file : {0}'.format(newname), file=sys.stderr)
	except Exception as e:
		print(e, file=sys.stderr)
		exit(1)

#prepare output file
try:
	if out_file=='-':
		out=sys.stdout
	else:
		out=open(out_file, 'w', encoding='utf-8')
except Exception as e:
	print(e, file=sys.stderr)
	exit(1)
		
#check files according to the rules
files=set(records.keys())
files.update(scanned.keys())
print('Checking records and files : {0}'.format(len(files)), file=sys.stderr)
num=0
t1=time.time()
for f in sorted(files):
	if f in records:
		rec=records[f]
		mark=rec[1]
		if f in scanned:
			sc=scanned[f]
			if rec[1]=='DEL':
				print(record_format(f, (today, 'NEW', sc[0], sc[1], md5(f))), file=out)
			elif rec[2]!=sc[0] or rec[3]!=sc[1]:
				print(record_format(f, (today, 'UPD', sc[0], sc[1], md5(f))), file=out)
			elif rec[4]==md5_err:
				if (m:=md5(f))!=md5_err:
					print(record_format(f, (today, rec[1], rec[2], rec[3], m)), file=out)
				else:
					print(record_format(f, rec), file=out)
			elif rec[1]=='BAD':
				if md5(f)==rec[4]:
					print(record_format(f, (today, 'GUD', rec[2], rec[3], rec[4])), file=out)
				else:
					print(record_format(f, rec), file=out)
			elif days==0 or (days>0 and (today-rec[0]).days>=days):
				if (m:=md5(f))!=md5_err:
					if m==rec[4]:
						print(record_format(f, (today, 'GUD', rec[2], rec[3], rec[4])), file=out)
					else:
						print(record_format(f, (today, 'BAD', rec[2], rec[3], rec[4])), file=out)
						print('File may be damaged : {0}'.format(f), file=sys.stderr)
				else:
					print(record_format(f, rec), file=out)
			else:
				print(record_format(f, rec), file=out)
		elif not clean:
			if rec[1]!='DEL':
				print(record_format(f, (today, 'DEL', rec[2], rec[3], rec[4])), file=out)
			else:
				print(record_format(f, rec), file=out)
	else:
		sc=scanned[f]
		print(record_format(f, (today, 'NEW', sc[0], sc[1], md5(f))), file=out)
	num=num+1
	if progress and (t2:=time.time())-t1>1:
		t1=t2
		sys.stderr.write('Records and files checked : {0}/{1}\r'.format(num,  len(files)))
		sys.stderr.flush()
print('Records and files checked : {0}/{1}'.format(num, len(files)), file=sys.stderr)
	
#end output
if out_file!='-':
	out.close()


引用
评论
加载评论中,请稍候...
200字以内,仅用于支线交流,主线讨论请采用回复功能。
折叠评论

想参与大家的讨论?现在就 登录 或者 注册

所属专业
所属分类
上级专业
同级专业
m24h
进士 学者 机友
文章
44
回复
772
学术分
1
2020/01/22注册,14分35秒前活动

无聊地过着没有意义的日子 偶尔期待一点意思

主体类型:个人
所属领域:无
认证方式:手机号
IP归属地:上海
文件下载
加载中...
{{errorInfo}}
{{downloadWarning}}
你在 {{downloadTime}} 下载过当前文件。
文件名称:{{resource.defaultFile.name}}
下载次数:{{resource.hits}}
上传用户:{{uploader.username}}
所需积分:{{costScores}},{{holdScores}}下载当前附件免费{{description}}
积分不足,去充值
文件已丢失

当前账号的附件下载数量限制如下:
时段 个数
{{f.startingTime}}点 - {{f.endTime}}点 {{f.fileCount}}
视频暂不能访问,请登录试试
仅供内部学术交流或培训使用,请先保存到本地。本内容不代表科创观点,未经原作者同意,请勿转载。
音频暂不能访问,请登录试试
支持的图片格式:jpg, jpeg, png
插入公式
评论控制
加载中...
文号:{{pid}}
投诉或举报
加载中...
{{tip}}
请选择违规类型:
{{reason.type}}

空空如也

加载中...
详情
详情
推送到专栏从专栏移除
设为匿名取消匿名
查看作者
回复
只看作者
加入收藏取消收藏
收藏
取消收藏
折叠回复
置顶取消置顶
评学术分
鼓励
设为精选取消精选
管理提醒
编辑
通过审核
评论控制
退修或删除
历史版本
违规记录
投诉或举报
加入黑名单移除黑名单
查看IP
{{format('YYYY/MM/DD HH:mm:ss', toc)}}