一些 Shell 脚本(持续更新)

时间:2023-03-09 03:56:22
一些 Shell 脚本(持续更新)

1. 启动日志分析

  • 启动日志格式如下:

    开机时间:2015/05/13 周三 16:45:17.79

    关机时间:2015/05/13 周三 18:46:03.91

    开机时间:2015/05/14 周四 17:07:11.80

    开机时间:2015/05/14 周四 17:09:34.68

    关机时间:2015/05/14 周四 18:42:35.40

  • 转换为导入 excel 的格式:

    2015/05/13 16:45:17.79 18:46:03.91

    2015/05/14 17:07:11.80

    2015/05/14 17:09:34.68 18:42:35.40

显然有些项缺失,可能导致错误配对,但假定只有关机项会缺失吧

代码

  1. 预处理文本

    先把“时间:”替换为空格

    补充:若能进一步把 \n关机时间: 替换为空,则下一步处理会更简单!

  2. awk 处理

    Markdown 中可以用全角空格或html转义符来保持代码缩进

    半方大的空白 或 

    全方大的空白 或 

    不断行的空白格 或 


{
   if ($1 == "开机") {
     if (ss == 1) {
      print ""
     }
      printf $2" "$4
      ss = 1 # 标识上次是否是开机
   } else {
       if (ss == 1) {
        print " "$4
        ss = 0
       } else {
        print $2" "$4
       }
   }
}

运行

Windows 下可以使用 gawk_win32 来处理

2. 远程通知

项目在服务器上需要长时间编译,如何在编译完成后提醒自己?

环境:一台自己用的机器,一台服务器。

代码

服务器: echo 'Done!' | nc 127.0.0.1 9000

机器:notify-send $(nc -l 9000)

3. 并行处理文件

有N个1-2 G大的文件,其中第六列是点分表示的IPV4地址,需要把它转为整数表示。


#!/bin/awk -f
{
  len = split($6, a, ".")
  ip = 0
  if (len == 4) {
    ip = lshift(a[1], 24) + lshift(a[2], 16) + lshift(a[3], 8) + a[4]
    printf $1"\t"$2"\t"$3"\t"$4"\t"$5"\t"ip"\n" > o
  }
}

CPU是6核的,就起了6个进程去转,每个awk进程基本都是CPU 100了

但转换一个文件还是需要15分钟左右,还是太慢

求教有什么方法可以让AWK跑的更快些吗?

4. 移除文件名里的特殊字符

中了病毒,所有xls文档后缀被改为scr,且有个不知道什么符号把字符串左右反转了

分析

  • 我用的 zsh,补全文件名时可以看见其中有个特殊字符,显示为:6-7生產產值(2)<202e>slx.scr。
  • 查了下 202e,发现是个特殊的 unicode 字符,用来左右反向文字。

    这样病毒的伎俩也很清楚了,文件实际后缀是 slx.scr(可执行),但显示出来是 rcs.xls!不过狐狸尾巴没完全藏住。
  • 如何移除这个特殊字符?确实费劲,毕竟 shell 处理这种不可见特殊字符很不给力

    首先想到 rename,支持 perl 正则,还挺强大的。那么删掉这个 unicode 字符就行了

    rename "s/\u202e//g" —— 移除失败

    detox -s utf_8 -v -n —— 失败,中文也被移除

    rename "s/[\x2e\x20]//g"

    rename "s/[\x20][\x2e]//g"

    rename -e "s/[[1]]//g" —— 中文被移除

    rename "s/[\e2\x80\xae]//g" —— 乱码了!

    好了,放弃精确匹配,改用模糊匹配

    rename "s/.{3}slx.scr/.xls/g" *.scr —— 成功!
  • 试了下 bash,直接跪了,看不见,但直接 mv 是有效的:mv 6-7生產產值(2)slx.scr 6-7生產產值(2).xls

    不知道有什么小工具能直接查看(hex)并处理文件名特殊字符的?不过可以用 vim 打开文件夹,看到文件名的特殊字符

5. 临时文件夹清理脚本

  1. 每一小时检查一次,如果文件夹大于3G,则开始清理文件夹里的文件,无子文件夹
  2. 清理的时候,按时间顺序清理文件。
  3. 如果文件有人使用,则不能使用rm暴力删除,使用cat /dev/null>文件名
  4. 文件夹小于500M时停止清理。

想法

  • 文件夹大小查看可用 du 命令;按时间顺序,可以用 ls 的 sort 选项;定时,cron
  • 不过要检测文件是否有人使用,用 lsof 或 fuser 好像不太灵光,要 root?
  • 而且使用检测导致批量删除文件不太方便,还是一个个来吧。

DIR="/tmp/"
dir_size=$(du -ms "$DIR" | cut -f1)
max_size=3000 #MB
min_size=500
[ "$dir_size" -lt "$max_size" ] && exit 0
del_size=$(((dir_size - min_size)*1024)) #KB,将清理的文件大小
pushd "$DIR" || exit 1
ls -tl | awk -v ds="$del_size" '{
 if (NF > 8) {
  sz += $5; nm = $9; # 获得文件大小(B)和文件名
  system("lsof "nm" && cat /dev/null>"nm" || rm -f "nm);
  #~ "lsof "nm" 2>/dev/null" | getline used; print "used:"used
  if (rshift(sz,10) >= ds) exit; # 策略有些激进,先删除再判断
 };
}'

6. 文件夹监控压缩脚本

  1. 环境:win10
  2. 要求:一个文件夹里不定时的会放入不少word、PPT文档。

    怎样实现即时放入一个文档后,自动使用Winrar生成一个单独的压缩包?手动实在麻烦

