我最近又心血来潮,翻出家里的旧笔记本,装上了 Ubuntu 20.04 LTS Desktop 版本,打算用来偶尔写写代码用。至于为什么选Ubuntu 是因为我觉得选择个最不折腾。事实证明我错了,我不应该信任任何 Linux Desktop 发行版。我刚装完系统,然后一通 apt update 和 apt upgrade,再 reboot 一下。然后我就发现一个蛋疼的问题,设置窗口打不开了,确切的说是不显示了。然后就 Google 一通搜,发现碰到这问题的人还真不少,从 Ubuntu 17 时代就有人碰到了。解决方法说啥的都有:什么 reinstall gnome-control-center,甚至有让人重装系统解决问题的,简直离谱。功夫不负有心人,我总算找到了正确的解决方法,现在记录下来,希望下一个碰到这个问题的可怜孩子能看到这篇文章。
症状 打开设置的时候,能看到侧边栏上有设置图标,但是屏幕上看不到设置窗口。在设置图标上右键点「所有窗口」,能看到设置窗口的预览。初步猜测,设置是正常打开的,但是窗口显示到屏幕外面出去了,导致我们看不到。
解决方法 左手按住Ctrl+F7,然后右手把鼠标向左拖动,记住这个时候不需要按鼠标上的任何键,然后你就会看到奇迹发生了:设置窗口被从屏幕右边拖出来了!
看来问题真的是窗口显示到屏幕的外面去了,我们现在把它拖回来了。但是问题并没有真正解决,当你下次再打开设置的时候,窗口依然显示在屏幕外面。
打开设置里面的「显示器」选项,你会看到,不知道在什么时候,「显示模式」这个选项被设置成了「加入显示器(Join Displays)」,现在把它改成「单显示器(Single Display)」。然后一切就正常了。
参考 https://askubuntu.com/a/1111186
自从去年买了一个蜗牛矿渣当作 NAS 开始,我就在折腾 NAS 这条路上越走越远。在 NAS上折腾软件和服务最方便的就是通过 docker 部署。所以能找到 docker 镜像的软件,我绝对不会自己折腾安装过程。当然有些时候没有办法找到现成的 docker 镜像,那就自己动手构建镜像。
众所周知,我们可以通过 Docker Hub 的自动化构建存放在 GitHub 上某个 branch 的代码。每当我们 git push 代码到 GitHub 就会触发一次 Docker Hub 构建一个新版本。
但是我最近发现一个问题,就是每次构建出来的镜像都是 latest 版本,没有留下历史版本的镜像,这就很不方便。比如我新构建的镜像有问题,但是没办法立刻回退到上一个版本的镜像,除非我把代码改回去再构建一次,这事儿听起来就很不科学。
当然 Docker Hub 也可以根据 GitHub 的特定 Tag 来自动构建镜像。难道我每次在 GitHub 创建一个新的 Tag 都要在 Docker Hub 上创建一条新的构建规则吗?这事儿听起来也挺蠢的。
有没有办法当我在 GitHub 创建一个任意版本号的 Tag 的时候,Docker Hub 这边都自动根据这个 Tag 的版本号,自动构建一个对应版本号的镜像呢?答案当然是有的,Docker Hub 的自动构建规则支持表达式匹配 Tag。
我们可以新增一条如下的自动构建规则:
Source Type Source Docker Tag Tag /^v([0-9]+)....
分享每周收集的碎片化知识和资讯。
技术 Ubuntu 18.04 设置静态 IP 的方法
在 Ubuntu 17.10 包括之前版本中可以通过修改 /etc/network/interfaces 设置静态 IP。但是在18.04 之后 Ubuntu 采用了新的 Netplan 程序管理网络配置,那么设置静态 IP 的方法就有些不同了。
sudo vim /etc/netplan/50-cloud-init.yaml network: ethernets: enp0s3: #dhep4: true dhcp4: false addresses:[192.168.2.90/24] gateway4: 192.168.2.1 nameservers: addresses:[8.8.8.8, 1.1.1.1] version: 2 sudo netplan apply 参考:Setting up your static IP address since Ubuntu 18.04
命令行代理设置
export ALL_PROXY=socks5://127.0.0.1:1080 export ALL_PROXY=http://127.0.0.1:8118 unset ALL_PROXY nginx warn:a client request body is buffered to a temporary file...
Divide Two Integers https://leetcode.com/problems/divide-two-integers/
题目要求不用乘法、除法和取模运算符,实现整型除法运算。
既然不能用乘法和除法运算符,那么基本思路就是用减法来代替。不过如果用每次循环减一次被除数这种方式,是肯定会超时的。所以要想办法加速运算。
Solution class Solution { public: int divide(int dividend, int divisor) { long long quotient = 0; int flag = 1; auto dividend1 = (long long)fabs(dividend); auto divisor1 = (long long)fabs(divisor); if(dividend < 0) { flag = -1; } if(divisor < 0) { flag = flag == 1 ? -1 : 1; } std::vector<std::pair<long long, long long> > vec; long long times = 1; while (dividend1 >= divisor1) { vec....
Container With Most Water https://leetcode.com/problems/container-with-most-water/
给定一个整型数组,表示坐标轴上的一组相应长度的垂直于 x 轴的线段,要求找出两条线跟 x 轴围起来的容器,能装最多的水。
这题一开始我想用最暴力的方法做两层循环O(n^2)的解法提交试一下,可惜超时了,果然 Medium 难度的题目不会这么轻松放我过去的。
既然 O(n^2) 的解法没有办法通过,那就要想办法能不能找出 O(n) 的解法,一遍循环弄出来。我绞尽脑汁想了很久,尝试了好几种方案,始终没有办法解决。我最终不得不求助于前人的经验,看到别人的解法之后,顿时觉得豁然开朗。
Solution class Solution { public: int maxArea(vector<int>& height) { int max_area = 0; int area = 0; int i = 0; int j = height.size() - 1; while(i < j) { if(height[i] > height[j]) { area = (j - i) * height[j]; --j; } else { area = (j - i) * height[i]; ++i; } if(area > max_area) { max_area = area; } } return max_area; } }; 其实这个解法的关键是用两个指针,分别从左右两边向中间开始遍历,每次算出当前两个指针所指的线围成的面积,并比较是不是当前最大的记到 max_area 里,然后每次排除较短的一条线段,继续遍历。...
ARTS 是由陈皓(网名「左耳朵耗子」)在极客时间上发起的一个每周打卡计划,详情查看 https://www.zhihu.com/question/301150832 。
Algorithm LeetCode 009 — Palindrome Number
Review How to think like a programmer — lessons in problem solving
这是 Medium 上的一篇文章,名字翻译成中文是《怎样像程序员一样思考》。
文章的开头说「像程序员一样思考」实际上是指一种更有效的解决问题的方法。
我们每个人总是碰到各种各样的问题,或大或小。我们处理这些问题的方式,从某种程度上来说,非常随机。一般人们总是这样来解决一个问题:首先尝试一种解决方法;如果这种方式不起作用,那么就尝试另外一种;如果依然不起作用,那么重复第二个步骤,直到你足够幸运解决了问题。有的时候,你足够幸运,但是这是解决问题最差的方式。
解决一个问题最好的方式包括两项:a) 找到一个处理问题的通用框架 b) 不断训练
通用框架 1. 理解问题 理解问题的本质。大多数困难的问题之所以困难是因为你没有理解问题的本质。
2. 做计划 不要在没有做好解决方案的计划之前一头扎进去。要写下解决问题的具体步骤。
3. 分解问题 这是最重要的一个步骤。
不要尝试解决一个巨大的问题,要把它分解成几个子问题。然后依次解决们每一个子问题。最后把这些子问题串联起来,你将会得到原始问题的解决方案。
4. 卡住了? 当你被卡住了,甚至解决不了一个子问题的时候,首先深呼吸,然后你要知道,这是很正常的。
下面是几个你可以尝试的方法:
调试:一步步审视你的解决方案找出你错在哪里。
重新评估问题:从另一个角度观察问题,看看是不是有某些东西能抽象成更通用的途径。
搜索:从搜索引擎寻找解决问题的方式。不管你的问题是什么,很可能别人已经解决过了。
5. 训练 不要期望自己在仅仅一周之后就变得很厉害。如果你希望成为一个很厉害的解决问题的人,那么唯一的途径就是解决很多问题。
不断训练。
Tip ES6 中遍历数组键值对的方法:
ES6 提供三个新的方法——entries(),keys()和values()——用于遍历数组。它们都返回一个遍历器对象,可以用for…of循环进行遍历,唯一的区别是keys()是对键名的遍历、values()是对键值的遍历,entries()是对键值对的遍历。
for (let index of ['a', 'b']....
Palindrome Number https://leetcode.com/problems/palindrome-number/
给定一个整型数,判断这个数是否为回文数
我们根据例子很容易得出,负数不是回文数,一位正整数肯定是回文数。
Solution1 首先用最简单的方法,把数字转换成字符串,从两边向中间依次判断对应位的数字是否相等。
class Solution { public: bool isPalindrome(int x) { if (x < 0) { return false; } if( x == 0) { return true; } std::string str = to_string(x); int middle = str.length()/2; for (int i = 0; i < middle; ++i) { if(str[i] != str[str.length() - 1 - i]) { return false; } } return true; } }; Solution2 题目的最后问能否不通过转换成字符串的方式解决这个问题,那就尝试一下吧。不通过字符串,其实我们还是要从两边到中间依次比较对应位的数字。那么问题来了,如何依次取得左右两边的数字呢?
我们以 12341 这个数字为例,想要获得最低位数字 1 很简单只要12341 % 10就得到了。那么如何获得最高位数字 1 呢?我们发现12345/10000 -> 1,也就是说用这个数除以 10^4 就行了,其中的 4 是 12341 的位数 5 减去 1。我们可以通过循环 / 10 的方式取得一个数字的位数。...
String to Integer (atoi) https://leetcode.com/problems/string-to-integer-atoi/
题目大意就是自己实现一个 atoi 函数把字符串转换成一个整型数字
字符可能以多个空格开始,处理的时候要舍弃左边的空格,从第一个不为空格的字符开始处理; 数值字符串可能以+-号开头,要正确处理; 数值字符串后可能带有多余的非数字字符,这些字符不会对结果造成影响,处理的时候要忽略; 第一个非空格字符不是合法的数字字符或者+-号的时候,这个字符串不是合法的数值字符串,要返回 0; 结果大于 INT_MAX (2^31 − 1) 的时候返回 INT_MAX,结果小于 INT_MIN (−2^31) 的时候返回 INT_MIN 。 这题目看起来并不难,我上手一通操作提交之后,连错了三次,尴尬。。。
以下是可能碰到的部分容易出错的情况:
越界,这个字符串数值可能远远超过了32位整型所能表示的大小,甚至超过了64位。所以不要试图用 long long 来存储最终算出来的数值再跟 INT_MAX 比较,应该在计算过程中随时跟 INT_MAX 发现超过了,就可以停止计算,直接返回相应结果,这样既节省时间,又不会造成 long long 越界; 字符串以多个+-号开头,比如 “+–1234”。从第二个符号开始,并不是数字,那么这个字符串就是非法的,返回 0; 数值字符串和非数字字符中间可能并没有空格,不要被 Example 误导了,比如 “12AB”结果是 12; +-号出现在字符串的中间,比如 “34+1”。这里的+号只能当做普通的非数字字符处理,因此结果返回 34; Solution class Solution { public: int myAtoi(string str) { str.erase(0, str.find_first_not_of(' ')); int last_pos = 0; for(last_pos = 0; last_pos < str....
最近在倒腾 pyspider,不得不说这个爬虫框架用起来真的很方便。从编写调试到部署一条龙服务, 对于我这种 Scrapy 苦手来说,简直就是救星。另外 pyspider 还提供了 Docker 镜像,通过 Docker 部署省去了安装依赖的麻烦。不过我实际根据官方文档尝试 Docker Compose 部署的时候还是遇到了一点小麻烦,搜索折腾了一番之后总算弄好了,这里做个记录。
ImportError: No module named MySQLdb 执行 docker-compose up 的时候,报了这个错误,解决方法是安装 MySQL-python。
# Dockerfile
FROM binux/pyspider:latest
RUN pip install MySQL-python
这里大家可以自己通过上面的 Dockerfile build 一个新的镜像,或者也可以直接使用我上传的镜像kuma1430/pyspider。然后把官方的 docker-compose.yml 里的 binux/pyspider 换成你创建的镜像名或者 kuma1430/pyspider。
Authentication plugin ‘caching_sha2_password’ cannot be loaded 通过上面安装 MySQL-python,我们解决了找不到 MySQLdb 的问题,但是新的问题出现了:
sqlalchemy.exc.OperationalError: (_mysql_exceptions.OperationalError) (2059, "Authentication plugin 'caching_sha2_password' cannot be loaded: /usr/lib/mysql/plugin/caching_sha2_password.so: cannot open shared object file: No such file or directory") (Background on this error at: http://sqlalche....
安装 在 Ubuntu 下可以通过 apt-get 安装:
sudo apt-get install nginx 安装完成后 nginx 会自动启动,这时候可以通过浏览器访问 80 端口。不出意外的话,就可以看到「Welcome to nginx!」的提示信息。
启动、关闭与重载配置 执行 nginx 命令可以直接启动 ngxin。如果 nginx 已经在运行,那么可能会出现端绑定错误提示。或者,nginx 默认的 80 端口被别的程序占用,那么也会出现这个错误。
在 nginx 已经在运行的情况下,可以通过 -s 参数对 nginx 进行控制:
nginx -s signal 执行 nginx -s quit 或者 nginx -s stop 关闭 ngxin 服务。其中 quit 和 stop 的区别在于:quit 是平滑关闭,它会等待工作进程完成当前的请求之后再关闭进程,而 stop 则是强制关闭。
在对 nginx 的配置文件进行修改之后,需要执行 nginx -s reload 命令来充值配置文件,使修改生效。
配置 nginx 默认的配置文件是 /etc/nginx/nginx.conf。在这个文件中我们可以对 nginx 进行各种参数的配置。
这个文件里有 include /etc/nginx/sites-enabled/*; 这样一句话。这说明在这里加载了额外的配置文件。在 sites-enable 文件夹里只有一个 default 文件。它其实是一个软链接,指向了 /etc/nginx/sites-available/default。default 去掉无关信息之后的如下:...