Giter VIP home page Giter VIP logo

blog's Introduction

👋 Hello, I'm Allan Chain

I joined GitHub on 16 February 2018. I contributed to 35 repositories (excluding my own) and made 4528 commits.

You can contact me via $\left[\mathbf{matrix}\right]$ at @allanchain:kde.org. You can also find me in the Fediverse at [email protected].

I'm a fan of Python and JavaScript/TypeScript! 👇

I'm using Neovim on Linux. A total of 29.4 hours of programming time were recorded last week, with an average of 4.9 hours per day. 👇

This file was generated with lowlighter/[email protected] on 28 May 2024, 11:00:41 (Asia/Shanghai). And private contributions and achievements are deliberately excluded.

blog's People

Contributors

allanchain avatar dependabot[bot] avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

blog's Issues

WTFs in C

View Post on Blog

为了备战变态的计算概论考试, 不得已要研究一下 C 的奇奇怪怪的东西


UPDATE: 看起来今年的计概考试并没有类似往年题一样的未定义行为出现,可以说今年收敛了一些。故这里说,zy 和 chj 比起来,还是排雷 chj 😁

除非有说明,均在 gcc 下测试

Nice websites

往年题?

#include <stdio.h>
#define f(x) -x*x+
int main()
{
    int i,j,x,n,k;
    x=1;
    n=2;
    k=3;
    i=-f(-x+n)-k;
    j=--x+n*-x+n+-k;
    
    printf("%d\n%d\n",i,j);
}

其中,gcc -E认为的是

i=- - -x+n*-x+n+-k;

注意到 C 的运算符处理和空格有关,故结果为

-4
-1

而 VC 认为的是

i=- --x+n*-x+n+-k;

注意到第二句时 x 已自减,故结果为

-1
0

h(x) != g(x) ?

#include <stdio.h>
#define f(a,b) a##b
#define g(a)   #a
#define h(a) g(a)

int main()
{
  printf("%s\n",h(f(1,2)));
  printf("%s\n",g(f(1,2)));
  return 0;
}

https://stackoverflow.com/a/4368983

A single '#' will create a string from the given argument, regardless of what that argument contains, while the double '##' will create a new token by concatenating the arguments.


An occurrence of a parameter in a function-like macro, unless it is the operand of # or ##, is expanded before substituting it and rescanning the whole for further expansion. Because g's parameter is the operand of #, the argument is not expanded but instead immediately stringified ("f(1,2)"). Because h's parameter is not the operand of # nor ##, the argument is first expanded (12), then substituted (g(12)), then rescanning and further expansion occurs ("12").

也就是说,碰到###后,宏就不会递归展开

No print?

#include<stdio.h>

#define TOTAL_ELEMENTS (sizeof(array) / sizeof(array[0]))
int array[] = {23,34,12,17,204,99,16};

int main()
{
  int d;

  for(d=-1;d <= (TOTAL_ELEMENTS-2);d++)
      printf("%d\n",array[d+1]);

  return 0;
}

这是非常神奇的, 但是和宏没什么关系。

首先,sizeof返回的是 unsigned long int, dint 类型,这两个比较的时候会比较迷,现行的是 unsigned preserving 策略,即把 int 变成 unsigned int,再行比较。

于是TOTAL_ELEMENTS-2还是unsigned long int,而 -1 就变成巨大无比,条件自然就不可能成立了。

所以呢,把TOTAL_ELEMENTS换成由unsigned int a=7;定义的a,还是一样的。

还有一点有意思的是:

https://stackoverflow.com/questions/2084949

On a platform with 32bit int with e.g.

int x = -1;
unsigned y = 0xffffffff;

the expression x == y would yield 1 because through the "usual arithmetic conversions" the value of x is converted to unsigned and thus to 0xffffffff.

The expression (unsigned int)x == y is 1 as well. The only difference is that you do the conversion explicitly with a cast.