想法

  • 用 IFTTT 之类的软件就可以,比如 https://www.nodesoft.com/foldermonitor
  • 如果想要轻量级的方案,自然脚本是首选,但批处理循环检测的做法还是太 low
  • 下面请出 powershell,从 win7 就开始内置,win10 更不用说。基于 .net framewrok,功能强大的系统管理工具。

$folder = 'C:\Users\Administrator\Documents' #监控文件夹
$zipPath = $env:ProgramFiles + '\7-Zip\' #压缩程序路径
$env:Path = $env:Path + ';' + $zipPath
Set-Location -Path "$folder"
$watcher = New-Object System.IO.FileSystemWatcher
$watcher.Path = $folder
$watcher.IncludeSubdirectories = $false
$watcher.EnableRaisingEvents = $true
$created = Register-ObjectEvent $watcher "Created" -SourceIdentifier "FileWatcher" -Action {
 $name = $Event.SourceEventArgs.Name
 $type = $Event.SourceEventArgs.ChangeType
 if ($name -notmatch ".*\`.(rar|zip|7z)$") {
  Write-Host "$name"
  7z.exe a "$name.7z" "$name"
 }
}
# 停止监控可手动运行如下命令:
# Unregister-Event FileWatcher
  • 脚本使用了 .net 对象及其事件机制,不愧是面向对象的 shell
  • 运行的话有点麻烦,ms 为了安全,默认不允许双击运行。可参考“快速运行 .ps1 脚本的 N 种方法”。

    我采用第一种,配一个同名的 .cmd 批处理文件来启动,内容如下:

    @set Path=%Path%;%SystemRoot%\system32\WindowsPowerShell\v1.0\ & powershell -ExecutionPolicy RemoteSigned -NoExit -NoProfile %~dpn0.ps1

7. 批量替换词典数据

  1. 共两个文件:词典和替换表
  2. 替换表包含多行,每行内容为:查找内容 替换成的内容。要求全字匹配把词典替换完

代码


while read s ts; do
 sed -i "s/${s}/${ts}/g" "$1"
done

8. Win 注销后自动休眠

问题:

当前用户注销后,用户进程会全部退出,后继无法再操作。这里使用 system 用户执行计划来达到注销后的操作,所以必须用管理员权限执行脚本

计划延时 2 分钟后执行,给注销留点时间。这里直接加了分钟,有 bug

正确做法应先把总时间转换成秒,再累加较好

批处理代码


@echo off & setlocal enabledelayedexpansion set /a mt=(%time:~3,2% + 2) %% 60
if %mt% lss 10 (set mt = 0!mt!)
set st=%time:~,2%:!mt! SCHTASKS /Create /F /SC ONCE /RU SYSTEM /TN "HibernateHelper" /TR "shutdown /h" /ST %st%

9. 文件名处理

有多个文件如:[牛人]Dear Annie_高清_纯音频文件_纯音频输出.mp3

       [牛人]My Friend_高清_纯音频文件_纯音频输出.mp3

怎么写bat,能把有重复部分的文件批量改为:

        [牛人]Dear Friend.mp3

       [牛人]My Destiny.mp3

想法

  • 经确认,需删除的相同字段只会在首部、中部或尾部
  • 本质上是求字符串的重复部分(2 个字符以上);但会有明显特征,不算随机字符串
  • 有用部分也可能重复,故有误杀可能
  • 这里只处理了尾部重复,这种情况应该比其它情况多

\#Requires -Version 2.0
 
$chars = @{} # 特征字符串
$append = @{} # 待定文件名
 
$ext = "mp3"
$files = dir *.$ext | % { $_.basename }
 
:DONE for ($i = 0; $i -lt $files.Count; $i++) {
  $a = $files[$i]
foreach($str in $chars.Keys) {
  if ($chars[$str] -lt $a.Length) {
  $idx = $a.IndexOf($str)
  if ($idx -gt 0) {
   $new = $a.Substring(0, $idx)
   rename-item "$a.$ext" "$new.$ext"
   continue DONE
  }
  }
} $s = $a
foreach($b in $append.Keys) {
  if ($s.Length > $b.Length) {
    $s, $b = $b, $s
  }
  for ($j = 0; $j -lt ($s.Length - 1); $j++) {
   $subStr = $s.Substring($j)
    $idx = $b.IndexOf($subStr)
    if ($idx -gt 0) {
    $newA = $s.Substring(0, $j)
    $newB = $b.Substring(0, $idx)
   echo "2.1: mv $s.$ext $newA.$ext"
   echo "2.2: mv $b.$ext $newB.$ext"
   $chars["$subStr"] = $subStr.Length
    $append.Remove($b)
    continue DONE
    }
   }
  }
 $append[$a] = 1
}

10. Execute remote script with passing to it some arguments


curl -s http://server/path/script.sh | bash /dev/stdin arg1 arg2

11. 递归批量修改任意文件后缀


find "$path" -type f -execdir rename 's/.[\w]+$/.htm/' '{}' +

12. 文件按序命名

有文件:2334.jpg ds23l.jpg 壁纸01.jpg 乱七八糟文件名.jpg

请问怎么把这些jpg文件批量重命名成如下:

1.jpg 2.jpg ... 100.jpg

或是:

001.jpg 002.jpg ... 100.jpg


ls -A1 | nl -w3 -n rz | while read num name; do mv $name $num.jpg; done

13. 词典文件按行去重

有英文词库文件,内容类似:

zymotic zymotic

zymotic zymotic 17700

zz zz

Zz Zz

zz zz 3780000

怎么把前两列相同的去重:

zymotic zymotic 17700

zz zz

Zz Zz


awk '!array[$1]++'

  1. :print: ↩︎