分类目录归档:shell

tar

一、采用脚本自动压缩和解压

本文来源:https://www.cnblogs.com/xiongzaiqiren/p/12658210.html

 bat脚本(批处理程序)调用WinRAR及7zip压缩和解压缩
最近项目要用到定期批量将文件及文件夹下的文档打包,所以想到要写脚本来实现,然后做Windows定期任务调用。bat脚本(批处理程序)调用WinRAR及7zip压缩和解压缩。

1、调用WinRAR来实现

A、bat调用WinRAR解压实例一

因为WinRAR是Windows系统经典的压缩及解压缩程序,也是目前为止使用非常普遍。
它不是Windows系统自带的,所以要调用它之前得确认是否安装了WinRAR,
如果没有安装则自行下载安装即可。WinRAR下载地址:http://www.winrar.com.cn/
  一般来说,64位安装在C:\Program Files\目录下,32位安装
  在C:\Program Files (x86)\目录下。这个知识点很关键,下面要用到.
bat调用WinRAR解压实例一:
::解压程序WinRAR.exe所在目录 32位%ProgramFiles(x86)% 或64位 %ProgramFiles%
set WinRarDir=%ProgramFiles%\WinRAR 
:: x即解压。-y是说如果遇到提示说是否覆盖,选择yes
start  /wait  ""  "%WinRarDir%\WinRAR.exe"  x  -l  D:\Software\Redis-x64-3.2.100.zip  C:\mySoftWare\

B、bat调用WinRAR解压实例二

上面实例是调用解压的核心命令,使用到项目中还需要结合上下文完善,
比如判断winrar程序是否存在,目标压缩包zip是否存在等。

@echo off
setlocal enabledelayedexpansion
echo please make sure you are admin!
echo 解压并部署redis,notepad及相关软件,请确保相关压缩包在当前目录。
echo 执行此脚本前,请确认已经安装解压缩程序WinRAR.exe
:::::::::: 配置参数 ::::::::::
::解压程序WinRAR.exe所在目录 32位%ProgramFiles(x86)% 或64位 %ProgramFiles%
set WinRarDir=%ProgramFiles%\WinRAR 
REM 基础文件
set redis=Redis-x64-3.2.100.zip
set notepad=npp.7.5.6.Installer.exe
REM 部署盘符
set Pan=C:\
:::::::::: 执行脚本前,请配置以上参数。::::::::::
echo ------------------------------
set curdir=%~dp0
cd /d  %curdir%
echo 开始准备部署资源:
::检查基础文件
if exist %curdir%%redis% (echo 存在:%redis%) else (echo %redis%文件不存在 & pause&exit)
if exist %curdir%%notepad% (echo 存在:%notepad%) else (echo %notepad%文件不存在 & pause&exit) 
echo ------------------------------
::创建部署目录
set deploydir=%Pan%mySoftWare
if exist %Pan% ( ^ if exist %deploydir% (echo 存在:%deploydir%) else (mkdir %deploydir% & echo 已创建:%deploydir%)
) else ( ^echo !! echo !!执行失败,当前系统不存在%Pan%盘 echo !! pause & exit)
echo ------------------------------

::解压并放到部署目录 32位%ProgramFiles(x86)% 或64位 %ProgramFiles%
if exist "%WinRarDir%\WinRAR.exe" (echo 已找到:"%WinRarDir%\WinRAR.exe") else (echo 找不到:"%WinRarDir%\WinRAR.exe"请检查。 & pause&exit)
echo 开始解压部署资源:
::替换掉.zip即移除后缀名形成解压到文件夹名
set "redisdir=%redis:.zip=%"
:: x即解压。-y是说如果遇到提示说是否覆盖,选择yes
start /wait "" "%WinRarDir%\WinRAR.exe" x -l %curdir%%redis% %deploydir%\%redisdir%\
copy "%curdir%script\Redis*" "%deploydir%\%redisdir%\" 
echo 部署资源已准备好,开始安装辅助软件:
::运行npp.7.5.6.Installer.exe(安装notepad++)
start /wait "" "%curdir%%notepad%" /S
echo 已安装%notepad% 
echo 部署资源已准备好,你可以开始部署了(请注意修改redis,preview等相关应用配置文件)。
echo ------------------------------
echo 检查服务是否存在:
::检查服务是否存在
set redisServerName=Redis
sc query "%redisServerName%" >nul && (echo 服务已存在:%redisServerName%) || (echo 请安装部署:%redisServerName%)
echo 检查服务已完成。 
echo ------------------------------
echo 部署资源已准备好,请开始部署吧。
::部署完成,打开windows服务管理器
start "" "services.msc" -refresh
pause

