サーバの大掃除のついでに、ユーザ権限でCPANライブラリを導入していたものを一掃したい。そのために、と、Stat::lsMode::format_modeをライブラリを使わずインライン実装する簡易関数をAIに書いてもらった。
Config::TinyのINIファイル読み書きを代替する関数
#!/usr/bin/perl use Data::Dumper; $ini_filename = "test.ini"; # ******************* # INI読み込み my $config = read_ini($ini_filename); if ( !defined($config) ) { print("ini file not found\n"); exit(0); } my $str_temp = $config->{debug_section}->{debug_attr}; if ( !defined($str_temp) ) { printf("value not found\n"); } else { printf( "[section] attr = %s\n", $str_temp ); } print("---debug---\n"); print Dumper $config; print("---debug---\n"); # ******************* # INI書き込み $time = time(); ( $sec, $min, $hour, $mday, $mon, $year, $wday ) = localtime($time); $new_value = sprintf( "%04d-%02d-%02d %02d:%02d:%02d", $year + 1900, $mon + 1, $mday, $hour, $min, $sec ); # 書き込み my $config = {}; $config->{debug_section0}->{debug_attr0} = "dummy string 0"; $config->{debug_section}->{debug_attr} = $new_value; $config->{debug_section}->{debug_attr2} = "dummy string 2"; $config->{debug_section2}->{debug_attr3} = "dummy string 3"; write_ini( $ini_filename, $config ); printf( "new value (%s) written\n", $new_value ); # ************* # INIファイルに書き込む (Config::Tiny 互換) # param[0]: string ファイル名 # return: hash 全ての値を格納したハッシュ{セクション名}{値名} sub read_ini { my ($filename) = @_; open my $fh, '<', $filename or return undef; my %config; my $section = ''; while ( my $line = <$fh> ) { chomp $line; $line =~ s/^\s+|\s+$//g; # 前後の空白除去 next if $line eq '' or $line =~ /^;/; # 空行・コメント除外 if ( $line =~ /^\[(.+?)\]$/ ) { $section = $1; $config{$section} ||= {}; } elsif ( $line =~ /^([^=]+?)\s*=\s*(.*)$/ ) { my ( $key, $value ) = ( $1, $2 ); $config{$section}{$key} = $value; } } close $fh; return \%config; } # ************* # INIファイルに書き込む (Config::Tiny 互換) # param[0]: string ファイル名 # param[1]: hash 全ての値を格納したハッシュ{セクション名}{値名} sub write_ini { my ( $filename, $config_ref ) = @_; open my $fh, '>', $filename or return 0; foreach my $section ( sort keys %$config_ref ) { print $fh "[$section]\n"; foreach my $key ( sort keys %{ $config_ref->{$section} } ) { my $value = $config_ref->{$section}{$key}; print $fh "$key=$value\n"; } print $fh "\n"; } close $fh; return 1; }
Stat::lsMode::format_modeを代替する関数
#!/usr/bin/perl use File::Basename; my $filepath; if ( @ARGV == 1 ) { $filepath = $ARGV[0]; } else { my $scriptname = basename( $0, '' ); print("usage : $scriptname [target filepath]\n"); exit(1); } print("target path = $filepath \n"); my @stats = stat($filepath); my $attr_int = $stats[2]; # $attr_int = 0100755; # -rwxr-xr-x # debug my $attr_formatted = format_mode($attr_int); printf( "attr : %s (%X, %o)\n", $attr_formatted, $attr_int, $attr_int ); # ************* # stat の第3要素(モード情報)を人間が読める形式(例: -rw-r--r--)に変換する関数 (Stat::lsMode::format_mode と互換の関数) sub format_mode { my ($mode) = @_; # ファイルタイプ my %file_types = ( 0xC000 => 's', # socket 0xA000 => 'l', # symbolic link 0x8000 => '-', # regular file 0x6000 => 'b', # block device 0x4000 => 'd', # directory 0x2000 => 'c', # character device 0x1000 => 'p', # FIFO ); my $type_char = '?'; foreach my $type ( keys %file_types ) { if ( ( $mode & 0xF000 ) == $type ) { $type_char = $file_types{$type}; last; } } # パーミッション my @rwx = qw(r w x); my $perm_str = ''; for my $i ( 0 .. 2 ) { my $shift = 6 - $i * 3; my $bits = ( $mode >> $shift ) & 0x7; for my $j ( 0 .. 2 ) { $perm_str .= ( $bits & ( 1 << ( 2 - $j ) ) ) ? $rwx[$j] : '-'; } } # setuid, setgid, sticky bit の処理 substr( $perm_str, 2, 1 ) = ( $mode & 0x800 ) ? ( ( $perm_str =~ /^..x/ ) ? 's' : 'S' ) : substr( $perm_str, 2, 1 ); substr( $perm_str, 5, 1 ) = ( $mode & 0x400 ) ? ( ( $perm_str =~ /^.....x/ ) ? 's' : 'S' ) : substr( $perm_str, 5, 1 ); substr( $perm_str, 8, 1 ) = ( $mode & 0x200 ) ? ( ( $perm_str =~ /^........x/ ) ? 't' : 'T' ) : substr( $perm_str, 8, 1 ); return $type_char . $perm_str; }
Copilot AIがsetuid, setgid, sticky bitを検出するビット計算を間違っていたので、そこだけ修正したのが上のソースコード