ownCloud 是一个文件分享服务,可以将个人的文件内容,比如文本,图片,音频等等存储到一个中心服务器上,类似于 Dropbox。但是与 Dropbox 不同之处在于 ownCloud 是开源的,任何人都可以检视其源代码并且可以为之贡献代码,这意味着他将文件的控制权交给了个人,敏感的文件任何人都无法查看,但于此同时他也将文件的安全交给了个人管理。
[[OwnCloud]] 安装之前确保有 sudo
权限,并且 ownCloud 需要
安装
apt install nginx mysql-server php7.0 php-bz2 php-curl php-gd php-imagick php-intl php-mbstring php-xml php-zip
更多的安装详细教程可以查看 DigitalOcean 的教程
按照教程 使用 snap 安装。
或者手动安装
在使用 Celery 的时候发现有的时候 Celery 会将同一个任务执行两遍,我遇到的情况是相同的任务在不同的 worker 中被分别执行,并且时间只相差几毫秒。这问题我一直以为是自己哪里处理的逻辑有问题,后来发现其他人 也有类似的问题,然后基本上出问题的都是使用 Redis 作为 Broker 的,而我这边一方面不想将 Redis 替换掉,就只能在 task 执行的时候加分布式锁了。
不过在 Celery 的 issue 中搜索了一下,有人使用 Redis 实现了分布式锁,然后也有人使用了 Celery Once。 大致看了一下 Celery Once ,发现非常符合现在的情况,就用了下。
Celery Once 也是利用 Redis 加锁来实现,他的使用非常简单,参照 GitHub 的使用很快就能够用上。Celery Once 在 Task 类基础上实现了 QueueOnce 类,该类提供了任务去重的功能,所以在使用时,我们自己实现的方法需要将 QueueOnce 设置为 base
@task(base=QueueOnce, once={'graceful': True})
后面的 once 参数表示,在遇到重复方法时的处理方式,默认 graceful 为 False,那样 Celery 会抛出 AlreadyQueued 异常,手动设置为 True,则静默处理。
另外如果要手动设置任务的 key,可以指定 keys 参数
@celery.task(base=QueueOnce, once={'keys': ['a']})
def slow_add(a, b):
sleep(30)
return a + b
总得来说,分为几步
第一步,安装
pip install -U celery_once
第二步,增加配置
from celery import Celery
from celery_once import QueueOnce
from time import sleep
celery = Celery('tasks', broker='amqp://guest@localhost//')
celery.conf.ONCE = {
'backend': 'celery_once.backends.Redis',
'settings': {
'url': 'redis://localhost:6379/0',
'default_timeout': 60 * 60
}
}
第三步,修改 delay 方法
example.delay(10)
# 修改为
result = example.apply_async(args=(10))
第四步,修改 task 参数
@celery.task(base=QueueOnce, once={'graceful': True, keys': ['a']})
def slow_add(a, b):
sleep(30)
return a + b
更多详细的参数可以参考 GitHub,或者直接阅读源码。
Pipfile
文件添加或删除安装的包,Pipfile.lock
来锁定安装包的版本和依赖信息。requirements.txt
, 使用 Pipfile
和 Pipfile.lock
来代替pyenv
的条件下,可以自动安装需要的 Python 版本这里就不得不提到 pyenv 了,pyenv
能用来管理不同的 Python 版本,结合 pyenv-virtualenv
也能够快速创建虚拟环境,不过这个 pipenv
提供了另外一种思路。
安装:
pip install pipenv
基本使用
--three / --two Use Python 3/2 when creating virtualenv.
如果在 macOS 下:
brew install pipenv
pipenv install package
pipenv update package
升级包在创建虚拟环境时,我们可以使用 python 版本信息
pipenv --python 3
pipenv --python 3.6.1
pipenv --python 2.7.13
pipenv 会自动扫描系统寻找合适的版本信息,如果找不到的话,同时又安装了 pyenv, 它会自动调用 pyenv 下载对应的版本的 python
类似 pip
pipenv install requests
pipenv install requests==2.19.1
pipenv install --dev requests # 安装开发环境依赖
如果 install 后面没有任何 package 会自动安装所有,第一次安装包会自动生成 lock 文件
兼容 requirements.txt
pipenv install -r path/to/requirements.txt
同样可以使用 Pipfile
和 Pipfile.lock
文件来生成 requirements.txt
pipenv lock -r
pipenv lock -r -d # 生成 dev requirements
所以一个基本流程就是,对于 pipenv 管理的项目,使用 pipenv lock
来冻结管理,在分享给别人之后使用 pipenv install
来安装依赖。
直接修改 Pipfile
文件
[[source]]
url = "https://pypi.python.org/simple"
verify_ssl = true
name = "pypi"
[[source]]
url = "http://pypi.home.kennethreitz.org/simple"
verify_ssl = false
name = "home"
[dev-packages]
[packages]
requests = {version="*", index="home"}
maya = {version="*", index="pypi"}
records = "*"
pipenv shell
exit
pipenv graph
requests==2.19.1
- certifi [required: >=2017.4.17, installed: 2018.8.13]
- chardet [required: >=3.0.2,<3.1.0, installed: 3.0.4]
- idna [required: >=2.5,<2.8, installed: 2.7]
- urllib3 [required: >=1.21.1,<1.24, installed: 1.23]
pipenv uninstall --all
pipenv 默认集成了 flake8
pipenv check --style hello.py
关于 pipenv 和 pyenv 等等其他的区别,可以看这个回答 ,如果想要在 IntelliJ 中使用 pipenv ,2018.2 更新的版本中,也已经支持 了。
pipenv 也使用 pyenv 来做 Python 的版本管理,所以基本上,分工明确了,pyenv 用来区分 Python 版本,pipenv 用来管理包依赖。
Wireshark 是一款网络分析工具,也是学习网络协议的工具,原先介绍过的 Charles ,mitmproxy 等 HTTP 抓包工具,都局限于 HTTP/HTTPS 请求,对于更底层的 TCP/IP,UDP 等协议就无能为力了。Wireshark 可以抓取网卡上的网络包,并实时展示,Wireshark 包括了过滤器,协议显示等等工具。
Wireshark 和其他工具的区别,比如 Charles,mitmproxy,Fiddler 等。Charles, mitmproxy,Fiddler 是专门用来捕获 HTTP,HTTPS 请求的。Wireshark 能获取 HTTP,也能获取 HTTPS,但是不能解密 HTTPS,所以 Wireshark 看不懂 HTTPS 中的内容。总结,如果是处理 HTTP,HTTPS 还是用 Charles, mitmproxy, Fiddler 等,其他协议比如 TCP,UDP,IP,ICMP 等就用 Wireshark
各大系统的安装文件:https://www.wireshark.org/download.html
Linux 下可以使用 PPA
sudo add-apt-repository ppa:wireshark-dev/stable && sudo apt-get update
sudo apt-get install wireshark
安装过程中选择 YES,让普通用户也能够抓包。
如果启动之后遇到
couldn't run /usr/bin/dumpcap in child process: Permission Denied.
这样的问题,
sudo dpkg-reconfigure wireshark-common
选择 “YES”, 将当前用户添加到 group
sudo adduser $USER wireshark
然后再登出登入 1
打开 Wireshark 就可以看到很多网络硬件可以选择,任选其中一块网卡就能够抓取经过这个网卡的所有流量包。常见的设备名字,或者网卡名字有这样几个:
还有一些设备名字可以参考之前的文章。比如我笔记本使用无线网卡连接了 WIFI,那么进入 Wireshark 之后选择 wlan0
设备,自动进入抓包,可以看到经过无线网卡的所有请求包。
Wireshark 的界面大致可以分成三个部分,最上面的部分为原始数据包预览,可以在该面板中看到抓取的包大致内容,包括序号,耗时,原始地址,目标地址,协议,长度,基本信息等等,分别使用不同的颜色标记了,这个颜色可以在设置 View -> Coloring Rules
中设置,根据不同的协议,或者自定义一些过滤规则,将关心的内容以不同的颜色标记出。
面板中间是封包详细信息 (Packet Details Pane),这个面板是最重要的,用来查看协议中的每一个字段。各行信息分别为
面板最下面一栏是数据包真正传输的内容,以十六进制和 ASCII 显示出来。
https://wiki.wireshark.org/CaptureFilters
两种过滤器的目的是不同的。
捕捉过滤器(CaptureFilters):用于决定将什么样的信息记录在捕捉结果中。需要在开始捕捉前设置。捕捉过滤器是数据经过的第一层过滤器,它用于控制捕捉数据的数量,以避免产生过大的日志文件。
显示过滤器(DisplayFilters):在捕捉结果中进行详细查找。他们可以在得到捕捉结果后随意修改。显示过滤器是一种更为强大(复杂)的过滤器。它允许您在日志文件中迅速准确地找到所需要的记录。
两种过滤器使用的语法是完全不同的。
http://openmaniak.com/cn/wireshark_filters.php
Wireshark 实际分析下三次握手的过程
在 wireshark 中输入 http 过滤, 然后选中 GET /tankxiao HTTP/1.1 的那条记录,右键然后点击”Follow TCP Stream”,
这样做的目的是为了得到与浏览器打开网站相关的数据包
可以看到 wireshark 截获到了三次握手的三个数据包。第四个包才是 HTTP 的, 这说明 HTTP 的确是使用 TCP 建立连接的。
第一次握手数据包
客户端发送一个 TCP,标志位为 SYN,序列号为 0, 代表客户端请求建立连接。
第二次握手的数据包
服务器发回确认包,标志位为 SYN,ACK. 将确认序号 (Acknowledgement Number) 设置为客户的 ISN 加 1 以. 即 0+1=1
第三次握手的数据包
客户端再次发送确认包 (ACK) SYN 标志位为 0,ACK 标志位为 1. 并且把服务器发来 ACK 的序号字段 +1, 放在确定字段中发送给对方. 并且在数据段放写 ISN 的 +1
https://askubuntu.com/a/778170/407870 ↩
Chevereto 是一款分享照片的程序,可以非常轻松得在自己的服务器上搭建照片分享程序,功能强大,外观精美。Chevereto 本身是收费使用的,一次性付费,终身使用,但是其开源版本可以免费使用。
目前 Chevereto 的价格是 $39,可以免费升级到 V4 版本。作者已经发生声明,在 2021 年底将终止 Free 版本的维护。
推荐使用 Docker 安装 Chevereto:
在安装之前请先检查需要的系统配置,至少保证 VPS 安装有
安装依赖
apt-get install nginx mysql-server php7.0 php7.0-common php7.0-curl php7.0-mysql php7.0-gd php7.0-xml php7.0-mbstring
从官网下载最新版本 压缩包
wget https://github.com/Chevereto/Chevereto-Free/archive/1.0.9.tar.gz
新建虚拟主机,修改域名 A 记录指向 VPS,然后配置对应的 vim /etc/nginx/sites-enabled/photo.einverne.info
由于 chevereto 默认提供基于 Apache 环境的伪静态规则,故 nginx 的配置是不能用的,需要自己添加规则
server {
listen 80;
listen [::]:80;
root /var/www/photo.einverne.info/html;
index index.php index.html index.htm;
server_name server_domain_or_IP;
location / {
try_files $uri $uri/ =404;
}
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php7.0-fpm.sock;
}
location ~ /\.ht {
deny all;
}
}
nginx 配置中还要注意一个 vim /etc/nginx/nginx.conf
配置中增加:
server { client_max_body_size 20M; //other lines… }
修改完重新加载 Nginx 配置 /etc/init.d/nginx reload
。
安装完 MySQL 之后需要为 Chevereto 新建一个数据库:
mysql -u root -p # 登录mysql
create database photo; # 创建 photo 数据库
默认的PHP上传大小在配置中略有不同,如果想要增大每张照片上传的大小,不仅上面 Nginx 中需要配置,同理 PHP 配置中也需要修改如下 vim /etc/php/7.0/fpm/php.ini
:
max_execution_time
max_input_time
memory_limit
post_max_size
upload_max_filesize
修改完重新加载PHP配置 /etc/init.d/php7.0-fpm reload
。
在做完这一系列配置之后,将之前下载的压缩包,在 /var/www/photo.einverne.info/html/
目录下解压,然后使用域名访问。如果一切都没有问题,那么 Chevereto 会显示要求数据配置。要求填写:数据库名、数据库用户名、数据库用户密码,还有数据库表头。
这几项在前面安装时都已经完成,新建的数据库名,还有 MySQL 的用户名和密码,最后的数据表头名可以不变。然后下一步会填写管理员的一些信息,最后完成就好。
设置中文,网上很多说需要修改密码,其实,在设置管理员面板中能够直接修改语言为中文。
修改图片存储路径:默认是在/images文件夹内,修改方法为在config.php修改define(‘DIR_IM’,’images/‘);
,这一步其实现在也能够在设置中直接修改。
如果上传遇到问题,界面上显示
Server error (Internal server error)
一般的情况就是 Nginx 或者 PHP 的上传大小设置不对,上传的图片大于了 Nginx 或者 PHP 能够处理的大小,这是时候调整上传的大小就可以。调试方法如下;
没有给出任何信息,查看 nginx 错误日志
tailf /var/log/nginx/error.log
发现如下错误
2018/02/05 19:21:12 [error] 2693#2693: *8 client intended to send too large body: 1318270 bytes, client: 172.xxx.xxx.xxx, server: photo.einverne.info, request: "POST /json HTTP/1.1", host: "photo.einverne.info", referrer: "http://photo.einverne.info/dashboard/settings/system"
解决办法
vi /etc/nginx/nginx.conf
添加
client_max_body_size 20M;
然后 /etc/init.d/nginx reload
重新加载 nginx 服务配置。
最近国内又火了一个答题游戏,回答正确12题平分多少多少万奖金。先不说他们都抄袭 HQ Trivia 这款App,但我这两天一直在想一个问题,为什么这一类的应用能火起来,难道就是因为最后平分100万,200万,现金带来的刺激吗?我想答案一定不是的,除了最开始的玩家或许还能分到几十块钱,在入场玩家越来越多的情况下,每个人瓜分到的奖励一定是越来越少的。那到底是什么能让一个应用一夜之间火到大江南北?
很多人之前说过是主播带火了”吃鸡”游戏,这一点我是认同的,但这应该只是能火的其中一个因素,也就是在正常的宣传情况下,带来的口碑一层层的堆叠,能少能有游戏能够做好老少咸宜,并且也能够在不同人群中拥有大多数的好评。所以游戏本身的可玩性,娱乐性是应该需要得到保证的,玩家能够在游戏中得到乐趣,并且通过游戏提供的场景能够扩展出来不同的玩法,而这些玩法又足够好玩。这里就要说道在吃鸡游戏前面1分钟左右的等待时间,有个叫平底锅的主播用来喊麦,还有人在游戏中组队交友。
然后这就说道了能够火,并且能够让绝大部分人能够接受的第二大原因,就是这个游戏需要有足够的社交属性,不管是陌生人社交,还是熟悉的好友的社交。在足够话题性的同时,能够让三五好友聚到一起开黑,说语音,开黑。这是这个时代的需要,在吃鸡火起来之前的“王者荣耀”我想也是因为能够拉上三五亲朋好友组个队,带个节奏,尤其在过年短短的几天时间内,好多亲朋好友从五湖四海聚到一家,每个人从事的职业和活动没有太多交集,唯有一局王者荣耀才能让气氛足够的活跃起来。所以这里也要提到另一个能够流行的原因了,也就是让绝大部分人获取的方法足够简单。
Steam 上的绝地求生或许正是因为他对设备要求过高,而过滤掉很大一部分玩家,才有了网易在国内能够通过荒野行动,快速的聚集了近2亿的玩家,让足够多的人,只用下载一个App,不到几分钟的时间就上手玩,才有能力让这个游戏走的足够远。
再其次,如果一个游戏想要不被这个时代淘汰,不断的进化,并且有能力让超越游戏的内容,比如游戏竞技,或者游戏的Cosplay出现,才能够立于不败之地,回想以前玩过的War 3,孵化出了Dota,Dota2,还有 LOL,可是暴雪自己却没有赶上,也略遗憾。而其实这一点也很难做到,无数的手机益智类游戏,火玩一波,再来一波,愤怒的小鸟,植物大战僵尸都已经存在回忆中了。
说道这里再回头来看答题分奖金这个应用,他能火正是也正是因为他足够有话题性,也足够有社交属性,失散多年的好友,同学,亲戚可能都被拉来一起玩,并且我从来没有看到过玩一个答题游戏,能够在短短几天时间内,开黑群,语音发答案,自动搜索答案助手等等延伸品出现。再这个游戏也足够被绝大多数人接受,官方打的宣传语就是,答题不仅能赢钱还能学习知识。这一些因素促成了这个游戏必定成为2018年开年的热点,但其实呢?我也再想另外一个问题,这个模式能够持续多长时间,爆红之后带来的用户如何沉淀。
首先这个应用能够吸引足够的目标,并且能吸引一定的广告主,这是一定的,但如果到足够需要花半个小时时间来赢取2 、 3 元的奖励,到最后还能剩下多少衷心的用户。我觉得如果使用这样的方式来吸引用户来吸取内容,未免不是一个极好的宣传方式,但如果频繁开场次,绝大多数的用户会厌倦了每天9点定时打开手机等待一串废话。
2018年的农历新年马上就要到来了,可想而知这样一个应用必定还有爆发的一次机会,但以后如何走,就只能拭目以待了。
用盒子也已经很多年了,几年来家里,自己用,也积累了一些常用的应用。这两天又拿到了 T1 盒子,又才想起来整理这样一份单子,这样不用每一次都一遍一遍的尝试了。记得以前 VST ,泰捷视频都还很不错的时候,再后来广电发了禁令,再后来这片市场混乱发展,各家大型网站优酷,爱奇艺又不敢公开大搞,却又在背后偷偷摸摸。再到现在几乎被什么芒果,CIBN 垄断,内容没什么可看,却什么都要收费。我始终抱有一个观点,如果电视盒子这一块开放发展,国内的厂家完全能够占领全世界的盒子市场,好几年前用的 Android 盒子就已经能够满足我的大部分需求,并且应用设计也早 Google 自己推出 Android TV 盒子以及规范 Android TV 应用好多年。可惜这一块市场被一道禁令打到了地下。
Android 盒子安装应用的方法,大概可以分为这几个:
当然对于一个新的设备,通过 adb 安装一个应用市场,然后通过应用市场下载其他应用是最简单的方式。
Apple 有 App Store, Google 有 Play Store,电视盒子直到现在依然还在乱斗:
地址就不都给了,Google 搜一下很快。
电视直播的应用,以前用 VST 和 泰捷还行,不过现在已经废了,然后现在 HDP 做的也还不错。
2018 年 3 月 3 号更新,发现了一款叫做 超级直播 的应用,非常好用。使用超级直播的时候,在播放页面点击设置,然后选择 “二维码扫一扫开启更多功能”,并在二维码显示之后,连续多次点击出现的二维码多次,即可解锁隐藏功能。隐藏功能开启 6000 以后的频道,包括可以查看多个台湾、香港的频道,频道范围在 6xxx 可能上下有些偏差,65xx 开始会有抢先观看的电影。
一下排名按照易用程度:
放一张截图
网络视频是我用的最多的了,我本人用 哔哩哔哩 最多
盒子带 samba ,能读局域网内 samba 共享的视频,那就需要一个本地播放器,最好支持的解码格式越多越好,在 Android 手机上我买过 MX Player Pro,不过免费的 KMPlayer 也不错。
说到乐播投屏这个应用,还是我去实体店,然后有一个店员向我展示一个只有魔方大小的投影仪时,用的应用,将手机的屏幕投影到投影仪上,我就记住了这个投屏应用,回来发现,iOS 投屏还是不错的。
其他体验还不错的应用:
今天突然想到,我平时看的最多的 YouTube ,是有电视版的啊,最近又把路由器更新了一下,局域网使用 YouTube 完全没问题啊,然后就下载了几个 YouTube for TV 的应用,发现只有下面这一个不需要依赖 Play Service,然后,在侧边栏设置中,有一个关联码,和手机关联,然后就可以非常轻松的将手机上的 YouTube 视频投送到电视盒子上,然后再到投影仪上。太舒服了。
YouTube for TV
版本:1.12.10 发布时间:2018-01-12
下载地址: https://apkpure.com/youtube-tv-watch-record-live-tv/com.google.android.apps.youtube.unplugged
OkHttp是一个非常高效的HTTP客户端,默认情况下:
阻塞请求
private final OkHttpClient client = new OkHttpClient();
public void run() throws Exception {
Request request = new Request.Builder()
.url("http://publicobject.com/helloworld.txt")
.build();
Response response = client.newCall(request).execute();
if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
Headers responseHeaders = response.headers();
for (int i = 0; i < responseHeaders.size(); i++) {
System.out.println(responseHeaders.name(i) + ": " + responseHeaders.value(i));
}
System.out.println(response.body().string());
}
响应可读时回调 Callback 接口,读取响应时阻塞当前线程
private final OkHttpClient client = new OkHttpClient();
public void run() throws Exception {
Request request = new Request.Builder()
.url("http://publicobject.com/helloworld.txt")
.build();
client.newCall(request).enqueue(new Callback() {
@Override public void onFailure(Request request, Throwable throwable) {
throwable.printStackTrace();
}
@Override public void onResponse(Response response) throws IOException {
if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
Headers responseHeaders = response.headers();
for (int i = 0; i < responseHeaders.size(); i++) {
System.out.println(responseHeaders.name(i) + ": " + responseHeaders.value(i));
}
System.out.println(response.body().string());
}
});
}
public static final MediaType MEDIA_TYPE_MARKDOWN
= MediaType.parse("text/x-markdown; charset=utf-8");
private final OkHttpClient client = new OkHttpClient();
public void run() throws Exception {
String postBody = ""
+ "Releases\n"
+ "--------\n"
+ "\n"
+ " * _1.0_ May 6, 2013\n"
+ " * _1.1_ June 15, 2013\n"
+ " * _1.2_ August 11, 2013\n";
Request request = new Request.Builder()
.url("https://api.github.com/markdown/raw")
.post(RequestBody.create(MEDIA_TYPE_MARKDOWN, postBody))
.build();
Response response = client.newCall(request).execute();
if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
System.out.println(response.body().string());
}
流的方式提交请求体
public static final MediaType MEDIA_TYPE_MARKDOWN
= MediaType.parse("text/x-markdown; charset=utf-8");
private final OkHttpClient client = new OkHttpClient();
public void run() throws Exception {
RequestBody requestBody = new RequestBody() {
@Override public MediaType contentType() {
return MEDIA_TYPE_MARKDOWN;
}
@Override public void writeTo(BufferedSink sink) throws IOException {
sink.writeUtf8("Numbers\n");
sink.writeUtf8("-------\n");
for (int i = 2; i <= 997; i++) {
sink.writeUtf8(String.format(" * %s = %s\n", i, factor(i)));
}
}
private String factor(int n) {
for (int i = 2; i < n; i++) {
int x = n / i;
if (x * i == n) return factor(x) + " × " + i;
}
return Integer.toString(n);
}
};
Request request = new Request.Builder()
.url("https://api.github.com/markdown/raw")
.post(requestBody)
.build();
Response response = client.newCall(request).execute();
if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
System.out.println(response.body().string());
}
POST 方式提交文件
public static final MediaType MEDIA_TYPE_MARKDOWN
= MediaType.parse("text/x-markdown; charset=utf-8");
private final OkHttpClient client = new OkHttpClient();
public void run() throws Exception {
File file = new File("README.md");
Request request = new Request.Builder()
.url("https://api.github.com/markdown/raw")
.post(RequestBody.create(MEDIA_TYPE_MARKDOWN, file))
.build();
Response response = client.newCall(request).execute();
if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
System.out.println(response.body().string());
}
public void run() throws Exception {
RequestBody formBody = new FormEncodingBuilder()
.add("search", "Jurassic Park")
.build();
Request request = new Request.Builder()
.url("https://en.wikipedia.org/w/index.php")
.post(formBody)
.build();
Response response = client.newCall(request).execute();
if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
System.out.println(response.body().string());
}
private static final String IMGUR_CLIENT_ID = "...";
private static final MediaType MEDIA_TYPE_PNG = MediaType.parse("image/png");
private final OkHttpClient client = new OkHttpClient();
public void run() throws Exception {
// Use the imgur image upload API as documented at https://api.imgur.com/endpoints/image
RequestBody requestBody = new MultipartBuilder()
.type(MultipartBuilder.FORM)
.addPart(
Headers.of("Content-Disposition", "form-data; name=\"title\""),
RequestBody.create(null, "Square Logo"))
.addPart(
Headers.of("Content-Disposition", "form-data; name=\"image\""),
RequestBody.create(MEDIA_TYPE_PNG, new File("website/static/logo-square.png")))
.build();
Request request = new Request.Builder()
.header("Authorization", "Client-ID " + IMGUR_CLIENT_ID)
.url("https://api.imgur.com/3/image")
.post(requestBody)
.build();
Response response = client.newCall(request).execute();
if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
System.out.println(response.body().string());
}
awk 是一个强大的文本分析工具,它是 Linux 中功能强大的数据处理引擎之一,awk 可以非常轻松地处理比如每行都是相同格式的文本,比如日志,csv 格式等等。相对于 grep 的查找,sed 的编辑,awk 在其对数据分析并生成报告时,显得尤为强大。
当使用 awk 命令处理一个或者多个文件时,它会依次读取文件的每一行内容,然后对其进行处理,awk 命令默认从 stdio 标准输入获取文件内容,awk 使用一对单引号来表示一些可执行的脚本代码,在可执行脚本代码里面,使用一对花括号来表示一段可执行代码块,可以同时存在多个代码块。awk 的每个花括号内同时可以有多个指令,每一个指令用分号分隔,awk 其实就是一个脚本编程语言。
awk 命令和 sed 命令结构相同,通常情况下,awk 将每个输入行解释为一条记录而每一行中的内容(由空格或者制表符分隔)解释为每一个字段,一个或者多个连续空格或者制表符看做定界符。awk 中 $0
代表整个记录。
awk ' /MA/ { print $1 }' list
解释:打印包含 MA 的行中的第一个单词。再举一个具体的例子,比如
echo 'this is one world\nthat is another world' | awk '{print $1}'
那么输出就是 awk 处理之后的每一行第一个字符也就是:
this
that
awk 命令的基本格式
awk [options] 'script' file
options
这个表示一些可选的参数选项,script
表示 awk 的可执行脚本代码(一般被{}
花括号包围),这个是必须的。file
这个表示 awk 需要处理的文件,注意需要是纯文本文件(意味着 awk 能够处理)。
之前提到的,awk 默认的分割符为空格和制表符,awk 会根据这个默认的分隔符将每一行分为若干字段,依次用 $1
, $2
,$3
来表示,可以使用 -F
参数来指定分隔符
awk -F ':' '{print $1}' /etc/passwd
解释:使用 -F
来改变分隔符为 :
,比如上面的命令将 /etc/passwd
文件中的每一行用冒号 :
分割成多个字段,然后用 print 将第 1 列字段的内容打印输出
在 awk 中同时指定多个分隔符,比如现在有这样一个文件 some.log 文件内容如下
Grape(100g)1980
raisins(500g)1990
plum(240g)1997
apricot(180g)2005
nectarine(200g)2008
现在我们想将上面的 some.log 文件中按照 “水果名称(重量)年份” 来进行分割
$ awk -F '[()]' '{print $1, $2, $3}' some.log
Grape 100g 1980
raisins 500g 1990
plum 240g 1997
apricot 180g 2005
nectarine 200g 2008
在 -F
参数中使用一对方括号来指定多个分隔符,awk 处理 some.log 文件时就会使用 “(“ 或者 “)” 来对文件的每一行进行分割。
awk 除了 $
和数字表示字段还有一些其他的内置变量:
$0
这个表示文本处理时的当前行,$1
表示文本行被分隔后的第 1 个字段列,$2
表示文本行被分割后的第 2 个字段列,$3
表示文本行被分割后的第 3 个字段列,$n 表示文本行被分割后的第 n 个字段列$NF
就表示最后一个字段,$(NF-1)
就表示倒数第二个字段比如我们有这么一个文本文件 fruit.txt 内容如下,用它来演示如何使用 awk 命令工具
peach 100 Mar 1997 China
Lemon 150 Jan 1986 America
Pear 240 Mar 1990 Janpan
avocado 120 Feb 2008 china
awk '{print $0}' fruit.txt # 表示打印输出文件的每一整行的内容
awk '{print $1}' fruit.txt # 表示打印输出文件的每一行的第 1 列内容
awk '{print $1, $2}' fruit.txt
文件的每一行的每一列的内容除了可以用 print 命令打印输出以外,还可以对其进行赋值
awk '{$2 = "***"; print $0}' fruit.txt
上面的例子就是表示通过对 $2
变量进行重新赋值,来隐藏每一行的第 2 列内容,并且用星号 *
来代替其输出
在参数列表中加入一些字符串或者转义字符之类的东东
awk '{print $1 "\t" $2 "\t" $3}' fruit.txt
像上面这样,你可以在 print 的参数列表中加入一些字符串或者转义字符之类的东东,让输出的内容格式更漂亮,但一定要记住要使用双引号。
awk 内置 NR 变量表示每一行的行号
awk '{print NR "\t" $0}' fruit.txt
1 peach 100 Mar 1997 China
2 Lemon 150 Jan 1986 America
3 Pear 240 Mar 1990 Janpan
4 avocado 120 Feb 2008 china
awk 内置 NF 变量表示每一行的列数
awk '{print NF "\t" $0}' fruit.txt
5 peach 100 Mar 1997 China
5 Lemon 150 Jan 1986 America
5 Pear 240 Mar 1990 Janpan
5 avocado 120 Feb 2008 china
awk 中 $NF 变量的使用
awk '{print $NF}' fruit.txt
上面这个 $NF
就表示每一行的最后一列,因为 NF 表示一行的总列数,在这个文件里表示有 5 列,然后在其前面加上 $
符号,就变成了 $5
,表示第 5 列
awk '{print $(NF - 1)}' fruit.txt
1997
1986
1990
2008
上面 $(NF-1) 表示倒数第 2 列, $(NF-2) 表示倒数第 3 列,依次类推。
awk 'NR % 6' # 打印出了 6 倍数行之外的其他行
awk 'NR > 5' # 打印第 5 行之后内容,类似 `tail -n +6` 或者 `sed '1,5d'`
awk 'NF >= 6' # 打印大于等于 6 列的行
awk '/foo/ && /bar/' # 打印匹配 `/foo/` 和 `/bar/` 的行
awk '/foo/ && !/bar/' # 打印包含 `/foo/` 不包含 `/bar/` 的行
awk '/foo/ || /bar/' # 或
awk '/foo/,/bar/' # 打印从匹配 `/foo/` 开始的行到 `/bar/` 的行,包含这两行
awk 还提供了一些内置函数,比如:
toupper()
用于将字符转为大写tolower()
将字符转为小写length()
长度substr()
子字符串sin()
正弦cos()
余弦sqrt()
平方根rand()
随机数更多的方法可以参考:man awk
awk '{print FILENAME "\t" $0}' demo1.txt demo2.txt
当你使用 awk 同时处理多个文件的时候,它会将多个文件合并处理,变量 FILENAME 就表示当前文本行所在的文件名称。
在脚本代码段前面使用 BEGIN 关键字时,它会在开始读取一个文件之前,运行一次 BEGIN 关键字后面的脚本代码段, BEGIN 后面的脚本代码段只会执行一次,执行完之后 awk 程序就会退出
awk 'BEGIN {print "Start read file"}' /etc/passwd
awk 脚本中可以用多个花括号来执行多个脚本代码,就像下面这样
awk 'BEGIN {print "Start read file"} {print $0}' /etc/passwd
awk 的 END 指令和 BEGIN 恰好相反,在 awk 读取并且处理完文件的所有内容行之后,才会执行 END 后面的脚本代码段
awk 'END {print "End file"}' /etc/passwd
awk 'BEGIN {print "Start read file"} {print $0} END {print "End file"}' /etc/passwd
可以在 awk 脚本中声明和使用变量
awk '{msg="hello world"; print msg}' /etc/passwd
awk 声明的变量可以在任何多个花括号脚本中使用
awk 'BEGIN {msg="hello world"} {print msg}' /etc/passwd
在 awk 中使用数学运算,在 awk 中,像其他编程语言一样,它也支持一些基本的数学运算操作
awk '{a = 12; b = 24; print a + b}' company.txt
上面这段脚本表示,先声明两个变量 a = 12 和 b = 24,然后用 print 打印出 a 加上 b 的结果。
请记住 awk 是针对文件的每一行来执行一次单引号 里面的脚本代码,每读取到一行就会执行一次,文件里面有多少行就会执行多少次,但 BEGIN 和 END 关键字后脚本代码除外,如果被处理的文件中什么都没有,那 awk 就一次都不会执行。
awk 还支持其他的数学运算符
+ 加法运算符
- 减法运算符
* 乘法运算符
/ 除法运算符
% 取余运算符
在 awk 中使用条件判断
比如有一个文件 company.txt 内容如下
yahoo 100 4500
google 150 7500
apple 180 8000
twitter 120 5000
如果要判断文件的第 3 列数据,也就是平均工资小于 5500 的公司,然后将其打印输出
awk '$3 < 5500 {print $0}' company.txt
上面的命令结果就是平均工资小于 5500 的公司名单,$3 < 5500
表示当第 3 列字段的内容小于 5500 的时候才会执行后面的 {print $0} 代码块
awk '$1 == "yahoo" {print $0}' company.txt
awk 还有一些其他的条件操作符如下
< 小于
<= 小于或等于
== 等于
!= 不等于
> 大于
>= 大于或等于
~ 匹配正则表达式
!~ 不匹配正则表达式
使用 if 指令判断来实现上面同样的效果
awk '{if ($3 < 5500) print $0}' company.txt
上面表示如果第 3 列字段小于 5500 的时候就会执行后面的 print $0
比如现在我们有这么一个文件 poetry.txt 内容如下:
This above all: to thine self be true
There is nothing either good or bad, but thinking makes it so
There’s a special providence in the fall of a sparrow
No matter how dark long, may eventually in the day arrival
使用正则表达式匹配字符串 “There” ,将包含这个字符串的行打印并输出
awk '/There/{print $0}' poetry.txt
There is nothing either good or bad, but thinking makes it so
There’s a special providence in the fall of a sparrow
使用正则表达式配一个包含字母 t 和字母 e ,并且 t 和 e 中间只能有任意单个字符的行
awk '/t.e/{print $0}' poetry.txt
There is nothing either good or bad, but thinking makes it so
There’s a special providence in the fall of a sparrow
No matter how dark long, may eventually in the day arrival
如果只想匹配单纯的字符串 “t.e”, 那正则表达式就是这样的 /t.e/ ,用反斜杠来转义 . 符号 因为 . 在正则表达式里面表示任意单个字符。
使用正则表达式来匹配所有以 “The” 字符串开头的行
awk '/^The/{print $0}' poetry.txt
在正则表达式中 ^ 表示以某某字符或者字符串开头。
使用正则表达式来匹配所有以 “true” 字符串结尾的行
awk '/true$/{print $0}' poetry.txt
在正则表达式中 $ 表示以某某字符或者字符串结尾。
awk '/m[a]t/{print $0}' poetry.txt
No matter how dark long, may eventually in the day arrival
上面这个正则表达式 /m[a]t/
表示匹配包含字符 m ,然后接着后面包含中间方括号中表示的单个字符 a ,最后包含字符 t 的行,输出结果中只有单词 “matter” 符合这个正则表达式的匹配。因为正则表达式 [a] 方括号中表示匹配里面的任意单个字符。
继续上面的一个新例子如下
awk '/^Th[ie]/{print $0}' poetry.txt
这个例子中的正则表达式 /^Th[ie]/ 表示匹配以字符串 “Thi” 或者 “The” 开头的行,正则表达式方括号中表示匹配其中的任意单个字符。
再继续上面的新的用法
awk '/s[a-z]/{print $0}' poetry.txt
正则表达式 /s[a-z]/
表示匹配包含字符 s 然后后面跟着任意 a 到 z 之间的单个字符的字符串,比如 “se”, “so”, “sp” 等等。
正则表达式 [] 方括号中还有一些其他用法比如下面这些
[a-zA-Z] 表示匹配小写的 a 到 z 之间的单个字符,或者大写的 A 到 Z 之间的单个字符
[^a-z] 符号 `^` 在方括号里面表示取反,也就是非的意思,表示匹配任何非 a 到 z 之间的单个字符
正则表达式中的星号 *
和加号 +
的使用方法,*
表示匹配星号前字符串 0 次或者多次,+
和星号原理差不多,只是加号表示任意 1 个或者 1 个以上,也就是必须至少要出现一次。
正则表达式问号 ? 的使用方法,正则中的问号 ?
表示它前面的字符只能出现 0 次 或者 1 次。
正则表达式中的 {} 花括号用法,花括号 {} 表示规定它前面的字符必须出现的次数,像这个 /go{2}d/ 就表示只匹配字符串 “good”,也就是中间的字母 “o” 必须要出现 2 次。
正则表达式中的花括号还有一些其他的用法如下
/go{2,10}d/ 表示字母 "o" 只能可以出现 2 次,3 次,4 次,5 次,6 次 ... 一直到 10 次
/go{2,}d/ 表示字母 "o" 必须至少出现 2 次或着 2 次以上
正则表达式中的圆括号表示将多个字符当成一个完整的对象来看待。比如 /th(in){1}king/ 就表示其中字符串 “in” 必须出现 1 次。而如果不加圆括号就变成了 /thin{1}king/ 这个就表示其中字符 “n” 必须出现 1 次。
接上面正则使用,比如文件中有行数据
/abc/def/123 456
/abc/def/222 456
想要移除 123,保留之前的字母和后面的数字,则可以使用
awk 'sub(/[0-9]+/,"",$1)' /path/to/file
使用 awk 过滤 history 输出,找到最常用的命令
history | awk '{a[$2]++}END{for(i in a){print a[i] " " i}}' | sort -rn | head
过滤文件中重复行
awk '!x[$0]++' <file>
将一行长度超过 72 字符的行打印
awk 'length>72' file
查看最近哪些用户使用系统
last | grep -v "^$" | awk '{ print $1 }' | sort -nr | uniq -c
假设有一个文本,每一行都是一个 int 数值,想要计算这个文件每一行的和,可以使用
awk '{s+=$1} ENG {printf "%.0f", s}' /path/to/file
斐讯投资送硬件这路子是着魔了,所以在斐讯 K2P 之后又入手了这个 T1 的盒子,配置 2G RAM,16G Sdcard,配置还不错。接口有 USB,网口,HDMI 口,还有 一个 AV 输出口,日常使用是没有任何问题的,初尝试一下非常流畅,不过让我不爽的是,第一次进入竟然需要验证手机,说是 CIBN 盒子的验证,如果不注册还不让进入。
CPU:Amlogic S912 八核 Cortex-A53 CPU up to 2.0GHz GPU:ARM Mali-T820MP3 GPU up to 750MHz
2G RAM+16G ROM,双频 WIFI,且支持 ac,但只是单天线
首先,进入盒子的设置 – 高级 – 远程调试打开,然后下载 adb, 开发过 Android 的人,或者大概熟悉 Android 的人一定用过这个命令,我之前也写过文章,总结 adb 常用的命令。
将盒子通电,将盒子联网(可以使用网线,也可以使用无线网络),只要能够在局域网中有一个 IP 地址,然后进路由器里记录,找到盒子的 IP(192.168.x.x)地址。
adb connect 192.168.x.x (盒子 IP)
adb shell
pm list packages # 可以用来查看安装的应用列表
pm uninstall -k --user 0 com.feixun.dangbeimarket #自带 cibn 当贝市场
pm uninstall -k --user 0 com.tianci.ad #广告
pm uninstall -k --user 0 com.feixun.qiyiguo #自带爱奇艺
pm uninstall -k --user 0 cn.cibn.health #健康中国
pm uninstall -k --user 0 com.pptv.tvsports.preinstall #CIBN 体育
pm uninstall -k --user 0 com.phicomm.phiweather #天气
pm uninstall -k --user 0 com.pplive.atv #聚精彩
pm uninstall -k --user 0 cn.cibntv.ott #CIBN 高清影视
pm uninstall -k --user 0 com.phicomm.update #斐讯 OTA 升级
pm uninstall -k --user 0 com.phicomm.datatracker #斐讯数据收集
如果想要恢复最原始的状态,在设置选项里,恢复出厂设置,还是最原来的状态。
如果路由器支持过滤广告,可以添加如下两条规则:
asimgs.cp61.ott.cibntv.net
hoisin.coocaatv.com
简单的方法,只通过遥控器操作,先用系统自带的当贝市场,找到“视频加速器”这个应用,安装然后运行,就会在视频加速器运行界面的下方,看到有“当贝市场”,再安装这个当贝市场。
如果你有需要安装本地文件的 APK 安装包,有两种方式,推荐使用 adb 方式安装,先将想要安装的 apk,下载到本地文件,然后 adb 连接盒子
adb connect <ip>
adb install /path/to/app.apk # 安装本地路径下的应用
直接将本地的应用安装到盒子。
如果安装不成功,记得禁用安装验证,在 adb 连接的情况下
settings put global package_verifier_enable 0
或者用“悟空遥控器”这个应用,当贝市场里可以搜索它来安装,但也要在手机上下载安装“悟空遥控器”APP,里面有个“本地 APK 推送”,用它就可以安装你想要装的应用了,只要有 APK 安装包。
最后要强烈推荐一个投屏 App,乐播投屏,因为斐讯 T1 并没有 DLNA 的功能,但幸而只要一个 App 就能够解决,这样就能够将手机爱奇艺上的内容快速的投屏到盒子上。
恩山最近的帖子更新了 T1 su 的密码,记录一下
31183118
最后这篇文章 是我使用过并且觉得不错的 Android TV 的应用,可以参考。