2、调用 7zip 来实现

A、bat调用7zip压缩实例

7zip也是目前为止使用非常普遍的一种压缩程序,它的压缩、解压缩效率高,
而且没有广告,是免费工具。它不是Windows系统自带的,所以要调用它之前得确认是否安装了7zip,
如果没有安装则自行下载安装即可。7zip下载地址:https://sparanoid.com/lab/7z/

@echo 定位到待压缩的文件夹
cd F:\数据压缩测试\0101C  
@echo 使用7z先将1这个目录下的所有文件压缩为tar格式 
"C:\Program Files\7-Zip\7z.exe" a -ttar F:\数据压缩测试\510101C.tar 1\*
@echo 将压缩后的tar格式再压缩为gzip格式,最终得到需要的tar.gz格式
"C:\Program Files\7-Zip\7z.exe" a -tgzip F:\数据压缩测试\2016_1_510101C.tar.gz F:\数据压缩测试\510101C.tar
@删除中间过程生成的文件
del "F:\数据压缩测试\510101C.tar"

B、bat调用7zip解压实例一

@echo off
setlocal enabledelayedexpansion
echo please make sure you are admin!
echo 执行此脚本前,请确认已经安装解压缩程序7-Zip
:::::::::: 配置参数 ::::::::::
::解压程序WinRAR.exe所在目录 32位%ProgramFiles(x86)% 或64位 %ProgramFiles%
set 7ZipDir=%ProgramFiles%\7-Zip
REM 当前目录下用来测试的压缩包
set plegodemo=plegodemo.zip
:: 解压
set curdir=%~dp0
set deploydir=E:\mytest
set "plegodemodir=!plegodemo:.zip=!"
:: C:\01_MyApp\7-Zip\7z.exe x newPack.zip -oc:\Doc -aoa
start /wait "" "!7ZipDir!\7z.exe" x !curdir!!plegodemo! -o!deploydir!\!plegodemodir!\ -aoa
@rem 参考此命令,自己写了一个成功的用例,解压结果符合预期,十分完美:
"c:\path\7-zip\7z.exe" x d:\path\xx.zip -o"d:\xxx\path\\" -aoa
@rem 根据实践结果,此命令对 .7z 同样适用

:: 压缩
set "name=%date:~0,4%%date:~5,2%%date:~8,2%%time:~0,2%%time:~3,2%%time:~6,2%"
:: C:\01_MyApp\7-Zip\7z.exe a -t7z newPack.7z "F:\14_newWork\7z\testDoc\*" -r -mx=9 -m0=LZMA2 -ms=10m -mf=on -mhc=on -mmt=on

start /wait "" "!7ZipDir!\7z.exe" a -tzip !deploydir!\!plegodemodir!_%name%.zip "C:\inetpub\*" -r -mx=9

3、用7z来解压.tar.gz

zip软件可以实现文件的压缩和解压缩,
如果使用7z.exe对tar.gz文件进行解压缩,
需要经过两次解压,第一次解压成tar文件,
再解压一次生成源文件,
例如test.tar.gz解压一次生成test.tar。
再次解压生成test

测试editor.md的公式功能

$$ x={-b\pm \sqrt{b^2 - 4ac}\over 2a}$$