The expression x == (int)y will most likely be 1 as well because converting 0xffffffff to int yields -1 on most platforms (two's complement negatives). Strictly speaking this is implementation-defined behavior and thus might vary on different platforms.


Safest is to check that the number is in range before casting:

if (x >= 0 && ((unsigned int)x) == y)

continue in do...while

#include<stdio.h>

enum {false,true};

int main()
{
    int i=1;
    do {
        printf("%d\n",i);
        i++;
        if(i < 15)
            continue;
    }while(false);
    return 0;
}

跟在后面的while也是循环控制的一种,所以continue不会跳过while

拓展知识:how for equals while?

Why does it tend to get into an infinite loop if I use continue in a while loop, but works fine in a for loop?
The loop-counter increment i++ gets ignored in while loop if I use it after continue, but it works if it is in for loop.

If continue ignores subsequent statements, then why doesn't it ignore the third statement of the for loop then, which contains the counter increment i++? Isn't the third statement of for loop subsequent to continue as well and should be ignored, given the third statement of for loop is executed after the loop body?

while(i<10)   //causes infinite loop
{
    ...
    continue
    i++
    ...
}

for(i=0;i<10;i++)  //works fine and exits after 10 iterations
{
    ...
    continue
    ...
}

The reason is because the continue statement will short-circuit the statements that follow it in the loop body. Since the way you wrote the while loop has the increment statement following the continue statement, it gets short-circuited. You can solve this by changing your while loop.

A lot of text books claim that:

for (i = 0; i < N; ++i) {
    /*...*/
}

is equivalent to:

i = 0;
while (i < N) {
    /*...*/
    ++i;
}

But, in reality, it is really like:

j = 0;
while ((i = j++) < N) {
    /*...*/
}

Or, to be a little more pedantic:

i = 0;
if (i < 10) do {
    /*...*/
} while (++i, (i < 10));

These are more equivalent, since now if the body of the while has a continue, the increment still occurs, just like in a for. The latter alternative only executes the increment after the iteration has completed, just like for (the former executes the increment before the iteration, deferring to save it in i until after the iteration).

著名的变态题

#include<stdio.h>

int main() {
    int i = 1;
    printf("%d", ++i+ ++i+ ++i);
}

著名的 ALE(bushi) 如是说:

operation on 'i' may be undefined

Stackoverflow 如是说

https://stackoverflow.com/a/2989771/8810271

Modifying the value of i more than once without a sequence point in between the modifications results in undefined behavior. So, the results of your code are undefined.

简单翻译成人话就是,想一口气多次改变变量值的行为是未定义的。所以考试考一个未定义的东西有什么用呢?

所以“往年题”部分提到的例子也是未定义的。

讲道理说,不同主流编译器在同样的计算机上,使用相同的合法的程序,产生不同的结果,要么是编译器的附加功能,要么就是未定义的运算了吧!

这只是普通的标签

#include<stdio.h>
int main()
{
    int a=10;
    switch(a)
    {
        case '1':
            printf("ONE\n");
            break;
        case '2':
            printf("TWO\n");
            break;
        defau1t:
            printf("NONE\n");
    }
    return 0;
}

坑就在deefaultl打成了1😂

居然编译器给过了!?

原来假的defau1t被处理成了一个标签供goto使用,casedefault也类似于 C 里的标签,共用类似的语法,毕竟case本身就可以直接跳到任何地方,包括if里面。

case真的可以随便跳吗?

#include<stdio.h>
int main()
{
    int a=1;
    switch(a)
    {   int b=20;
        case 1: printf("b is %d\n",b);
                break;
        default:printf("b is %d\n",b);
                break;
    }
    return 0;
}

跳过了变量初始化就会报错了,编译都过不了

Showcase

View Post on Blog

测试一下 Markdown 渲染的效果 网页的 CSS


Note

Useful information that users should know, even when skimming content.

Warning

Urgent info that needs immediate user attention to avoid problems.

Hello!

Yes!

😂 🐶

print('hello world!')

really awesome!

  • Can
  • I
  • nest
even('write').code ? ah : ha

code in head of course!

code with unspecified language
or code with indent

More complex heading level!

A very very very very very very very very very very very very very very very very very very very very very very very very very very very very very long code

And more!

Paragraph

Small Headers

Paragraph

Should Be Visible

Another paragraph

Inline $\frac{1}{2 \pi i}$

$$ \int_{- \infty}^{\infty} \frac{1}{x} \mathrm d x $$

Jupyter on Raspberry Pi

View Post on Blog

This post was originally created at 2020-02-26T20:20:29+08:00


Start Jupyter as service

From https://gist.github.com/whophil/5a2eab328d2f8c16bb31c9ceaf23164f

/etc/systemd/system/jupyter.service:

# After Ubuntu 16.04, Systemd becomes the default.
# It is simpler than https://gist.github.com/Doowon/38910829898a6624ce4ed554f082c4dd

[Unit]
Description=Jupyter Notebook

[Service]
Type=simple
PIDFile=/run/jupyter.pid
ExecStart=jupyter-notebook --config=/home/pi/.jupyter/jupyter_notebook_config.py
User=pi
Group=pi
WorkingDirectory=/home/pi/Notebooks/
Restart=always
RestartSec=10
#KillMode=mixed

[Install]
WantedBy=multi-user.target

To use, just do it in systemctl way:

sudo systemctl start jupyter
sudo systemctl stop jupyter
# To launch while start up
sudo systemctl enable jupyter

NOTICE

run systemctl daemon-reload often when changing unit file. Or it will silently use the old bad file.

不要问我是怎么知道的

libf77blas.so.3: cannot open shared object file

sudo apt install libatlas-base-dev

Config Jupyter

Follow https://jupyter-notebook.readthedocs.io/en/stable/public_server.html

Typical Way

jupyter notebook --generate-config
# Get passwd hash
python3 -c 'from notebook.auth import passwd;print(passwd())'
# cd to a nice directory
# where `nodes` means `no-des`, no passwd for private key
openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365 -sha256 -nodes

And here is the official example for the configuration file:

# Set options for certfile, ip, password, and toggle off
# browser auto-opening
c.NotebookApp.certfile = '/absolute/path/to/your/certificate/fullchain.pem'
c.NotebookApp.keyfile = '/absolute/path/to/your/certificate/privkey.pem'
# Set ip to '*' to bind on all interfaces (ips) for the public server
c.NotebookApp.ip = '*'
c.NotebookApp.password = 'sha1:bcd259ccf...<your hashed password here>'
c.NotebookApp.open_browser = False
# It is a good idea to set a known, fixed port for server access
c.NotebookApp.port = 9999

⚠️ ​IMPORTANT

There are so many keys in the file, be sure to modify the correct key.

Listen to ipv6 Only

c.NotebookApp.ip = '::'

Jupyter Themes

Installation

pip3 install jupyterthemes

Configuration

jt -t oceans16 -T -N -fs 16 -nfs 16 -cellw 90%

Explanation:

-T, --toolbar         make toolbar visible
-N, --nbname          nb name/logo visible
-t THEME              theme name to install
-fs MONOSIZE          code font-size
-nfs NBFONTSIZE       notebook fontsize
-tfs TCFONTSIZE       txtcell fontsize
-dfs DFFONTSIZE       pandas dataframe fontsize
-ofs OUTFONTSIZE      output area fontsize
-mathfs MATHFONTSIZE  mathjax fontsize (in %)
-cellw CELLWIDTH      set cell width (px or %)

Font sizes are in pt

Behind Nginx

Reference: https://gist.github.com/christopherbaek/39e6c432e212ca7a67ffe015fe869664

  • Disable SSL and bind IP back to localhost, add base_url
c.NotebookApp.base_url = 'jupyter'
c.NotebookApp.ip = '127.0.0.1'
c.NotebookApp.open_browser = False
c.NotebookApp.password = 'sha1:84cb...36f7e6'
c.NotebookApp.port = 9999
  • Config Nginx, headers are important, slash is also important
location /jupyter/ {
    proxy_pass http://127.0.0.1:9999/jupyter/;

    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-NginX-Proxy true;

    # WebSocket support
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_set_header Origin "";
}

Lucky GitHub Number

View Post on Blog

Record the lucky GitHub contribution number.


screenshot

1234! That's cool!

You can easily notice June the separator.

Update: after some repo operation, the number has changed.

Deploying Hugo with CircleCI

View Post on Blog

This post was originally created at 2019-10-27T20:12:30+08:00

Building blog sites manually is tedious and often leads to mistakes, especially when hosting on master/docs


Think about when you happily update the blog post and every thing looks fine by hugo server, you just typed git add, git commit, git push without a second thought, only to find that you forgot to build the site to docs directory. And you git add, git commit, git push again, finding it a pain to reinvent a fancy commit message. Let's get rid of this!

Deploying Hugo with CircleCI: Glance

There is a good article, but is for version 2 and heavily used && which is not beautiful in YAML.

Version 2.1

Here is the snippet, where workflows is at root level:

workflows:
  main:
    jobs:
      - deploy:
          filters:
            branches:
              only: master

Beautiful YAML

Something like this:

      - run:
          name: Update Submodules
          command: |
            git submodule sync
            git submodule update --init
      - run:
          name: Set up Repo
          command: |
            git worktree add -B gh-pages ../public origin/gh-pages
            git config user.email "[email protected]"
            git config user.name "ci-build"

Don't forget to set SSH keys with push access

Just follow the official documentation

Other things to know

Tell CircleCI to do nothing on gh-pages

You probably needs to first manually checkout an orphan branch gh-pages and add the .circleci/config.yml in to let CircleCI know that this branch shall not be deployed.

just:

git checkout --orphan gh-pages
# And add that config.yml
git add .circleci/config.yml
git push -u origin gh-pages

Set up timezone

If you want to include time information in the commit, you well probably find that TZ=xx/xx does not work. That's because the image does not contain the necessary package. Just install it:

      - run:
          name: Install tzdata
          command: |
            apt-get install tzdata

Finally my config file

version: 2.1
jobs:
  deploy:
    working_directory: ~/repo/blog
    environment:
      TZ: Asia/Shanghai
    docker:
        - image: cibuilds/hugo:latest
    steps:
      - checkout
      - add_ssh_keys:
          fingerprints:
            - '8f:11:02:f3:3e:35:37:d7:17:4d:e4:1e:6b:e8:f6:db'
      - run:
          name: Update Submodules
          command: |
            git submodule sync
            git submodule update --init
      - run:
          name: Set up Repo
          command: |
            git worktree add -B gh-pages ../public origin/gh-pages
            git config user.email "[email protected]"
            git config user.name "ci-build"
      - run:
          name: Build with Hugo
          command: |
            HUGO_ENV=production hugo -v -d ../public
      - run:
          name: Install tzdata
          command: |
            apt-get install tzdata
      - run:
          name: Deploy to gh-pages
          command: |
            cd ../public
            git add --all
            git commit -m "Site build at $(date '+%F %T')"
            git push
workflows:
  main:
    jobs:
      - deploy:
          filters:
            branches:
              only: master

Introduce and Setup Tmux

View Post on Blog

Setup and introduce Tmux so that I can use it


Cheat Sheet

Tmux cheatsheet

Image source is in the image

Enable 256 color vim

From https://superuser.com/a/1459904

First, outside tmux, you need:

export TERM=xterm-256color

in .tmux.conf:

set -g default-terminal "screen-256color"

And don't forget to source config manually:

tmux source-file ~/.tmux.conf

All above make sure tmux have full 256 colors support.

And in your vimrc, add:

if exists("$TMUX")
    set t_Co=256
    set notermguicolors
else
    set termguicolors
endif

Enable True Color for vim

From tmux/tmux#1246 and https://github.com/lifepillar/vim-solarized8#troubleshooting

Basically,

let &t_8f = "\<Esc>[38;2;%lu;%lu;%lum"
let &t_8b = "\<Esc>[48;2;%lu;%lu;%lum"
set termguicolors

will do the trick.

Resize Panes on Keyboard

You may just use mouse though

This assumes that you've hit ctrl + b and : to get to the command prompt

:resize-pane -D (Resizes the current pane down)
:resize-pane -U (Resizes the current pane upward)
:resize-pane -L (Resizes the current pane left)
:resize-pane -R (Resizes the current pane right)
:resize-pane -D 10 (Resizes the current pane down by 10 cells)
:resize-pane -U 10 (Resizes the current pane upward by 10 cells)
:resize-pane -L 10 (Resizes the current pane left by 10 cells)
:resize-pane -R 10 (Resizes the current pane right by 10 cells)

Border Chaos

If I split the panes vertically, the first column of the right pane is overwritten by the border.

chaos

It turns out to be the old problem with MinTTY. As you can see on the top-middle of the screenshot, the border is displayed in full width (2 chars), with introduced the chaos.

To solve it, inspired by mintty/mintty#615, all you need to do is Options → Text → Locale: C (Or any language with sane character width). And the broken vim airline is solved too!

fixed

Tricks about Raspberry Pi

View Post on Blog

Handy tricks when playing with raspberry pi. You should know them.


Get the Temperature

cat /sys/class/thermal/thermal_zone0/temp

Outputs:

29482

And here is bash script to create temperature PS1

if [ -e /sys/class/thermal/thermal_zone0/temp ]; then
    temp=$(cat /sys/class/thermal/thermal_zone0/temp)
    if [ $temp -lt 30000 ]; then color=2
    elif [ $temp -lt 50000 ]; then color=3
    else color=1
    fi
    prompt_section $color "${temp::${#temp}-3}.${temp:${#temp}-3}"
fi

Adding a dot is just mixing bash substrings (${string:offset[:length]}) with bash string lengths (${#string})

vcgencmd

From https://www.raspberrypi.org/documentation/raspbian/applications/vcgencmd.md

To get all commands, use:

vcgencmd commands

Voltage

vcgencmd get_throttled

If voltage is okay, you will get 0x0

Else, say 0x50000, convert it to binary, and see each bit:

19 18 17 16 15 14 13 12 11 10  9  8  7  6  5  4  3  2  1  0
 0  1  0  1  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
Bit Meaning
0 Under-voltage detected
1 Arm frequency capped
2 Currently throttled
3 Soft temperature limit active
16 Under-voltage has occurred
17 Arm frequency capping has occurred
18 Throttling has occurred
19 Soft temperature limit has occurred

Generally, the 3 0s in the middle do not have specific meaning. Just need to care about the first 5 and the last 0.

Screen on / off

When you start the Raspberry Pi with display / monitor / screen on, it will automatically turn on display power. If not, display power will never turn on even if you later connect it to a screen. So it is necessary to control the display power.

vcgencmd display_power 1

1 for on and 0 for off. bare vcgencmd display_power shows current state.

It is possible to specify a certain display ID. For more info, visit the doc above.

Why is EEPROM called "ROM"?

View Post on Blog

This post was originally created at s2019-11-06T08:28:02+08:00


After searching on the Internet, I found interesting facts:

  • ROM: Read-Only Memory. Written at the factory.
  • PROM: Programmable Read-Only Memory but programmable (once) by the user. Really a one-time programmable, forever readable memory. Get it wrong and you dump the chip.
  • EPROM: Eraseable Programmable Read-Only Memory. Usually erased using UV light through a quartz window above the chip. A bit of trouble but very useful.
  • EEPROM: Electrically Erasable Programmable Read-Only Memory. Can be erased or re-written under program control.

And early EEPROM devices could only be erased all together, and programming required conditions very different from those associated with normal operation; consequently, as with PROM/EPROM devices, they were generally used in circuitry which could read but not write them.

So unlike RAM (random access memory) which holds its contents during power cycle and, therefore, behaved more like a ROM.

To conclude, EEPROM was hard to erase and write, and is more often read than written. So it still served more like a ROM.

About the relationship between EEPROM and flash memory

EEPROM is in fact Flash.

EEPROM is an evolution of the older UV-eraseable EPROMs (EEPROM's "EE" stands for "Electrically Eraseable"). However, despite it being an improvement to its old pal, today's EEPROM's way of holding information is the exact same of the flash memory.

The ONLY major difference between the two is the read/write/erase logic.

  • NAND Flash (regular flash)
  • NOR Flash (aka EEPROM)

Reference:

Damning GPG Key

View Post on Blog

Why GPG key keeps annoying me! 整天 Fail 有意思吗?


Read the doc carefully and don't forget to tell git what gpg key to use.


Finally signed commit with success on Windows machine, and I happily did the same on my Ubuntu virtual machine.

However, GitHub said that the commits by my windows machine was unverified but the ones by Ubuntu was verified.

WTH? That's IMPOSSIBLE! I even copied the private keys to windows machine and without luck.

Alright. The email setting was different between two machines and GitHub requires that the email used to commit MUST equals the email (a.k.a. comment) of GPG key.


And today, when I have succeeded in signing many commits in different repos, I failed to sign this repo...

Type:

git config -l

And I saw two user.signingkey there... Interesting ...

One is global and one is local, the local one is introduced in the early age when I configure the GPG key generated by windows locally and forgot to remove it...


Alright, damn GPG again.

When I set up gpg keys on WSL today, odd things happend again:

error: gpg failed to sign the data
fatal: failed to write commit object

> echo "test" | gpg2 --clearsign
gpg: signing failed: Inappropriate ioctl for device
gpg: [stdin]: clear-sign failed: Inappropriate ioctl for device

GPG NEEDS A FOLLISH TTY?!

export GPG_TTY=$(tty)

That solved the problem


Oh, god damn it! The first sign after start up always fail on WSL Ubuntu. Type:

echo "test" | gpg2 --clearsign

again and it shows:

gpg: WARNING: unsafe ownership on homedir '/home/ac/.gnupg'
gpg: can't connect to the agent: IPC connect call failed
gpg: can't connect to the agent: IPC connect call failed
gpg: keydb_search failed: No agent running
gpg: no default secret key: No agent running
gpg: [stdin]: clear-sign failed: No agent running

No agent running. Just need to enable gpg-agent on start up:

echo 'eval $(gpg-agent --daemon 2>/dev/null)' >> ~/.bashrc

To fix unsafe ownership, run:

sudo chown -R $USER:$USER ~/.gnupg
sudo find ~/.gnupg -type d -exec chmod 700 {} \;
sudo find ~/.gnupg -type f -exec chmod 600 {} \;

Reference:

我为什么要选 Flask?

View Post on Blog

This post was originally created at 2020-01-03T21:00:00+08:00

注意,这里不是说 Flask 有多好,而是。。用 Flask 用到怀疑人生!


不错,我又开始后悔了 😳 :

  • Jinja2, inspired by django
  • WTForm, looks like django
  • Flask-SQLAlchemy, even more like django
  • Tons of extensions but many are not in active development.
  • Lack of documentations, and existing documentation is not friendly towards newcomers.
    • Because of the confusing namespace and they don't show the import statement.

但是django怎么样呢?(以前用过的感受)

  • 比较死板的项目目录
  • 迷惑的views.py
  • 庞大的体积
  • 以及 redirect 后一直没弄懂的数目不定的斜杠
  • 据说还有一堆super().__init__(self)

既然选择了 Flask,那就要一直做下去。于是,被逼之下,解锁新技能——面向 GitHub 编程

  • GitHub 搜索代码片段
  • 转到 Code 搜索结果
  • 选择 Python 语言
  • 虽然搜索结果并不很准,但第一页总有较好结果的,而且这些代码应该都能跑起来的

UPDATE

用了一段时间后的感受又不一样了。

正如西方文化选读的老师讲的,基督教认为,上帝给了你自由意志,让你用自由意志去服从上帝。

Flask 和 Django 关系大抵类似。Flask 给了你很自由的编程方式,允许你任意组织项目结构、决定使用什么库。但是呢,Django 的模式还是一样的香,ORM 就是好用,没有像样的 template 引擎就是不行,表单验证就是需要简化,一个功能分一个模块,模块下 route 和 database 分开也很舒服。总之,Django 选择这样的模式还是很有道理的(不然也不会有那么多星对吧)

但是呢?你还是不会选择完全模仿 Django。Flask 有自己的装饰器实现的 route,因为业务需求,也会把所有涉及数据库的函数全部封装放到一个类里。而且 Django 使用的类的黑魔法也有些反人类。使用 Flask 还是有着更多的掌控感。

也许所谓上帝自由意志也是如此,虽然给了你自由意志,但是也像 Flask 一样,总归有新教徒,面向同一件事,各自有着自己的实现方式。

所以,Flask or Django 的实质还是那句话,你是想要一把榔头,从五金市场拿到所需的材料,构建合适的项目,就像自己理解圣经并不断探zhe索teng呢,还是要一个开箱即用的工具箱,拥有 drop-in experience,就像直接接受流行教派的**搭上救赎的快车呢?

Maybe, solutions for problems in Flask are open to the whole world, but solutions for problems Django are confined in the Cathollic church world.

However, Django's documentation and community are mature enough.

All in all, Flask or Django is your own choice.

声明:本文扯上基督教只是因为课上讲到并有感而发,并非黑,而且对基督教**还停留在比较浅薄的地步,如有错误轻喷

谜之 Hugo

View Post on Blog

This post was originally created at 2020-01-03T21:19:16+08:00


UPDATE: Hugo 是什么垃圾?现已弃坑。

对新手不友好的 Hugo

在之前 PKUCard 操作之后加了几个 Github 好友,发现很多(显然是主观感受)都在用 Hugo,也感觉之前用的 Mkdocs 不是像样的博客,早有换掉的意思。于是近期就开始上手试了一下。

说 Hugo 之前先说一下 Python 的 Pelican。Pelican 也是一个博客写作应用,但是因为用户量不够,主题样式太少,并且主题的功能也不健全,(说白了就是不合胃口,)于是就很难受。

不用 Hugo 不知道,用了才发现原来 Markdown 渲染可以有这么快。但是当我按照教程,选了一个主题,按照提示一步一步来的时候。。?!为什么是白页面?怎么还是白的?把配置文件全部照抄还不行?错误提示都是虚假的!令人抓狂

无奈只好变成全部复制下来,再进行改动。。

从 v0.55 到 v0.59 一直有 Bug 的 Hugo

Source block following a plain list ends up in the last plain list item #556

kaushalmodi commented [on 2 Aug]
Hello,

Here's a minimal example to reproduce the issue:

  • item 1
  • item 2
This block should be out of <ul>...</ul>
And also out of blockquote

Ref: https://discourse.gohugo.io/t/possible-regression-in-v0-55-5-regarding-lists-containing-code-blocks/18502/4?u=kaushalmodi

/cc @aignas as you seem to be the last person to work on this Blackfriday plain-list/code block issue :)

来,你瞅瞅,现在还是这个样子的:sob:

v0.60.0 UPDATE: 见“新的引擎”一节

这么不稳定的 Markdown 引擎,没法和 Python 比 😏

仿佛急冰的 toc 功能

Hugo 自带的目录功能简直了,因为只能从 h1 开始,否则:

      • Header 3
      • Header 3

自带的功能,老兄!这种事情都做得出来!NOT PYTHONIC AT ALL!

最后我找到了https://gist.github.com/skyzyx/a796d66f6a124f057f3374eff0b3f99a @looeee 的代码,基本可以满足需求。

修改找到的 TOC 代码

如果你要更改标题级别的范围,可以把[2-4]替换成你想要的。

而且这位老兄的代码就是会把标题里的一些诸如don't的标点干掉,也不会保留加粗的格式。

解决办法就是去掉planify | htmlEscape, 并在

{{ $cleanedID := replace (replace $id "id=\"" "") "\"" "" }}

后加入

{{- $header := replaceRE "<h[2-4].*?>((.|\n])+?)</h[2-4]>" "$1" $header -}}

谜之 Template 语法

看了 Hugo 的之后这才发现 Jinja 原来是真的漂亮简洁

就问你and (ConditionA) (ConditionB) 是人话吗?没有not是什么?没有括号也叫 Function?怕是 Shell 用多了?

not的正确打开方式:

{{ if eq (reflect.IsMap site.Params.address) false }}

的确。。反人类

奇奇怪怪的 Toml

放着好好的 YAML 和 JSON 不用,突然冒出来一个 TOML。好像 Markdown 前面加 YAML 已经很常用了,然后跟我说用 TOML,不觉得等于号 有点 丑?不觉得 引号 有点 丑?虽然在做配置文件方面可能有独到之处,对不起,Vim 原生不支持,一键秒杀颜值。

新的引擎

突然,Hugo 给我换了一个引擎 https://github.com/gohugoio/hugo/releases/tag/v0.60.0

使用了新的引擎之后之前的问题消失了,但是变成了默认忽略 HTML,需要在config.toml里做修改

[markup.goldmark.renderer]
    unsafe = true

或者config.yml

markup:
  goldmark:
    renderer:
      unsafe: true

新引擎,新bug

headerID

Goldmark 在设置 headerID 的时候,默认是仅保留字母和数字,但是这对 CJK 及其不友好。“更有甚者”,该项目的作者是日本人,竟然不考虑对本国文字的支持?!还说,仅保留字母和数字是合适的默认行为。Excuse me? 不应该原封不动保留 Unicode 才是好的默认行为吗?

据说会在 Hugo v0.63.0 解决,那就拭目以待吧。。

render_link

本来说好可以支持 link 的 Markdown hook 的,但是呢,只支持[text](url)型,不支持<url>型。。[摊手.jpg]

而且,说好引用其他 Markdown 文档的 render_link 实现居然还有点复杂。。还好找到了官方实现,不然又是大坑。。

Hugo theme,注定又是一个面向 GitHub 编程。

TOC 新回复

我又针对 toc 写了一篇博客 hugo-toc,然后有去原来的 gist 上评论了,受到了@looeee 的回复。他说。。。他已经弃坑 Hugo 了,使用 js 解决问题。

我。。。

Setup Vim Airline (in Termux)

View Post on Blog

This post was originally created at 2019-02-09


Vim airline is a tool to enhance the looking of vim. And it is a little tricky to set up, especially in termux.

As I did not install any package manager for vim (in fact, I failed to install and set up Vundle), I need to clone the repo to ~/.vim/pack/dist/start/ to let vim automatically load the package. And I see the airline when I opened vim.

By the way, all packages I tested (NERDTree, indentLine, gitgutter) all support this kind of operation.

Trouble Shooting

Only mode and filename is displayed!

0

For termux disable the auto truncate of section A,B,C and Z is perfect. That is, let other sections truncate at a larger width than your screen.

let g:airline#extensions#default#section_truncate_width = {
    \ 'warning': 80,
    \ 'error': 80,
    \ 'x': 80,
    \ 'y': 80}

Note: Only when nocompatible set can you use \ to continue line.

1

No fancy > s there!

Enable fancy fonts!

let g:airline_powerline_fonts = 1

2

There are dots following the mode!

Set the name of the modes!

let g:airline_mode_map = {
    \ 'c': 'C',
    \ 'n': 'N',
    \ 'V': 'V',
    \ 'i':'I'}

3

I do not need the section Z so long!

Set the custom section z!

let g:airline_section_z = '%2l/%L☰%2v'

4

What if I need a tab bar?

Enable it and set the good style!

let g:airline#extensions#tabline#enabled=1
let g:airline#extensions#tabline#formatter = 'unique_tail'

5

The word count is annoying!

Disable it!

let g:airline#extensions#wordcount#enabled = 0

6

Fancy style?

Yes Badwolf!

let g:airline_theme = 'badwolf'

7
8

Do not want extra > s?

Disable it!

let g:airline_skip_empty_sections = 1

9

MySQL 存储 Emoji

View Post on Blog

This post was originally created at 2019-12-23T22:21:24+08:00

最近需要使用 SQLAlchemy 存弹幕的内容,但是遇到了存 emoji 的问题。


UPDATE: See https://docs.sqlalchemy.org/en/13/dialects/mysql.html#unicode for more info

utf8_bin?

一开始就套用存储中文姓名的那一套,使用utf8_bin的 collation,觉得 utf8 这种万能的东西直接用就行了。可谁知给我报错:

mysql.connector.errors.DatabaseError: 1366 (HY000): Incorrect string value: '\xE8\x86\x9C' for column 'text' at row 1

蛤?竟有如此操作?

utf8mb4_unicode_ci?

一波搜索之后就看到了使用utf8mb4_unicode_ci的 collation。于是就写:

text = db.Column(db.Unicode(256, collation='utf8mb4_unicode_ci'))

可惜并没有实质性效果

暴力二进制

utf8 解决不了,二进制存储总行了吧?

果然,存进去并没有问题。但是当要读取出来的时候又报了奇怪的错误:

TypeError: string argument without an encoding

明明是二进制怎么给我搞出来一个编码问题?

后期查阅资料发现,SQL 似乎有一个动态类型,每列的数据类型是建议值,并不强制。其结果就是我存一个二进制编码的字符串,他就真的以为是字符类型,并且数据库表里没有存编码,导致了问题出现。

疾病设计:angry:!!!

直接escape

二进制也不行,全 escape 总可以了吧!

>>> text.encode('unicode_escape').decode()
>>> text.decode('unicode_escape').encode()

总算成功存储和读取了!

人人都说utf8mb4_unicode_ci

为什么就是不行呢?

哦,还要改数据库 charset, collation

ALTER DATABASE databasename CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
ALTER TABLE tablename CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

还要给 SQLAlchemy 加 ?charset=utf8mb4

Edit: 使用 SQLALCHEMY_ENGINE_OPTIONS.connect_args 不应该加 ?charset=utf8mb4,加完之后会优先认 ?charset=utf8mb4,导致 collation 默认 utf8mb4_0900_ai_ci (which is MySQL 8.0's default but not implemented in MySQL 5.7)

可是为什么还是不行?

mysql.connector.errors.DatabaseError: 1273 (HY000): Unknown collation: 'utf8mb4_0900_ai_ci'

我几几年用过这编码?

版本大坑

我找到了mysql-connector-python的官方文档:https://dev.mysql.com/doc/connector-python/en/connector-python-connectargs.html

collation 一栏中:

Argument Name Default Description
collation utf8mb4_general_ai_ci (is utf8_general_ci in 2.x Which MySQL collation to use. The 8.x default values are generated from the latest MySQL Server 8.0 defaults.

什么?8.0 的默认值?我们只有 5.7 呢!

虽然并不是utf8mb4_0900_ai_ci这种东西,我还是试着把链接数据库参数修改成utf8mb4_unicode_ci

SQLALCHEMY_ENGINE_OPTIONS:
    connect_args:
        collation: utf8mb4_unicode_ci

噫!成功了!

事实证明utf8mb4_0900_ai_ciutf8mb4_general_ai_ci应该是同义词一样的存在。

可是:

MySQL Connector/Python 8.0 is highly recommended for use with MySQL Server 8.0, 5.7, 5.6, and 5.5. Please upgrade to MySQL Connector/Python 8.0.

好一个recommended,就给我挖这种坑?版本问题害死人!

TOC in Hugo

View Post on Blog

Building a table of contents (TOC) in Hugo is challenging. Here I record my attempts to build my TOC correctly. It might be outdated.


As mentioned in the previous blog about Hugo (wtf-hugo), The built-in Toc feature is very inconvenient. And recently, I find that the code I found on the Internet is not perfect. It just ensures enough end tags are rendered, And do not support where the first header is not the biggest. And I tried to fix them all.

First Attempt

It is easy to render all the start tags, and the code I copied is correctly dealing with start tags, so it wouldn't be covered here.

I tried to remove some of the unnecessary end tags. The errors by htmlhint were less, but still some: some markdown files have some empty level of headers, which are not handled correctly, such as:

  • Header 1
      • Header 3
<ul>
<li>Header 1
<ul><ul>
<li>Header 3
</li>
</ul>
</ul></li>
</ul>

But normally, it should be:

  • Header 1
    • Header 2
      • Header 3
<ul>
<li>Header 1
<ul>
<li>Header 2
<ul>
<li>Header 3
</li>
</ul></li>
</ul></li>
</ul>

By looking into it carefully, you would find that when dealing with the end tags, first render </li>, and render </ul></li> per loop fits the usual case, but if some level is skipped, you should just render </ul>

Second Attempt

I use a variable to record how many blank level previous indents made, and render the corresponding number of </ul> when dedenting, and the rest will be </ul></li>

But this is still not enough, what if your dedent is less than the blank level, or in other words, you don't actually need to close all these blank levels, such as:

  • Header 1
        • Header 4
      • Header 3
<ul>
<li>Header 1
<ul><ul><ul>
<li>Header 4
</li>
</ul>
<li>Header 3
</li>
</ul>
</ul></li>
</ul>

If you think that you could use a variable to record the number of previous blank levels, and subtract some from it in every dedent until it becomes 0, take a look at this example:

  • Header 1
      • Header 3
          • Header 5
        • Header 4
  • Header 1
<ul>
<li>Header 1
<ul><ul>
<li>Header 3
<ul><ul>
<li>Header 5
</li>
</ul>
<li>Header 4
</li>
</ul></li>
</ul>
</ul>
<li>Header 1
</li>
</ul>

Third Attempt

I need a stack to record what levels are left blank, and just render </ul> if closing that level, and pop that level out of the stack. Else, I will render </ul></li>

But unfortunately, Hugo does not have a stack implementation, so I have to build a wheel.

Luckily, Hugo has Scratch, which supports:

  • Create an entry
    • $.Scratch.Set "key" slice
  • Add to an entry
    • $.Scratch.Add "key" 2
  • Get all of an entry
    • $.Scratch.Get "key"
  • Delete the whole key
    • $.Scratch.Delete "key"
  • In the value
    • if in ($.Scratch.Get "key") .

That's just enough for making a stack, the only tedious part is poping item, and I did this the hard way:

{{- $tmp := $.Scratch.Get "bareul" -}}
{{- $.Scratch.Delete "bareul" -}}
{{- $.Scratch.Set "bareul" slice}}
{{- range seq (sub (len $tmp) 1) -}}
  {{- $.Scratch.Add "bareul" (index $tmp (sub . 1)) -}}
{{- end -}}

Note that in Hugo, seq is 1-based, but index is 0-based 😂

Besides, {{ seq $a [1] $b}} only supports auto detect increase or decrease, which means at least one element will be generated, and you cannot force a seq not to be executed by using {{ seq $a 1 $b}} if by chance $b is smaller than $a😭. But bare {{ seq $a }} will do nothing if $a is 0.

As a result, manually add and sub will be inevitable...

Ultimate Solution

The discussion above did not cover the tricks to handle the start of the TOC and the end of it. But it is a simple trick if you understand what I mentioned in the Third Attempt Section.

        • Header 4
      • Header 3
  • Header 1

Just make a loop to find the biggest header, render the correct number of <ul>s before, and record blank

{{- $largest := 6 -}}
{{- range $headers -}}
  {{- $headerLevel := index (findRE "[1-4]" . 1) 0 -}}
  {{- $headerLevel := len (seq $headerLevel) -}}
  {{- if lt $headerLevel $largest -}}
    {{- $largest = $headerLevel -}}
  {{- end -}}
{{- end -}}

{{- $firstHeaderLevel := len (seq (index (findRE "[1-4]" (index $headers 0) 1) 0)) -}}

{{- $.Scratch.Set "bareul" slice -}}
<div id="TableOfContents">
<ul>
  {{- range seq (sub $firstHeaderLevel $largest) -}}
    <ul>
    {{- $.Scratch.Add "bareul" (sub (add $largest .) 1) -}}
  {{- end -}}
  {{/* ... */}}

As for the end of TOC, just do the same closing from the last header to the biggest header.

So, here is the full code:

{{- $headers := findRE "<h[1-4].*?>(.|\n])+?</h[1-4]>" .Content -}}
{{- $has_headers := ge (len $headers) 1 -}}
{{- if $has_headers -}}

{{- $largest := 6 -}}
{{- range $headers -}}
  {{- $headerLevel := index (findRE "[1-4]" . 1) 0 -}}
  {{- $headerLevel := len (seq $headerLevel) -}}
  {{- if lt $headerLevel $largest -}}
    {{- $largest = $headerLevel -}}
  {{- end -}}
{{- end -}}

{{- $firstHeaderLevel := len (seq (index (findRE "[1-4]" (index $headers 0) 1) 0)) -}}

{{- $.Scratch.Set "bareul" slice -}}
<ul>
  {{- range seq (sub $firstHeaderLevel $largest) -}}
    <ul>
    {{- $.Scratch.Add "bareul" (sub (add $largest .) 1) -}}
  {{- end -}}
  {{- range $i, $header := $headers -}}
    {{- $headerLevel := index (findRE "[1-4]" . 1) 0 -}}
    {{- $headerLevel := len (seq $headerLevel) -}}

    {{/* get id="xyz" */}}
    {{ $id := index (findRE "(id=\"(.*?)\")" $header 9) 0 }}

    {{/* strip id="" to leave xyz (no way to get regex capturing groups in hugo :( */}}
    {{ $cleanedID := replace (replace $id "id=\"" "") "\"" "" }}
    {{- $header := replaceRE "<h[1-4].*?>((.|\n])+?)</h[1-4]>" "$1" $header -}}

    {{- if ne $i 0 -}}
      {{- $prevHeaderLevel := index (findRE "[1-4]" (index $headers (sub $i 1)) 1) 0 -}}
      {{- $prevHeaderLevel := len (seq $prevHeaderLevel) -}}
        {{- if gt $headerLevel $prevHeaderLevel -}}
          {{- range seq $prevHeaderLevel (sub $headerLevel 1) -}}
            <ul>
            {{/* the first should not be recorded */}}
            {{- if ne $prevHeaderLevel . -}}
              {{- $.Scratch.Add "bareul" . -}}
            {{- end -}}
          {{- end -}}
        {{- else -}}
          </li>
          {{- if lt $headerLevel $prevHeaderLevel -}}
            {{- range seq (sub $prevHeaderLevel 1) -1 $headerLevel -}}
              {{- if in ($.Scratch.Get "bareul") . -}}
                </ul>
                {{/* manually do pop item */}}
                {{- $tmp := $.Scratch.Get "bareul" -}}
                {{- $.Scratch.Delete "bareul" -}}
                {{- $.Scratch.Set "bareul" slice}}
                {{- range seq (sub (len $tmp) 1) -}}
                  {{- $.Scratch.Add "bareul" (index $tmp (sub . 1)) -}}
                {{- end -}}
              {{- else -}}
                </ul></li>
              {{- end -}}
            {{- end -}}
          {{- end -}}
        {{- end -}}
        <li>
          <a href="#{{- $cleanedID  -}}">{{- $header | safeHTML -}}</a>
    {{- else -}}
    <li>
      <a href="#{{- $cleanedID -}}">{{- $header | safeHTML -}}</a>
    {{- end -}}
  {{- end -}}
  {{ $firstHeaderLevel := $largest }}
  {{- $lastHeaderLevel := len (seq (index (findRE "[1-4]" (index $headers (sub (len $headers) 1)) 1) 0)) -}}
  </li>
  {{- range seq (sub $lastHeaderLevel $firstHeaderLevel) -}}
    {{- if in ($.Scratch.Get "bareul") (add . $firstHeaderLevel) -}}
      </ul>
    {{- else -}}
      </ul></li>
    {{- end -}}
  {{- end -}}
</ul>
{{- end -}}

JQuery Solution

And I tried to implement this using JS, that's indeed much simpler. Besides, I can do many cool stuffs using JS!

function createToC() {
  let primaryHeading = 6;
  let headings = [];
  $("main :header").each(
    (index, header) => {
      let level = header.tagName.slice(-1);
      if(level < primaryHeading) primaryHeading = level;
      headings.push({
        level: level,
        id: header.id,
        title: header.innerHTML
      });
    }
  );
  let root = $(document.createElement('ul'))
    .appendTo($("#toc"));
  let parents = [root];
  let prevLevel = primaryHeading;
  let parentIndex = 0;
  headings.forEach(
    (heading, index) => {
      if (heading.level < prevLevel)
        parentIndex -= prevLevel - heading.level;
      else
        for (let i=prevLevel; i < heading.level; i++, parentIndex++)
          parents[parentIndex + 1] = $(document.createElement('ul'))
            .appendTo(parents[parentIndex]);
      prevLevel = heading.level;
      $(document.createElement('a'))
        .attr("href", "#" + heading.id)
        .html(heading.title)
        .appendTo($(document.createElement('li'))
          .appendTo(parents[parentIndex]));
    }
  );
}

C Review

View Post on Blog

计概基础知识复习手记


函数

函数不能嵌套定义

函数改变指针所指变量

调用函数不可能改变实参指针变量的值,但可以改变实参指针变量所指变量的值。

swap(int *p1,int *p2)
{
    int *p;
    p=p1; p1=p2; p2=p;
}

swap(int *p1,int *p2)
{
    int t;
    t=*p1; *p1=*p2; *p2=t;
}

的区别

++

a++错,a是数组首地址,是常量,不能++。

函数p中a++正确,a其实为指针变量
main()中a++错,数组头地址为常数

指针初始化

swap(int *p1,int *p2)
{
    int *temp;
    *temp=*p1;
    *p1=*p2;
    *p2=temp;
}

多维数组与指针

一点提示

&a[1]只是地址的一种计算方法,不要简单理解为a[1]的物理地址,因不存在变量a[1]。

理解指针-数组传递

void average(float *p, int n);
void search(float (*p)[4], int n);
main()
{
    float score[3][4] = {{65, 67, 70, 60},
                         {80, 87, 90, 81},
                         {90, 99, 100, 98}};
    average(*score, 12);
    search(score, 2);
}

score 看成一维数组,
*score就是获得该数组的第一个元素。
由于二维数组,第一个元素还是数组。
average(*score,12)就相当于传一个数组进去。
float *p相符。

float (*p)[4]是 a pointer to array of 4 float,
score本身一维数组就等同于指针,指向第一个数组元素。
故这两个是相符的。

指针与字符串

常量区?

有家可归的字符串视为一般变量,直接呆在“家”里(栈 stack),无家可归的字符串常量呆在常量区(全局、静态区 data)。

img

Image from https://www.geeksforgeeks.org/memory-layout-of-c-program/

有无static相同

char *day_name(int n)
{
    static char *name[] = {
        "Illegal day", "Monday",
        "Tuesday", "Wednesday",
        "Thursday", "Friday",
        "Saturday", "Sunday"};
    return((n<1||n>7) ? name[0] : name[n]);
}

有无static不相同,有:可正确打印;无:打印值有时不对

char *day_name(int n)
{
    static char name[][20] = {
        "Illegal day", "Monday",
        "Tuesday", "Wednesday",
        "Thursday", "Friday",
        "Saturday", "Sunday"};
    return ((n < 1 || n > 7) ? name[0] : name[n]);
}

对字符指针及字符数组赋初值

char *a="I love China!";
/* == */
char *a;
a="I love China";

char str[14]="I love China!";
/* != */
char str[14];
str = "I love China!"; 
/* 错误:将‘const char [1]’赋值给‘char [14]’时类型不兼容 */

位运算

交换两个值,不用临时变量

a = a ^ b;
b = b ^ a;
a = a ^ b;
/*
b = b ^ (a ^ b) = a ^ b ^ b = a ^ 0 = a
a = (a ^ b) ^ a = a ^ a ^ b = 0 ^ b = b
*/

Change Win10 CMD Fonts

View Post on Blog

This post was originally created at 2020-01-03T15:39:10+08:00

It took me quite a lot time to try to solve this...

Note: this answer only works in non-English regions


Based on: https://answers.microsoft.com/en-us/windows/forum/windows_10-start/how-to-customize-consoles-fonts-in-windows-10/f257d215-66db-4186-86cc-2e0b5056b50c

Introduction to the Problem

I have tried for a long time to change to nice font in CMD. After following the tutorials ( GUI version and non-GUI version) on the web and enabling the fonts in the registry, strange things happened:

  • PowerShell recognizes the installed font, and can remember as default, but when running programs, it will switch to default ugly font
  • CMD recognizes the font only when I right click and choose "Default" (默认值), but not "Properties" (属性)

Root of the Problem

That's the Code Page. I have not heard of this before, but I do know cp-936 encoding stuff when configuring gVim, that is short for Code Page 936 (ANSI/OEM - 简体中文 GBK).

Check it Out

When you right click and click "Option" (选项) tab, it will show you the current code page.

In CMD, you can use chcp 65001 to change Unicode code page. Now right click again and go to "Properties", fonts enabled should be there.

Solutions

Here are two ways to go:

It looks like you have installed the Simplified Chinese version of Windows. Do you need to display Chinese text in the console?

If not, and if you don't run legacy Chinese apps that use code page 936 text encoding instead of Unicode, then one option for you is to change the "Language for non-Unicode programs" setting to any language other than Chinese, Japanese or Korean.

To change that setting:

  • Press Windows key + R to open the Run dialog.
  • Enter "intl.cpl" (without the quotation marks) and press Enter. This will option the Region control panel dialog.
  • Click on the Administrative tab.
  • In the "Language for non-Unicode programs" section, click the button, Change system locale...

But unfortunately:

I am sorry that I have to run some legacy Chinese apps which still use non-Unicode encoding.

So here is another way, choose Beta: Use Unicode UTF-8 for i18n, in the prompt said above, and click to reboot.

intl.cpl

You will see that CMD is using UFT-8 now, and CMD recognizes those fonts! Enjoy programing!

Still Problem

PowerShell still changes to cp 936 when executing commands or running programs 🤔...

Working with tmux and SSH

View Post on Blog

Useful tips when SSH in remote device which uses tmux


How to automatically start tmux on SSH session?

From https://stackoverflow.com/a/40192494/8810271

Just put it in server-side .bashrc

if [[ -n "$PS1" ]] && [[ -z "$TMUX" ]] && [[ -n "$SSH_CONNECTION" ]]; then
  tmux attach-session -t ssh_$USER || tmux new-session -s ssh_$USER
fi

Detach from tmux session and close SSH session with 1 command

From https://unix.stackexchange.com/a/546831

Just type

tmux detach -P

Copy Content Inside tmux

  1. enter copy mode using c-b [
  2. navigate to beginning of text, you want to select and hit C-Space
  3. move around using arrow keys to select region
  4. when you reach end of region simply hit M-w to copy the region
  5. now c-b ] will paste the selection

Copy over SSH with MinTTY

Options → Selection → Allow setting selection

And copy as usual in tmux.

If inside vim, enter copy mode first by hitting c-b [

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.