├── .gitignore
├── LICENSE
├── README.md
├── _version.py
├── command.py
├── how.py
├── requirements.txt
└── setup.py
/.gitignore:
--------------------------------------------------------------------------------
1 | # Byte-compiled / optimized / DLL files
2 | __pycache__/
3 | *.py[cod]
4 | *$py.class
5 |
6 | # C extensions
7 | *.so
8 |
9 | # Distribution / packaging
10 | .Python
11 | .idea
12 | .vscode
13 | build/
14 | develop-eggs/
15 | dist/
16 | downloads/
17 | eggs/
18 | .eggs/
19 | lib/
20 | lib64/
21 | parts/
22 | sdist/
23 | var/
24 | wheels/
25 | *.egg-info/
26 | .installed.cfg
27 | *.egg
28 | MANIFEST
29 |
30 | # PyInstaller
31 | # Usually these files are written by a python script from a template
32 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
33 | *.manifest
34 | *.spec
35 |
36 | # Installer logs
37 | pip-log.txt
38 | pip-delete-this-directory.txt
39 |
40 | # Unit test / coverage reports
41 | htmlcov/
42 | .tox/
43 | .coverage
44 | .coverage.*
45 | .cache
46 | nosetests.xml
47 | coverage.xml
48 | *.cover
49 | .hypothesis/
50 |
51 | # Translations
52 | *.mo
53 | *.pot
54 |
55 | # Django stuff:
56 | *.log
57 | .static_storage/
58 | .media/
59 | local_settings.py
60 |
61 | # Flask stuff:
62 | instance/
63 | .webassets-cache
64 |
65 | # Scrapy stuff:
66 | .scrapy
67 |
68 | # Sphinx documentation
69 | docs/_build/
70 |
71 | # PyBuilder
72 | target/
73 |
74 | # Jupyter Notebook
75 | .ipynb_checkpoints
76 |
77 | # pyenv
78 | .python-version
79 |
80 | # celery beat schedule file
81 | celerybeat-schedule
82 |
83 | # SageMath parsed files
84 | *.sage.py
85 |
86 | # Environments
87 | .env
88 | .venv
89 | env/
90 | venv/
91 | ENV/
92 | env.bak/
93 | venv.bak/
94 |
95 | # Spyder project settings
96 | .spyderproject
97 | .spyproject
98 |
99 | # Rope project settings
100 | .ropeproject
101 |
102 | # mkdocs documentation
103 | /site
104 |
105 | # mypy
106 | .mypy_cache/
107 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019~now chenjiandongx
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | 📝 how
6 |
7 | Impressive Linux commands cheat sheet.
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 | ### 💡 IDEA
23 |
24 | Linux 是每位开发者必备的技能,如何高效地掌握 Linux 命令就成为一件很重要的事了。[jaywcjlove/linux-command](https://github.com/jaywcjlove/linux-command) 项目收集和整理了 500+ 的 Linux 命令使用文档,不过缺少了一个命令行版本,`how` 决定来填补这个空缺。
25 |
26 | * Golang 版本: [chenjiandongx/pls](https://github.com/chenjiandongx/pls)
27 |
28 | ### 🔰 安装
29 |
30 | **pip 安装**
31 | ```bash
32 | $ pip install how
33 | ```
34 |
35 | **源码安装**
36 | ```bash
37 | $ git clone https://github.com/chenjiandongx/how.git
38 | $ cd how
39 | $ pip install -r requirements.txt
40 | $ python setup.py install
41 | ```
42 |
43 | ### 📏 使用
44 |
45 | ```bash
46 | $ how
47 | usage: how [-h] [-i] [-v] [COMMAND [COMMAND ...]]
48 |
49 | Lovely Linux commands cheat sheet.
50 |
51 | positional arguments:
52 | COMMAND the puzzling command
53 |
54 | optional arguments:
55 | -h, --help show this help message and exit
56 | -i, --init initialize all commands
57 | -v, --version displays the current version of `how`
58 | ```
59 |
60 | > Note: 建议第一次使用 `how` 时先初始化所有的命令文档,`how -i`,该命令会将 https://github.com/jaywcjlove/linux-command 的 .md 文档下载到 `~/.command` 本地路径下。不过这个操作不是必须的,因为如果 `how some-command` 在本地路径中查询不到的话,会尝试先向远程地址下载。
61 |
62 | ### 🔖 示例
63 |
64 | 初始化所有文档,同时也是更新所有文档的命令
65 | ```shell
66 | $ how -i
67 | Initializing commands: 96/562
68 | ```
69 |
70 | 查询如何使用 `man` 命令
71 | ```shell
72 | $ how man
73 | # man
74 |
75 | 查看 Linux 中的指令帮助
76 |
77 | ## 补充说明
78 |
79 | man 命令 是 Linux 下的帮助指令,通过 man 指令可以查看
80 | Linux 中的指令帮助、配置文件帮助和编程帮助等信息。
81 |
82 | ### 语法
83 |
84 | man(选项)(参数)
85 |
86 | ### 选项
87 |
88 | -a:在所有的 man 帮助手册中搜索;
89 | -f:等价于 whatis 指令,显示给定关键字的简短描述信息;
90 | -P:指定内容时使用分页程序;
91 | -M:指定 man 手册搜索的路径。
92 |
93 | ### 参数
94 |
95 | - 数字:指定从哪本 man 手册中搜索帮助;
96 | - 关键字:指定要搜索帮助的关键字。
97 |
98 | ### 数字代表内容
99 |
100 | 1:用户在 shell 环境可操作的命令或执行文件;
101 | 2:系统内核可调用的函数与工具等
102 | 3:一些常用的函数(function) 与函数库(library),大部分为 C
103 | 的函数库(libc)
104 | 4:设备文件说明,通常在/dev 下的文件
105 | 5:配置文件或某些文件格式
106 | 6:游戏(games)
107 | 7:惯例与协议等,如 Linux 文件系统,网络协议,ASCII code
108 | 等说明
109 | 8:系统管理员可用的管理命令
110 | 9:跟 kernel 有关的文件
111 |
112 | ### 实例
113 |
114 | 我们输入 man ls,它会在最左上角显示“LS(1)”,在这里,“LS”表示手
115 | 册名称,而“(1)”表示该手册位于第一节章,同样,我们输 man
116 | ifconfig
117 | 它会在最左上角显示“IFCONFIG(8)”。也可以这样输入命令:“man
118 | [章节号] 手册名称”。
119 |
120 | man 是按照手册的章节号的顺序进行搜索的,比如:
121 |
122 | man sleep
123 |
124 | 只会显示 sleep 命令的手册,如果想查看库函数 sleep,就要输入:
125 |
126 | man 3 sleep
127 | ```
128 |
129 | ### 📅 Changelog
130 |
131 | #### V0.1.1 - 2020-05-11
132 |
133 | * Updated: 更新命令列表
134 |
135 | #### V0.1.0 - 2019-07-28
136 |
137 | * Alpha: 第一个正式版发布
138 |
139 | ### 📃 LICENSE
140 |
141 | MIT [©chenjiandongx](https://github.com/chenjiandongx)
142 |
--------------------------------------------------------------------------------
/_version.py:
--------------------------------------------------------------------------------
1 | __version__ = "0.1.1"
2 | __author__ = "chenjiandongx"
3 |
--------------------------------------------------------------------------------
/command.py:
--------------------------------------------------------------------------------
1 | import requests
2 |
3 | COMMAND = [
4 | "ab",
5 | "accept",
6 | "ack",
7 | "alias",
8 | "apachectl",
9 | "apk",
10 | "apropos",
11 | "apt-get",
12 | "apt-key",
13 | "apt-sortpkgs",
14 | "aptitude",
15 | "ar",
16 | "arch",
17 | "arj",
18 | "arp",
19 | "arpd",
20 | "arping",
21 | "arptables",
22 | "arpwatch",
23 | "as",
24 | "at",
25 | "atop",
26 | "atq",
27 | "atrm",
28 | "awk",
29 | "axel",
30 | "badblocks",
31 | "basename",
32 | "batch",
33 | "bc",
34 | "bg",
35 | "bind",
36 | "blkid",
37 | "blockdev",
38 | "bmodinfo",
39 | "break",
40 | "builtin",
41 | "bunzip2",
42 | "bye",
43 | "bzcat",
44 | "bzcmp",
45 | "bzdiff",
46 | "bzgrep",
47 | "bzip2",
48 | "bzip2recover",
49 | "bzless",
50 | "bzmore",
51 | "cal",
52 | "cancel",
53 | "cat",
54 | "cd",
55 | "cdrecord",
56 | "chage",
57 | "chattr",
58 | "chcon",
59 | "chfn",
60 | "chgrp",
61 | "chkconfig",
62 | "chmod",
63 | "chown",
64 | "chpasswd",
65 | "chroot",
66 | "chsh",
67 | "cksum",
68 | "clear",
69 | "clock",
70 | "clockdiff",
71 | "cmp",
72 | "col",
73 | "colrm",
74 | "comm",
75 | "command",
76 | "compress",
77 | "consoletype",
78 | "continue",
79 | "convertquota",
80 | "cp",
81 | "cpio",
82 | "crontab",
83 | "csplit",
84 | "cu",
85 | "cupsdisable",
86 | "cupsenable",
87 | "curl",
88 | "cut",
89 | "date",
90 | "dd",
91 | "declare",
92 | "depmod",
93 | "df",
94 | "dhclient",
95 | "dhcpd",
96 | "dhcrelay",
97 | "diff",
98 | "diff3",
99 | "diffstat",
100 | "dig",
101 | "dircolors",
102 | "dirname",
103 | "dirs",
104 | "disown",
105 | "dmesg",
106 | "dmidecode",
107 | "dnf",
108 | "dnsdomainname",
109 | "domainname",
110 | "dos2unix",
111 | "dpkg-deb",
112 | "dpkg-divert",
113 | "dpkg-preconfigure",
114 | "dpkg-query",
115 | "dpkg-reconfigure",
116 | "dpkg-split",
117 | "dpkg-statoverride",
118 | "dpkg-trigger",
119 | "dpkg",
120 | "dris",
121 | "dstat",
122 | "du",
123 | "dump",
124 | "e2fsck",
125 | "e2label",
126 | "echo",
127 | "ed",
128 | "edquota",
129 | "egrep",
130 | "eject",
131 | "elinks",
132 | "elm",
133 | "emacs",
134 | "enable",
135 | "env",
136 | "ethtool",
137 | "ex",
138 | "exec",
139 | "exit",
140 | "expand",
141 | "export",
142 | "exportfs",
143 | "expr",
144 | "false",
145 | "fc",
146 | "fdisk",
147 | "fg",
148 | "fgrep",
149 | "file",
150 | "find",
151 | "findfs",
152 | "finger",
153 | "firewall-cmd",
154 | "fishshell",
155 | "fmt",
156 | "fold",
157 | "fping",
158 | "free",
159 | "fsck",
160 | "ftp",
161 | "ftpcount",
162 | "ftpshut",
163 | "ftptop",
164 | "ftpwho",
165 | "fuser",
166 | "gcc",
167 | "gcov",
168 | "gdb",
169 | "get_module",
170 | "getenforce",
171 | "getsebool",
172 | "git",
173 | "gpasswd",
174 | "gpm",
175 | "grep",
176 | "groupadd",
177 | "groupdel",
178 | "groupmod",
179 | "groups",
180 | "grpck",
181 | "grpconv",
182 | "grpunconv",
183 | "grub",
184 | "gunzip",
185 | "gzexe",
186 | "gzip",
187 | "halt",
188 | "hdparm",
189 | "head",
190 | "help",
191 | "hexdump",
192 | "history",
193 | "host",
194 | "hostid",
195 | "hostname",
196 | "hping3",
197 | "htdigest",
198 | "htop",
199 | "htpasswd",
200 | "hwclock",
201 | "iconv",
202 | "id",
203 | "ifcfg",
204 | "ifconfig",
205 | "ifdown",
206 | "ifstat",
207 | "iftop",
208 | "ifup",
209 | "indent",
210 | "info",
211 | "init",
212 | "inotifywait",
213 | "insmod",
214 | "install",
215 | "iostat",
216 | "iotop",
217 | "ip",
218 | "ip6tables-restore",
219 | "ip6tables-save",
220 | "ip6tables",
221 | "ipcalc",
222 | "ipcrm",
223 | "ipcs",
224 | "iperf",
225 | "iptables-restore",
226 | "iptables-save",
227 | "iptables",
228 | "iptraf",
229 | "iptstate",
230 | "ispell",
231 | "jed",
232 | "jobs",
233 | "joe",
234 | "join",
235 | "jwhois",
236 | "kernelversion",
237 | "kexec",
238 | "kill",
239 | "killall",
240 | "last",
241 | "lastb",
242 | "lastlog",
243 | "ld",
244 | "ldconfig",
245 | "ldd",
246 | "less",
247 | "let",
248 | "lftp",
249 | "lftpget",
250 | "lha",
251 | "lilo",
252 | "ln",
253 | "lnstat",
254 | "local",
255 | "locate",
256 | "logger",
257 | "login",
258 | "logname",
259 | "logout",
260 | "logrotate",
261 | "logsave",
262 | "logwatch",
263 | "look",
264 | "losetup",
265 | "lp",
266 | "lpadmin",
267 | "lpc",
268 | "lpq",
269 | "lpr",
270 | "lprm",
271 | "lpstat",
272 | "ls",
273 | "lsattr",
274 | "lsb_release",
275 | "lsblk",
276 | "lscpu",
277 | "lsmod",
278 | "lsof",
279 | "lspci",
280 | "lsusb",
281 | "ltrace",
282 | "lvcreate",
283 | "lvdisplay",
284 | "lvextend",
285 | "lvreduce",
286 | "lvremove",
287 | "lvresize",
288 | "lvscan",
289 | "lynx",
290 | "mail",
291 | "mailq",
292 | "mailstat",
293 | "make",
294 | "man",
295 | "mapfile",
296 | "md5sum",
297 | "mesg",
298 | "mii-tool",
299 | "mkbootdisk",
300 | "mkdir",
301 | "mke2fs",
302 | "mkfs",
303 | "mkinitrd",
304 | "mkisofs",
305 | "mknod",
306 | "mkswap",
307 | "mktemp",
308 | "modprobe",
309 | "more",
310 | "mount",
311 | "mpstat",
312 | "mtools",
313 | "mv",
314 | "mysql",
315 | "mysqladmin",
316 | "mysqldump",
317 | "mysqlimport",
318 | "mysqlshow",
319 | "named-checkzone",
320 | "nano",
321 | "nc",
322 | "ncftp",
323 | "nethogs",
324 | "netstat",
325 | "newusers",
326 | "nfsstat",
327 | "ngrep",
328 | "nice",
329 | "nisdomainname",
330 | "nl",
331 | "nm",
332 | "nmap",
333 | "nmcli",
334 | "nohup",
335 | "nologin",
336 | "nslookup",
337 | "ntpdate",
338 | "ntsysv",
339 | "objdump",
340 | "od",
341 | "openssl",
342 | "parted",
343 | "partprobe",
344 | "passwd",
345 | "paste",
346 | "patch",
347 | "pathchk",
348 | "perl",
349 | "pfctl",
350 | "pgrep",
351 | "php",
352 | "pico",
353 | "pidof",
354 | "pigz",
355 | "ping",
356 | "pkill",
357 | "pmap",
358 | "popd",
359 | "poweroff",
360 | "ppp-off",
361 | "pr",
362 | "printf",
363 | "protoize",
364 | "ps",
365 | "pssh",
366 | "pstack",
367 | "pstree",
368 | "pushd",
369 | "pv",
370 | "pvchange",
371 | "pvck",
372 | "pvcreate",
373 | "pvdisplay",
374 | "pvremove",
375 | "pvs",
376 | "pvscan",
377 | "pwck",
378 | "pwconv",
379 | "pwd",
380 | "pwunconv",
381 | "quota",
382 | "quotacheck",
383 | "quotaoff",
384 | "quotaon",
385 | "rcconf",
386 | "rcp",
387 | "read",
388 | "readelf",
389 | "readonly",
390 | "reboot",
391 | "reject",
392 | "rename",
393 | "renice",
394 | "repquota",
395 | "resize",
396 | "restore",
397 | "restorecon",
398 | "return",
399 | "rev",
400 | "rexec",
401 | "rlogin",
402 | "rm",
403 | "rmdir",
404 | "rmmod",
405 | "route",
406 | "rpm",
407 | "rpm2cpio",
408 | "rpmbuild",
409 | "rpmdb",
410 | "rpmquery",
411 | "rpmsign",
412 | "rpmverify",
413 | "rsh",
414 | "rsync",
415 | "runlevel",
416 | "sar",
417 | "scp",
418 | "screen",
419 | "sed",
420 | "seinfo",
421 | "semanage",
422 | "sendmail",
423 | "seq",
424 | "service",
425 | "sesearch",
426 | "set",
427 | "setfacl",
428 | "setpci",
429 | "setsebool",
430 | "setsid",
431 | "sftp-server",
432 | "sftp",
433 | "sh",
434 | "shift",
435 | "shopt",
436 | "showmount",
437 | "shuf",
438 | "shutdown",
439 | "skill",
440 | "slabtop",
441 | "sleep",
442 | "slocate",
443 | "smbclient",
444 | "smbpasswd",
445 | "sort",
446 | "source",
447 | "speedtest-cli",
448 | "spell",
449 | "split",
450 | "squid",
451 | "squidclient",
452 | "ss",
453 | "ssh-add",
454 | "ssh-agent",
455 | "ssh-copy-id",
456 | "ssh-keygen",
457 | "ssh-keyscan",
458 | "ssh",
459 | "sshd",
460 | "startx",
461 | "stat",
462 | "strace",
463 | "strings",
464 | "stty",
465 | "su",
466 | "sudo",
467 | "sum",
468 | "supervisord",
469 | "suspend",
470 | "swapoff",
471 | "swapon",
472 | "sync",
473 | "sysctl",
474 | "syslog",
475 | "systemctl",
476 | "systool",
477 | "tac",
478 | "tail",
479 | "tailf",
480 | "talk",
481 | "tar",
482 | "tcpdump",
483 | "tcpreplay",
484 | "tee",
485 | "telint",
486 | "telnet",
487 | "tempfile",
488 | "test",
489 | "tftp",
490 | "time",
491 | "times",
492 | "tload",
493 | "tmux",
494 | "top",
495 | "touch",
496 | "tput",
497 | "tr",
498 | "tracepath",
499 | "traceroute",
500 | "trap",
501 | "tree",
502 | "true",
503 | "tty",
504 | "type",
505 | "ulimit",
506 | "umask",
507 | "umount",
508 | "unalias",
509 | "uname",
510 | "unarj",
511 | "uncompress",
512 | "unexpand",
513 | "uniq",
514 | "unlink",
515 | "unprotoize",
516 | "unrar",
517 | "unset",
518 | "unzip",
519 | "updatedb",
520 | "uptime",
521 | "useradd",
522 | "userdel",
523 | "usermod",
524 | "usernetctl",
525 | "users",
526 | "uucico",
527 | "uupick",
528 | "uuto",
529 | "vdfuse",
530 | "vgchange",
531 | "vgconvert",
532 | "vgcreate",
533 | "vgdisplay",
534 | "vgextend",
535 | "vgreduce",
536 | "vgremove",
537 | "vgrename",
538 | "vgscan",
539 | "vi",
540 | "vmstat",
541 | "volname",
542 | "w",
543 | "wait",
544 | "wall",
545 | "watch",
546 | "wc",
547 | "wget",
548 | "whatis",
549 | "whereis",
550 | "which",
551 | "who",
552 | "whoami",
553 | "write",
554 | "xargs",
555 | "xauth",
556 | "xclip",
557 | "xhost",
558 | "xinit",
559 | "xlsatoms",
560 | "xlsclients",
561 | "xlsfonts",
562 | "xset",
563 | "xz",
564 | "yes",
565 | "ypdomainname",
566 | "yum",
567 | "zcat",
568 | "zfore",
569 | "zip",
570 | "zipinfo",
571 | "zipsplit",
572 | "znew",
573 | ]
574 |
575 |
576 | def _fetch_commands():
577 | url = "https://unpkg.com/linux-command/dist/data.json"
578 | commands = requests.get(url).json()
579 | for cmd in commands:
580 | print(cmd)
581 |
--------------------------------------------------------------------------------
/how.py:
--------------------------------------------------------------------------------
1 | import argparse
2 | import os
3 | import re
4 | import textwrap
5 | from multiprocessing.dummy import Pool
6 |
7 | import commonmark
8 | import huepy
9 | import requests
10 | from tenacity import retry, stop_after_attempt
11 |
12 | from _version import __version__
13 | from command import COMMAND
14 |
15 | TAG_HEADING = "heading"
16 | TAG_ITEM = "item"
17 | TAG_CODE_BLOCK = "code_block"
18 | TAG_LIST = "list"
19 | TAG_PARAGRAPH = "paragraph"
20 | TAG_HTML_BLOCK = "html_block"
21 | TAG_STRONG = "strong"
22 | TAG_TEXT = "text"
23 | TAG_BLOCK_QUOTE = "block_quote"
24 |
25 | EXT = ".md"
26 | MAX_WIDTH = 40
27 | MAX_CONCURRENCY = 8
28 |
29 | COMMAND_DIR = os.path.join(os.path.expanduser("~"), ".command")
30 | FILE_URL = "https://unpkg.com/linux-command/command/{}.md"
31 |
32 |
33 | class Tag:
34 | def __init__(self, t, literal, level):
35 | self.t = t
36 | self.literal = literal
37 | self.level = level
38 |
39 | def __repr__(self):
40 | return "[ ]".format(
41 | self.t, self.literal, self.level
42 | )
43 |
44 |
45 | def docs_need_space(docs):
46 | left = re.compile(r"([a-zA-Z0-9)])([\u4e00-\u9fa5])")
47 | right = re.compile(r"([\u4e00-\u9fa5])([a-zA-Z0-9\[])")
48 | return re.sub(right, r"\1 \2", re.sub(left, r"\1 \2", docs))
49 |
50 |
51 | def wrap_text(text):
52 | return textwrap.fill(text=text, width=MAX_WIDTH) + "\n"
53 |
54 |
55 | def highlight(text: str, keyword: str):
56 | return re.sub(
57 | keyword,
58 | "\33[0m" + "\33[93m" + keyword + "\33[0m" + "\33[37m",
59 | text,
60 | flags=re.IGNORECASE,
61 | )
62 |
63 |
64 | def get_content(command):
65 | file_path = os.path.join(COMMAND_DIR, command + EXT)
66 | if not os.path.exists(file_path):
67 | download_file(command)
68 | if not os.path.exists(file_path):
69 | return ""
70 | with open(file_path, "r", encoding="utf8") as f:
71 | return f.read()
72 |
73 |
74 | def how_to_use(command):
75 | content = get_content(command)
76 | if not content:
77 | print("Sorry: could not find the `{}` command".format(command))
78 | return
79 |
80 | parse = commonmark.Parser()
81 | ast = parse.parse(content)
82 |
83 | tags = []
84 | for obj, entering in ast.walker():
85 | if not entering or obj.t == TAG_HTML_BLOCK:
86 | continue
87 | tags.append(Tag(obj.t, obj.literal, obj.level))
88 |
89 | tag_length, out = len(tags), ""
90 | for i, tag in enumerate(tags):
91 | if i < tag_length - 1:
92 | if tag.t == TAG_HEADING:
93 | tag.literal = huepy.bold("#" * tag.level + " ")
94 | if tag.t == TAG_TEXT:
95 | if tags[i + 1].t in (TAG_PARAGRAPH, TAG_HEADING, TAG_CODE_BLOCK):
96 | tag.literal = tag.literal + "\n" * 2
97 | if tags[i + 1].t == TAG_ITEM:
98 | tag.literal = tag.literal + "\n" + "- "
99 | if tags[i + 1].t == TAG_LIST:
100 | tag.literal = tag.literal + "\n" * 2 + "- "
101 | if tags[i + 1].t == TAG_BLOCK_QUOTE:
102 | tag.literal = tag.literal + "\n" * 2 + "> "
103 | if tag.t == TAG_CODE_BLOCK:
104 | tag.literal = tag.literal + "\n"
105 |
106 | if tag.literal:
107 | out += tag.literal
108 | doc = [wrap_text(d) for d in docs_need_space(out).strip().split("\n")]
109 | print(highlight("".join(doc), command))
110 |
111 |
112 | @retry(stop=stop_after_attempt(3))
113 | def download_file(command):
114 | if not os.path.exists(COMMAND_DIR):
115 | os.makedirs(COMMAND_DIR)
116 |
117 | url = FILE_URL.format(command)
118 | response = requests.get(url)
119 | if response.status_code >= 400:
120 | return
121 |
122 | file_path = os.path.join(COMMAND_DIR, command + EXT)
123 | with open(file_path, "w+", encoding="utf8") as f:
124 | f.write(response.text)
125 |
126 |
127 | cnt = 0
128 |
129 |
130 | def download_file_progress(command):
131 | download_file(command)
132 | global cnt
133 | cnt += 1
134 | print("\rInitializing commands: {}/{} ".format(cnt, len(COMMAND)), end="")
135 |
136 |
137 | def init_command():
138 | # speed up
139 | p = Pool(MAX_CONCURRENCY)
140 | p.map(download_file_progress, COMMAND)
141 | p.close()
142 | p.join()
143 | print()
144 |
145 |
146 | def get_parser():
147 | parser = argparse.ArgumentParser(
148 | description="Impressive Linux commands cheat sheet."
149 | )
150 | parser.add_argument(
151 | "command", metavar="COMMAND", type=str, nargs="*", help="the puzzling command"
152 | )
153 | parser.add_argument(
154 | "-i", "--init", action="store_true", help="initialize all commands"
155 | )
156 | parser.add_argument(
157 | "-v",
158 | "--version",
159 | action="store_true",
160 | help="displays the current version of `how`",
161 | )
162 | return parser
163 |
164 |
165 | def command_line_runner():
166 | parser = get_parser()
167 | args = vars(parser.parse_args())
168 |
169 | command = "".join(args["command"]).lower()
170 |
171 | if args["version"]:
172 | print(huepy.info("how " + __version__))
173 | return
174 |
175 | if args["init"]:
176 | init_command()
177 | return
178 |
179 | if not args["command"]:
180 | parser.print_help()
181 | return
182 |
183 | how_to_use(command)
184 |
185 |
186 | if __name__ == "__main__":
187 | command_line_runner()
188 |
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | commonmark
2 | huepy
3 | requests
4 | tenacity
5 |
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | import os
2 | import sys
3 | from shutil import rmtree
4 |
5 | from setuptools import Command, find_packages, setup
6 |
7 | # $ python setup.py upload
8 |
9 | __title__ = "how"
10 | __description__ = "Impressive Linux commands cheat sheet."
11 | __url__ = "https://github.com/chenjiandongx/how"
12 | __author_email__ = "chenjiandongx@qq.com"
13 | __license__ = "MIT"
14 |
15 | __requires__ = ["commonmark", "huepy", "requests", "tenacity"]
16 | __keywords__ = ["python", "linux-command", "cheat sheet"]
17 | __modules__ = ["how", "command", "_version"]
18 |
19 | # Load the package's _version.py module as a dictionary.
20 | here = os.path.abspath(os.path.dirname(__file__))
21 | about = {}
22 | with open(os.path.join(here, "_version.py")) as f:
23 | exec(f.read(), about)
24 |
25 |
26 | __version__ = about["__version__"]
27 |
28 |
29 | class UploadCommand(Command):
30 | description = "Build and publish the package."
31 | user_options = []
32 |
33 | @staticmethod
34 | def status(s):
35 | print("✨✨ {0}".format(s))
36 |
37 | def initialize_options(self):
38 | pass
39 |
40 | def finalize_options(self):
41 | pass
42 |
43 | def run(self):
44 | try:
45 | self.status("Removing previous builds…")
46 | rmtree(os.path.join(here, "dist"))
47 | rmtree(os.path.join(here, "build"))
48 | rmtree(os.path.join(here, "{0}.egg-info".format(__title__)))
49 | except OSError:
50 | pass
51 |
52 | self.status("Building Source and Wheel distribution…")
53 | os.system("{0} setup.py bdist_wheel".format(sys.executable))
54 |
55 | self.status("Uploading the package to PyPI via Twine…")
56 | os.system("twine upload dist/*")
57 |
58 | self.status("Pushing git tags…")
59 | os.system('git tag -a v{0} -m "release version v{0}"'.format(__version__))
60 | os.system("git push origin v{0}".format(__version__))
61 |
62 | sys.exit()
63 |
64 |
65 | setup(
66 | name=__title__,
67 | version=__version__,
68 | description=__description__,
69 | url=__url__,
70 | author=about["__author__"],
71 | author_email=__author_email__,
72 | license=__license__,
73 | packages=find_packages(exclude=("test",)),
74 | keywords=__keywords__,
75 | py_modules=__modules__,
76 | install_requires=__requires__,
77 | zip_safe=False,
78 | include_package_data=True,
79 | classifiers=[
80 | "Development Status :: 4 - Beta",
81 | "Environment :: Console",
82 | "Intended Audience :: Developers",
83 | "License :: OSI Approved :: MIT License",
84 | "Operating System :: OS Independent",
85 | "Programming Language :: Python",
86 | "Programming Language :: Python :: 3.6",
87 | "Programming Language :: Python :: 3.7",
88 | "Topic :: Software Development :: Libraries",
89 | ],
90 | cmdclass={"upload": UploadCommand},
91 | entry_points={"console_scripts": ["how=how:command_line_runner"]},
92 | )
93 |
--------------------------------------------------------------------------------