$$ x\href{why-equal.html}{=} y^2 + 1 $$

what does the red 'href' mean?

sed

特殊字符反斜杠的问题

#sed -i '4 s/^/good baby\n/' test.txt 
执行成功,符合预期,但是
#sed -i '4 s/^/<center>查询结果:6个  <a href="./">继续</a>/' test.txt
却运行失败,经多次尝试排查,终于发现原因。
因为正则表达式中有'/'这个特殊字符,应该进行转义。
改成'\/'即可,但是实际在C代码中,采用system(cmd)的方式,并未成功

由于时间紧张,未能深入排查。
但是在排查的时候,意外发现把's'换成‘a’,即进行追加而不是替换的话,可以完美工作。
不需要考虑特殊字符的转义问题,在'a'后面,反斜杠'/'成了普通字符。

#sed -i '8 a<center>查询结果:6个  <a href="./">继续</a>' test.txt

一个常用技巧,查询数据类型在哪个头文件中定义
grep -rn keyWords ../../ | sed -n "/\.h:\w\+/"p

find ../ -name '*.c' | xargs grep -rni keyWords
--->>输出中带有路径

find ../ -type f | grep '\.c' | xargs grep -rni keyWords
-->>这种情况下,输出带有路径,行数,内容

find ../ -name '*.c' | xargs -i grep -rni keyWords {}
==>>这种情况下,输出结果中,没有文件名

grep 执行条目过多时,会报错
# grep -rni keyWords `find ../../ -name *.c`

找到关键字所在的行,将其替换为别的内容
如:void function(int a, int b) 修改为:
static void function(int a, int b)

fileName=/home/user/a.c
lineNum=`sed -n '/void function/=' a.c` ; \
echo ${lineNum}; \
sed -i "${lineNum}d" a.c; \
sed -i "${lineNum}iStatic void function(int a, int b)" a.c

说明: sed -i "${lineNum}d" 删除指定行
      sed -i "${lineNum}iXXX" 在指定行插入XXX
      sed -n "/key/=" 查找关键字所在行,如果是多个,
则返回 3 5 7 这样的字符串,可以用数组接收

找到关键字,并在其后追加一行
第一步,制作准备原始文件
# cat pps
aaaa
bbbb
cccc
dddd

第二步,命令行实验,尝试先查找"cccc"所在的行,并在其后追加"zzzz"
# sed -e "/cccc azzzz"
aaaa
bbbb
cccc
zzzz
dddd

第三步,因为第二步实验成功,将其写入文件
# sed -i "/cccc azzzz" pps
# cat pps
aaaa
bbbb
cccc
zzzz
dddd

一个高难度应用
1、问题描述
源文本如下:
<test> blah blah   blah blah blah   widget   blah blah blah
</test>
<formula>   blah   <details>      widget   </details>
</formula>

希望从文本中,提取出如下内容:
<test>widget</test> 
<formula>widget</formula>

2、实现脚本
sed -ne '/[Ww][Ii][Dd][Gg][Ee][Tt]/,/^<\// {//p}' file.txt | awk 'NR%2==1 { sub(/^[ \t]+/, ""); search = $0 }

效果:
<test>widget</test>
<formula>widget</formula>

