分享脚本工具
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


[修改于 8个月28天前 - 2024/03/28 14:31:54]

来自:计算机科学 / 软件综合
3
已屏蔽 原因:{{ notice.reason }}已屏蔽
{{notice.noticeContent}}
~~空空如也
m24h 作者
3年0个月前 修改于 8个月28天前 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作者
1年2个月前 修改于 1年2个月前 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作者
8个月28天前 修改于 8个月19天前 IP:上海
930574

这里代码有bug 新版本已上传GitHub 等我回家也上传这里(忘了)....最新-> 已经改好了 因为是重大bug才改这里 这个bug发现比较有趣 是因为win10抓住了我文件中一个潜藏了10年的病毒 发生了读取失败造成 一开始测试时候没有全面测试读取失败的例子

此外 在实施过程中发现 有两个文件 在两次检查之间 我并没有对其做任何操作 实质上没有发生变化 但是"修改时间"丢失 不知道何时被改成了"创建时间" 我代码中完全没有这方面的逻辑 也不会写待检查的文件 感觉应该与我无关 但除了我的脚本似乎也没有其他原因(这两个新的修改时间相差0.05秒 也许发生在我第一次运行脚本的时候 但是我检查逻辑 实在是只读打开)...... 这两个文件都是是.eml 但是其他eml又没事 不知道是系统干了什么 或者后台发生了什么 或者是python读取时候发生了什么 如果没有检查 我也不会发现这种情况...我找不到原因 如果你们用我这个脚本 应该注意一下这个情况发生的可能性(今天又发现两个.eml文件修改日期发生改变 但是并未运行本脚本 显然与本脚本无关 发生在文件拷贝过程 而且源和目的文件修改日期都不一样)

chg.png  

2024.4.6 找到原因了 .eml文件修改日期变化的原因是windows的索引和搜索功能导致 见XXXXXXXXXXXXXXXXXXXXXXXX/t/90054

--------

前两天不幸遇到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: python C:\SHELL\Tool\XXXXXXXXXt [Options] <directory to be checked, which must be explicitly specified>

Example: python C:\SHELL\Tool\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> : if specified, Not-modified files that have just been checked in the these days are excluded from the MD5 re-calculation

  -n <bytes> : if specified, MD5 re-calculation are no longer performed when the total bytes of MD5 calculation reaches this value (-d 和 -n 合理设置和配合 可以逐渐将文件的检查任务分散开来 不至于一次同时一堆文件需要检查 加入定期任务时候 不至于一连好多天没运算量 一天又运算量来不及完成)

  -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
md5_total_bytes=0
def md5(fname):
	global md5_total_bytes
	m = hashlib.md5()
	try:
		with open(fname,'rb') as f :
			while d:=f.read(32768) :
				m.update(d)
				md5_total_bytes=md5_total_bytes+len(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, size, MD5)
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:n: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> : if specified, Not-modified files that have just been checked in the these days are excluded from the MD5 re-calculation
  -n <bytes> : if specified, MD5 re-calculation are no longer performed when the total bytes of MD5 calculation reaches this value
  -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=0
bytes_limit=-1
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)
	elif n in ('-n',):
		try:
			bytes_limit=int(v)
		except:
			print('"{0}" is not a number for "-n"'.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
bytes_done=0
bytes_recalc=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 (bytes_limit<0 or md5_total_bytes<bytes_limit) and (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()
引用
评论(2)
2
加载评论中,请稍候...
200字以内,仅用于支线交流,主线讨论请采用回复功能。
折叠评论

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

所属专业
所属分类
上级专业
同级专业
m24h
进士 学者 机友
文章
55
回复
904
学术分
1
2020/01/22注册,1时32分前活动

个人开源项目: XXXXXXXXXXXXXX

主体类型:个人
所属领域:无
认证方式:手机号
IP归属地:上海
插入公式
评论控制
加载中...
文号:{{pid}}
投诉或举报
加载中...
{{tip}}
请选择违规类型:
{{reason.type}}

空空如也

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