3、脚本讲解
## The sed pipe: 
sed -ne '/[Ww][Ii][Dd][Gg][Ee][Tt]/,/^<\// {//p}'
## This finds the widget pattern, ignoring case, then finds the last, 
## highest level markup tag (these must match the start of the line)
## Ultimately, this prints two lines for each pattern match 
## Now the awk pipe: 
NR%2==1 { sub(/^[ \t]+/, ""); search = $0 }
## This takes the first line (the widget pattern) and removes leading
## whitespace, saving the pattern in 'search' 
NR%2==0 { end = $0; sub(/^<\//, "<"); printf "%s%s%s\n", $0, search, end }
## This finds the next line (which is even), and stores the markup tag in 'end'
## We then remove the slash from this tag and print it, the widget pattern, and
## the saved markup tag

对包含特殊关键字的行进行操作
1、在包含关键字的行中,删除另外一个关键字
# cat ass.txt
good boy
good man
good girl
# 将包含 man 的行中,对应的关键字 good 删除
# sed -i /man/{s/good//} ass.txt
# cat ass.txt
good boy
 man
good girl

2、将同时包含关键字A和关键字B的行删除
# cat ass.txt
good boy
good man
good girl
# 将包含 man 的行中,对应的关键字 good 删除
# sed -i /man/{/good/d} ass.txt
# cat ass.txt
good boy
good girl

sed 的一个大 bug
1、诡异现象
写入文件,出现故障。
一段脚本之中,前面写入表现都很正常。
但是执行到某一段代码时,出现莫名其妙的故障,代码如下:

#==>>这是前面一段代码,表现正常,能成功写入,符合预期
for fileName in ${file_1} ${file_2}
do
    lineNum=`sed -n /keyword_1/= ${fileName}`
    startLine=$((lineNum - 5))
    endLine=$((lineNum + 2))
    # 删除这一段内容
    sed -i "${startLine},${endLine} d"
    # 下面用 sed 重新插入代码
    sed -i "${startLine}a #ifndef OK" ${fileName} ; startLine=$((${startLine} + 1))
    sleep 0.01 # 为了防止写文件太耗时,加一个延时10ms
    sed -i "${startLine}a #define OK 0" ${fileName} ; startLine=$((${startLine} + 1))
    sleep 0.01 # 为了防止写文件太耗时,加一个延时10ms
    sed -i "${startLine}a #define ERRPR 1" ${fileName} ; startLine=$((${startLine} + 1))
    sleep 0.01 # 为了防止写文件太耗时,加一个延时10ms
    ...
    sed -i "${startLine}a #endif" ${fileName} ; startLine=$((${startLine} + 1))
done

#==>>第二段代码,与第一段雷同,只是行数略多,20行而已,但是后来发现跟行数无关
for fileName in ${file_3} ${file_4}
do
    lineNum=`sed -n /keyword_2/= ${fileName}`
    startLine=$((lineNum - 19))
    endLine=$((lineNum + 2))
    # 删除这一段内容
    sed -i "${startLine},${endLine} d"
    # 下面用 sed 重新插入代码
    sed -i "${startLine}a #ifndef OK" ${fileName} ; startLine=$((${startLine} + 1))
    sleep 0.01 # 为了防止写文件太耗时,加一个延时10ms
    sed -i "${startLine}a #define OK 0" ${fileName} ; startLine=$((${startLine} + 1))
    sleep 0.01 # 为了防止写文件太耗时,加一个延时10ms
    sed -i "${startLine}a #define ERRPR 1" ${fileName} ; startLine=$((${startLine} + 1))
    sleep 0.01 # 为了防止写文件太耗时,加一个延时10ms
    ...
    sed -i "${startLine}a #endif" ${fileName} ; startLine=$((${startLine} + 1))
done
# 此时,执行到第二段代码时,实际上成了死循环。如果只有一两行写入语句
# 还能跳出死循环,但目标文件已被写乱掉
# 如果行数多,直接就是死循环,及时中止,txt 文件已经达到37M
# 此问题百思不得其解,实在诡异

2、发现一点不同
在出故障的代码段中,发现最后一行行为正常:
    sed -i "${startLine}a #endif" ${fileName} ; startLine=$((${startLine} + 1))
它能正常写入,然后用它替换其它的行:
sed -i "${startLine}a #ifndef OK" ${fileName} ; startLine=$((${startLine} + 1))
改为 `#endif`之后,就能正常写入,其它字符串就不行,将中间空格去掉也不行:
sed -i "${startLine}a #ifndefOK" ${fileName} ; startLine=$((${startLine} + 1))

到了这份上,其实不就是写一个普通字符串吗,`#ifndefOK` 和 `#endif`能有啥区别?

powershell

删除一个文件或文件夹(或其它输入的目标)

Remove-Item cmdlet 正好顾名思义:它能使你清除一个东西和所有东西。清除文件C:/Script/test.txt ?那就删除它:
Remove - Item c:/scripts test.txt 
你也可以使用通配字符删除多个项目。举个例子,这个命令会删除所有在 C:/Scripts 里的文件 :
Remove - Item c:/scripts/ * 
这里是一个捕捉,当然。假设C:/Script包含替代的文件夹。在这种情况下,你可以提示是否要真的想要删除在Scripts文件夹里的任何东西。
Confirm
The item at C:/test/scripts has children and the  - recurse parameter was not specified. 
If you  continue , all children will be removed with the item. Are you sure you want to  continue ?
[Y] Yes  [A] Yes to All  [N] No  [L] No to All  [S] Suspend  [ ? ] Help
( default   is   " Y " ): 
有快速绕过这个的方法吗?对;只要在你的命令结尾处附加 -recurse 参数:
Remove - Item c:/scripts/ *   - recurse 
这是一个有趣的变化。假设这个Scripts文件夹包含了一大堆文件和你想要删除的所有东西。等等:也许不是所有的东西,也许想要留下什么 .wav文件。 没问题;只要使用 -exclude 参数并打入要说明的文件应该把它排出删除:
Remove - Item c:/scripts/ *   - exclude  * .wav 
那是什么?现在你仅仅想要删除 .wav 和 .mp3 文件,保留其它所有类型?你不得不要求(使用 -include 参数):
Remove - Item c:/scripts/ *   - include .wav,.mp3 
当你想通后,如果你使用 -include 参数 cmdlet 将运行在那些指定项目的一部分参数上(不错,你可以指定多个项目;只要分开时用逗号)。相比之下,如果你使用 -exclude 参数排除这些项目将免除cmdlet的行动。 
是的,如果你想得到真正喜欢的,你可以使用 -include 与 -exclude 在同一个命令中。你认为在运行这个命令的时候会发生什么?
Remove - Item c:/scripts/ *   - include  * .txt –exclude  * test * 
你懂得:在C:/Scripts文件夹里所有的 .txt 文件(当用 -include 参数标明时)都将被删除,除了一些在文件名为test字符值的任何文件名(当用 -exclude 参数标明时)。尝试一些不同的变化,不久它将变的很完美。 
顺便说一下,Remove-Item cmdlet 有一个 -whatif 参数,这实际上并不能删除任何东西,但能告诉你会发生什么,如果你使用 Remove-Item。没有任何有用的意义那你是什么意思?这里,你先看看这个命令:
Remove - Item c:/scripts/ * .vbs  - whatif 
如果我们运行这个命令,在文件夹C:/Scripts里所有 .vbs 类型的文件将会删除;当然我们会得到像这样返回的如下信息,让我们知道哪个文件被删除,如果你使用 Remove-Item 外部的 -whatif 参数:
What  if : Performing operation  " Remove File "  on Target  " C:/scripts/imapi.vbs " .
What  if : Performing operation  " Remove File "  on Target  " C:/scripts/imapi2.vbs " .
What  if : Performing operation  " Remove File "  on Target  " C:/scripts/methods.vbs " .
What  if : Performing operation  " Remove File "  on Target  " C:/scripts/read-write.vbs
" .
What  if : Performing operation  " Remove File "  on Target  " C:/scripts test.vbs " .
What  if : Performing operation  " Remove File "  on Target  " C:/scripts/winsat.vbs " . 
除此之外,你可以删除其它的文件和文件夹。举个例子,任命一个show别名来获取这个清除命令:
Remove - Item alias:/show 
做一个说明,如这个具体位置:alias:/ 。这是 Windows PowerShell 驱动的标准记号法。驱动字母,然后冒号,然后由一个"/" 。
Set - Location env:    
Remove-Item 别名      ri     rd     erase     rm     rmdir     del

统计二级目录大小

分享一个自己写的项目里用到的PowerShell脚本,
这个脚本写的很简单,功能也不复杂,
主要作用就是统计一个文件夹下的所有子文件夹的大小,
这是为了配合统计之前从各个用户计算机收集的信息,
每个收集到的信息都会被存档到以这台计算机名称命名的
文件夹里,但是哪台计算上收集失败了呢?
每个人收集上来的数据大小是多少呢?这个就需要再统计了。
这种功能的软件其实网上有很多,但是对于一个IT Pro来说,
什么事都问百度可不是个好习惯,自己动手丰衣足食,
反正这也是个很简单的功能不需要太复杂的逻辑,
直接上手用PowerShell就搞定了
下边来看看代码
function filesize ([string]$filepath)
{
    if ($filepath -eq $null)
    {
        throw "路径不能为空"
    }
    dir -Path $filepath |   ForEach-Object -Process {
        if ($_.psiscontainer -eq $true) 
        {
            $length = 0 
            dir -Path $_.fullname -Recurse | ForEach-Object {
                $length += $_.Length
            }
            $l = $length/1KB    $_.name + "文件夹的大小为: {0:n1} KB" -f $l
        }
    }
}
filesize -filepath "E:\系统文件转储\桌面\test"  
就是一个很简单的函数而已,来看看测试用的文件夹结构,test是父目录,在这个目录下会有很多子目录,每个子目录里可能还会嵌套子目录 
wKioL1VTTZuRtxIMAAJv0W-fTJ8698.jpg   
另外这些文件夹里也会包含很多文件 
wKiom1VTTCHBNTmMAAC2mzHXofE576.jpg     
执行这个函数之后,可以看到统计出来的结果如下,为了观看方便这里统计转换成了kb的形式 
filesize -filepath "E:\系统文件转储\桌面\test"
wKiom1VTTCHAPqQ9AAET2oq6s_U019.jpg
---over

shell数组倒序打印

一、方法一:使用字符串切片

#! /bin/bash
read -p "请输入倒序内容:" str
len=${#str} # 获取字符串长度
for((i=$len;i>=0;i--))
do
        echo -e "${str:$i:1}\c"  # 其中 -e 是开启转义 \c 取消echo的换行
done
echo ""

二、第二种:利用shell中的数组

#! /bin/bash
read -p "请输入倒序内容:" s
str=`echo $s | sed 's/./& /g'` # 这里将字符串转化为数组格式 h e l l e v e r y o n e
array=($str)    # 将字符串转化为数组(h e l l e v e r y o n e)
len=${#array[@]}    # 获取数组长度 还有一种方式 len=${#array[*]}
for ((i=$len - 1;i>=0;i--))
do
        echo -e "${array[$i]}\c" # 其中 -e 是开启转义 \c 取消echo的换行
done
echo ""

linux服务器ssh断开问题

方案一

修改server端的配置文件/etc/ssh/sshd_config
# server每隔60秒给客户端发送一次保活信息包给客户端
ClientAliveInterval 60

# server端发出的请求客户端没有回应的次数达到86400次的时候就断开连接,正常情况下客户端都会相应
ClientAliveCountMax 86400
修改client端的配置文件/etc/ssh/ssh_config
# client 每隔60秒给客户端发送一次保活信息包给客户端
ServerAliveInterval 60

# client 端发出的请求服务端没有回应的次数达到86400次的时候就断开连接,正常情况下服务端都会相应
ServerAliveCountMax 86400

# 方案二

在命令参数里
 ssh -o ServerAliveInterval=60 
这样子只会在需要的连接中保持持久连接,具体的参数请参考这里。

最后要重启sshd服务:
 # /etc/init.d/sshd restart 
或者:
[root@ecs-bxx7 ~]# systemctl restart sshd.service