├── .gitignore ├── Makefile ├── README.md ├── SUMMARY.md ├── TODO.md ├── book.json ├── cover.jpg ├── cover.svg ├── cover.txt ├── cover.xcf.bz2 ├── cover_small.jpg ├── doc ├── BUILD.md ├── COVER.md ├── PLUGINS.md └── README.md ├── pic ├── Linux_FileSystem_Architecture-150x150.jpg ├── Linux_FileSystem_Architecture-197x300.jpg ├── Linux_FileSystem_Architecture.jpg ├── MBR_Architecture-150x150.jpg ├── MBR_Architecture-300x139.jpg ├── MBR_Architecture.jpg ├── Network_Architecture-150x150.jpg ├── Network_Architecture-300x176.jpg ├── Network_Architecture.jpg ├── Network_Layer_Compare-150x150.jpg ├── Network_Layer_Compare-300x118.jpg ├── Network_Layer_Compare.jpg ├── Network_Layer_OSI-150x150.jpg ├── Network_Layer_OSI-300x221.jpg ├── Network_Layer_OSI.jpg ├── Network_Layer_TCP_IP-118x300.jpg ├── Network_Layer_TCP_IP-150x150.jpg ├── Network_Layer_TCP_IP.jpg ├── UI_Shell_and_GUI-150x150.jpg ├── UI_Shell_and_GUI-300x190.jpg ├── UI_Shell_and_GUI.jpg ├── cover-tinylab.org.png ├── cover.png ├── tinylab-author.jpg ├── tinylab-shop.jpg ├── tinylab-sina.jpg ├── tinylab-sponsor-1.68.jpg ├── tinylab-sponsor-3.69.jpg ├── tinylab-sponsor-6.88.jpg ├── tinylab-sponsor-8.99.jpg ├── tinylab-sponsor-9.68.jpg ├── tinylab-sponsor.jpg ├── tinylab-wechat.jpg ├── tinylab-weixin.jpg └── tinylab-xingqiu.jpg ├── tools ├── .vim.format.rc ├── build-toc.sh ├── convert2gitbook.sh ├── h2m └── makepdf └── zh ├── appendix ├── 02-chapter1.markdown └── pic │ └── mommy-tea.png ├── chapters ├── 01-chapter1.markdown ├── 01-chapter10.markdown ├── 01-chapter2.markdown ├── 01-chapter3.markdown ├── 01-chapter4.markdown ├── 01-chapter5.markdown ├── 01-chapter6.markdown ├── 01-chapter7.markdown ├── 01-chapter8.markdown ├── 01-chapter9.markdown └── pic │ ├── Linux_FileSystem_Architecture-150x150.jpg │ ├── Linux_FileSystem_Architecture-197x300.jpg │ ├── Linux_FileSystem_Architecture.jpg │ ├── MBR_Architecture-150x150.jpg │ ├── MBR_Architecture-300x139.jpg │ ├── MBR_Architecture.jpg │ ├── Network_Architecture-150x150.jpg │ ├── Network_Architecture-300x176.jpg │ ├── Network_Architecture.jpg │ ├── Network_Layer_Compare-150x150.jpg │ ├── Network_Layer_Compare-300x118.jpg │ ├── Network_Layer_Compare.jpg │ ├── Network_Layer_OSI-150x150.jpg │ ├── Network_Layer_OSI-300x221.jpg │ ├── Network_Layer_OSI.jpg │ ├── Network_Layer_TCP_IP-118x300.jpg │ ├── Network_Layer_TCP_IP-150x150.jpg │ ├── Network_Layer_TCP_IP.jpg │ ├── UI_Shell_and_GUI-150x150.jpg │ ├── UI_Shell_and_GUI-300x190.jpg │ ├── UI_Shell_and_GUI.jpg │ ├── cover-tinylab.org.png │ ├── cover.png │ ├── mommy-tea.png │ ├── tinylab-targets-150x110.gif │ ├── tinylab-targets-150x110.jpg │ ├── tinylab-targets-300x54.gif │ ├── tinylab-targets-300x54.jpg │ ├── tinylab-targets.jpg │ ├── tinylab.org-sina-weibo.jpg │ └── tinylab.org-weixin-logo.jpg └── preface ├── 01-chapter0.markdown ├── 01-chapter1.markdown └── pic ├── Linux_FileSystem_Architecture-150x150.jpg ├── Linux_FileSystem_Architecture-197x300.jpg ├── Linux_FileSystem_Architecture.jpg ├── MBR_Architecture-150x150.jpg ├── MBR_Architecture-300x139.jpg ├── MBR_Architecture.jpg ├── Network_Architecture-150x150.jpg ├── Network_Architecture-300x176.jpg ├── Network_Architecture.jpg ├── Network_Layer_Compare-150x150.jpg ├── Network_Layer_Compare-300x118.jpg ├── Network_Layer_Compare.jpg ├── Network_Layer_OSI-150x150.jpg ├── Network_Layer_OSI-300x221.jpg ├── Network_Layer_OSI.jpg ├── Network_Layer_TCP_IP-118x300.jpg ├── Network_Layer_TCP_IP-150x150.jpg ├── Network_Layer_TCP_IP.jpg ├── UI_Shell_and_GUI-150x150.jpg ├── UI_Shell_and_GUI-300x190.jpg ├── UI_Shell_and_GUI.jpg ├── cover-tinylab.org.png ├── cover.png ├── mommy-tea.png ├── tinylab-targets-150x110.gif ├── tinylab-targets-150x110.jpg ├── tinylab-targets-300x54.gif ├── tinylab-targets-300x54.jpg ├── tinylab-targets.jpg ├── tinylab.org-sina-weibo.jpg └── tinylab.org-weixin-logo.jpg /.gitignore: -------------------------------------------------------------------------------- 1 | kaiyuanbook.* 2 | *.pdf 3 | *.swp 4 | *.aux 5 | *.log 6 | *.html 7 | latex/* 8 | !latex/makepdf 9 | !latex/README 10 | !latex/template.tex 11 | !latex/config.yml 12 | epub/temp 13 | *~ 14 | .#* 15 | *.epub 16 | 17 | # Node rules: 18 | ## Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 19 | .grunt 20 | 21 | ## Dependency directory 22 | ## Commenting this out is preferred by some people, see 23 | ## https://docs.npmjs.com/misc/faq#should-i-check-my-node_modules-folder-into-git 24 | node_modules 25 | 26 | # Book build output 27 | _book 28 | 29 | # eBook build output 30 | *.epub 31 | *.mobi 32 | *.pdf 33 | *.html 34 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | all: serve 2 | 3 | html: 4 | gitbook build 5 | 6 | pdf: 7 | gitbook pdf 8 | 9 | serve: 10 | gitbook serve > .gitbook-serve.log 2>&1 & 11 | 12 | view: 13 | chromium-browser http://localhost:4000 >/dev/null 2>&1 & 14 | 15 | read: view 16 | 17 | read-pdf: 18 | chromium-browser book*.pdf >/dev/null 2>&1 & 19 | 20 | read-html: 21 | chromium-browser _book/index.html >/dev/null 2>&1 & 22 | 23 | clean: 24 | @rm -rf _book 25 | 26 | distclean: clean 27 | @rm book*.pdf 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | **关注作者公众号**: 2 |
3 | 4 |
5 | 6 | # Shell 编程范例 7 | 8 | v 0.3 9 | 10 | 不同于传统 Shell 书籍,本书并未花大篇幅去介绍 Shell 语法,而是以面向“对象” 的方式引入大量的实例介绍 Shell 日常操作,“对象” 涵盖数值、逻辑值、字符串、文件、进程、文件系统等。这样有助于学以致用,并在用的过程中提高兴趣。也可以作为 Shell 编程索引,在需要的时候随时检索。 11 | 12 | ## 介绍 13 | 14 | - 项目首页: 15 | - 代码仓库: 16 | - 在线阅读: 17 | 18 | 更多背景和计划请参考:[前言](zh/preface/01-chapter1.markdown)。 19 | 20 | ### 编译 21 | 22 | 要编译本书,请使用 [Markdown Lab](http://tinylab.org/markdown-lab)。 23 | 24 | ### 纠错 25 | 26 | 欢迎大家指出不足,如有任何疑问,请邮件联系 wuzhangjin at gmail dot com 或者直接修复并提交 Pull Request。 27 | 28 | ### 版权 29 | 30 | 本书采用 ![CC BY NC ND 4.0](http://i.creativecommons.org/l/by-nc-nd/4.0/88x31.png) 协议发布,详细版权信息请参考 [CC BY NC ND 4.0](http://creativecommons.org/licenses/by-nc-nd/4.0/)。 31 | 32 | ### 联系作者 33 | 34 | 35 | 36 | ### 赞助作者 37 | 38 | 39 | 40 | ### 更多原创开源书籍 41 | 42 | * [C 语言编程透视](http://tinylab.gitbooks.io/cbook/) 43 | * [嵌入式 Linux 知识库(eLinux.org 中文版)](http://tinylab.gitbooks.io/elinux/) 44 | * [Linux 内核文档(Linux Documentation/ 中文版)](http://tinylab.gitbooks.io/linux-doc/) 45 | -------------------------------------------------------------------------------- /SUMMARY.md: -------------------------------------------------------------------------------- 1 | * [简介](README.md) 2 | * [版本修订历史](zh/preface/01-chapter0.markdown) 3 | * [前言](zh/preface/01-chapter1.markdown) 4 | * [准备工作](zh/chapters/01-chapter1.markdown) 5 | * [数值运算](zh/chapters/01-chapter2.markdown) 6 | * [布尔运算](zh/chapters/01-chapter3.markdown) 7 | * [字符串操作](zh/chapters/01-chapter4.markdown) 8 | * [文件操作](zh/chapters/01-chapter5.markdown) 9 | * [文件系统操作](zh/chapters/01-chapter6.markdown) 10 | * [进程操作](zh/chapters/01-chapter7.markdown) 11 | * [网络操作](zh/chapters/01-chapter8.markdown) 12 | * [用户管理](zh/chapters/01-chapter9.markdown) 13 | * [总结](zh/chapters/01-chapter10.markdown) 14 | * [附录](zh/appendix/02-chapter1.markdown) 15 | -------------------------------------------------------------------------------- /TODO.md: -------------------------------------------------------------------------------- 1 | 2 | 1. Add graphic operation 3 | * Graphic format and conversion 4 | * Data to graphic 5 | * Graphic to Data 6 | * Graphic display 7 | 2. Add audio operation 8 | * Audio format and conversion 9 | * Data to audio 10 | * Audio to data 11 | * Audio play 12 | -------------------------------------------------------------------------------- /book.json: -------------------------------------------------------------------------------- 1 | { 2 | "author": "Wu Zhangjin ", 3 | "description": "这是开源书籍《Shell 编程范例》的 GitBook 版本", 4 | "generator": "site", 5 | "plugins" : [ "disqus", "collapsible-menu"], 6 | "pluginsConfig": { 7 | "disqus": { 8 | "shortName": "tinylab" 9 | } 10 | }, 11 | "links": { 12 | "sidebar": { 13 | "泰晓科技-项目首页": "http://www.tinylab.org/open-shell-book/", 14 | "泰晓学院-视频课堂": "https://m.cctalk.com/inst/sh8qtdag", 15 | "知识星球-Linux频道": "https://t.zsxq.com/uB2vJyF", 16 | "开源小店-相关产品": "https://shop155917374.taobao.com/", 17 | "C 语言编程透视": "http://tinylab.gitbooks.io/cbook" 18 | } 19 | }, 20 | "title": "Shell 编程范例" 21 | } 22 | -------------------------------------------------------------------------------- /cover.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tinyclub/open-shell-book/752ffde2badb9b2291af79208fa2f85d958ca9b1/cover.jpg -------------------------------------------------------------------------------- /cover.svg: -------------------------------------------------------------------------------- 1 | timetesttreesourcemkfsbgbunzipbzipcatcdchattrchmodchownchshcitycpdddeclaredhclientdiffdirdmesgechoevalexitexportexprfactorfalsefgfilefindfunctiongrepgunzipgziphellohelpifconfigjobsjoinkillletlnlocatelslsattrlsmodmanmkdirmkfifo网络mknodmountmvodpasswdpidofpingpspstreereadreadonlyredirectrenicermrmdirroutescreensedseqsleepstatstracestringssusudosynctartoptouchtraptruetypeumountupdatedbwgetwhereisawkBash数值逻辑值字符串文件进程文件系统basename路由求幂余数查询替换插入删除子串比较排序进制编码随机数正则表达式大小权限属性时间创建更新压缩编辑搜索磁盘分区挂载逻辑卷格式化卸载MBR后台管道作业网桥Shell -------------------------------------------------------------------------------- /cover.txt: -------------------------------------------------------------------------------- 1 | awk 2 | basename 3 | bash 4 | bg 5 | bunzip 6 | bzip 7 | cat 8 | cd 9 | chattr 10 | chmod 11 | chown 12 | chsh 13 | city 14 | cp 15 | dd 16 | declare 17 | dhclient 18 | diff 19 | dir 20 | dmesg 21 | echo 22 | eval 23 | exit 24 | export 25 | expr 26 | factor 27 | false 28 | fg 29 | file 30 | find 31 | for 32 | function 33 | grep 34 | gunzip 35 | gzip 36 | hello 37 | help 38 | if 39 | ifconfig 40 | jobs 41 | join 42 | kill 43 | let 44 | ln 45 | locate 46 | ls 47 | lsattr 48 | lsmod 49 | man 50 | mkdir 51 | mkfifo 52 | mkfs 53 | mknod 54 | mount 55 | mv 56 | od 57 | passwd 58 | pidof 59 | ping 60 | ps 61 | pstree 62 | read 63 | readonly 64 | redirect 65 | renice 66 | rm 67 | rmdir 68 | route 69 | screen 70 | sed 71 | seq 72 | sleep 73 | source 74 | stat 75 | strace 76 | strings 77 | su 78 | sudo 79 | sync 80 | tar 81 | test 82 | time 83 | top 84 | touch 85 | trap 86 | tree 87 | true 88 | type 89 | umount 90 | updatedb 91 | wget 92 | whereis 93 | which 94 | while 95 | 数值 96 | 逻辑值 97 | 字符串 98 | 文件 99 | 进程 100 | 文件系统 101 | 网络 102 | 加 103 | 减 104 | 乘 105 | 除 106 | 求幂 107 | 余数 108 | 查询 109 | 替换 110 | 插入 111 | 删除 112 | 子串 113 | 比较 114 | 排序 115 | 进制 116 | 编码 117 | 真 118 | 假 119 | 与 120 | 或 121 | 非 122 | 随机数 123 | 正则表达式 124 | 大小 125 | 权限 126 | 属性 127 | 时间 128 | 创建 129 | 更新 130 | 压缩 131 | 编辑 132 | 搜索 133 | 磁盘 134 | 分区 135 | 挂载 136 | 逻辑卷 137 | 格式化 138 | 卸载 139 | MBR 140 | 后台 141 | 管道 142 | 作业 143 | 网桥 144 | 路由 145 | Shell 146 | Shell 147 | Shell 148 | Bash 149 | -------------------------------------------------------------------------------- /cover.xcf.bz2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tinyclub/open-shell-book/752ffde2badb9b2291af79208fa2f85d958ca9b1/cover.xcf.bz2 -------------------------------------------------------------------------------- /cover_small.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tinyclub/open-shell-book/752ffde2badb9b2291af79208fa2f85d958ca9b1/cover_small.jpg -------------------------------------------------------------------------------- /doc/BUILD.md: -------------------------------------------------------------------------------- 1 | # Introduction 2 | 3 | gitbook is supported, please [install gitbook](http://www.tinylab.org/docker-quick-start-docker-gitbook-writing-a-book/) and simply build it as followiing: 4 | 5 | ## Installation 6 | 7 | $ sudo aptitude install -y retext git nodejs npm 8 | $ sudo ln -fs /usr/bin/nodejs /usr/bin/node 9 | $ sudo aptitude install -y calibre fonts-arphic-gbsn00lp 10 | $ npm config set registry https://registry.npm.taobao.org 11 | $ sudo npm install gitbook-cli -g 12 | 13 | ## Compile 14 | 15 | $ gitbook build 16 | $ gitbook pdf 17 | -------------------------------------------------------------------------------- /doc/COVER.md: -------------------------------------------------------------------------------- 1 | 2 | # Generate the Cover Pictures 3 | 4 | The pic/cover.png is generated by [Word cloud generator](http://www.jasondavies.com/wordcloud/#http%3A%2F%2Ftinylab.org) 5 | -------------------------------------------------------------------------------- /doc/PLUGINS.md: -------------------------------------------------------------------------------- 1 | 2 | # Plugins 3 | 4 | ## Navigation 5 | 6 | * [gitbook-plugin-anchor-navigation](https://github.com/yaneryou/gitbook-plugin-anchor-navigation) 7 | - pdf 生成后,导航在页面底部 8 | - 展示的导航很漂亮 9 | * [gitbook-plugin-maxiang](http://plugins.gitbook.com/plugin/maxiang) 10 | - 只默认支持两级(h2) 11 | - 不为 pdf 生成导航 12 | 13 | * [gitbook-plugin-toc](http://plugins.gitbook.com/plugin/toc) 14 | - 需要修改 Markdown 文件,并且不支持中文,对于 pdf 可能会有一些用。可补充 maxiang(在anchor-navigation修复之前) 15 | 16 | ## Sidebar 17 | 18 | * [tree](http://plugins.gitbook.com/plugin/tree) 19 | - 为侧边栏加树状结构 20 | * [collapsible-menu](http://plugins.gitbook.com/plugin/collapsible-menu) 21 | - 可自动折叠边栏(不同与 tree 一起使用,效果不好) 22 | * [toggle-chapters](http://plugins.gitbook.com/plugin/toggle-chapters) 23 | - 效果同上 24 | * [gitbook-plugin-tocstyles](http://plugins.gitbook.com/plugin/tocstyles) 25 | - 可以设计更多侧边栏的花样,符合中文要求 26 | * [gitbook-plugin-multipart](http://plugins.gitbook.com/plugin/multipart) 27 | - 在 Chapters 之上加 Part I/II 28 | 29 | ## Comments 30 | 31 | * [disqus](http://plugins.gitbook.com/plugin/disqus) 32 | 33 | ## Code 34 | 35 | * [gitbook-plugin-google_code_prettify](http://plugins.gitbook.com/plugin/google_code_prettify) 36 | 37 | ## Chinese 38 | 39 | * [gitbook-plugin-betterchinese](http://plugins.gitbook.com/plugin/betterchinese) 40 | - 本地没有特别效果,需要到 gitbooks.io 上验证 41 | 42 | ## Introduction DIY 43 | 44 | * [diy-introduction](http://plugins.gitbook.com/plugin/diy-introduction) 45 | - 未看到生效? 46 | 47 | ## Exercises 48 | 49 | * [exercises](http://plugins.gitbook.com/plugin/exercises) 50 | - 可交互式的练习(很有意义),gitbook 2.0 因为 markdown 解析 `{%` 出错,导致无法使用。 51 | - `sudo npm install gitbook -g gitbook@1.5.0` 52 | - /usr/lib/node_modules/gitbook/bin/gitbook.js 53 | - http://cowmanchiang.me/gitbook/gitbook/contents/plugin/exercises.html 54 | -------------------------------------------------------------------------------- /doc/README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Shell Programming Series 4 | 5 | - Author: Wu Zhangjin 6 | - Date : Sat Aug 10 02:25:08 CST 2013 7 | 8 | ## Introduction 9 | 10 | 2007 年开始系统地学习 Shell 编程,并在兰大开源社区写了序列文章。 11 | 12 | 这一序列文章对于Shell编程的介绍有自己独特的视角,它把包括逻辑、 13 | 数值、字符串、文件、文件系统、网络等应用对象作为切入点,进行了 14 | 系统而全面的介绍。 15 | 16 | 相关文章在网路上有非常多地转载,说明确实有比较多的读者,为了 17 | 更全面地完整地呈现给读者,这里计划重新全面地整理。 18 | 19 | 在整理完早前内容的同时,加入早期计划做但是还没有做的内容,比如 20 | 说更多的应用对象:图像(一种特殊的文件)、网络、性能优化、不同 21 | Shell 的兼容性等。所有用例建议支持多个Shell。 22 | 23 | 在编写该书的过程中,可以考虑逐步开放一些章节到网路上接收反馈, 24 | 包括 PREFACE 和项目日程以及计划等。 25 | 26 | 也可以考虑前期仅仅针对早前的内容做一个总结和完善,更多内容 27 | 等待后续版本再添加,不过优化部分可以考虑单独列出来。 28 | 29 | ## Outline 30 | 31 | - [Shell编程范例之开篇](http://www.tinylab.org/shell-programming-paradigm-begins-with/) (更新时间:2007-07-21) 32 | - [Shell编程范例之数值运算](http://www.tinylab.org/shell-numeric-calculation/) (更新时间:2007-11-9) 33 | - [Shell编程范例之布尔运算](http://www.tinylab.org/shell-programming-paradigm-of-boolean-operations/) (更新时间:2007-10-30) 34 | - [Shell编程范例之字符串操作](http://www.tinylab.org/shell-programming-paradigm-of-string-manipulation/) (更新时间:2007-11-21) 35 | - [Shell编程范例之文件操作](http://www.tinylab.org/shell-programming-paradigms-of-file-operations/) (更新时间:2007-12-5) 36 | - [Shell编程范例之文件系统操作](http://www.tinylab.org/shell-programming-paradigm-in-file-system-operations/) (更新时间:2007-12-29) 37 | - [Shell编程范例之进程操作](http://www.tinylab.org/shell-programming-paradigm-of-process-operations/) (更新时间:2008-02-22) 38 | - [Shell编程范例之网络操作](http://www.tinylab.org/shell-programming-paradigm-of-network-operations/) (更新时间:2008-04-19) 39 | - [Shell编程范例之总结篇](http://www.tinylab.org/summary-of-shell-programming-paradigm-article/) (更新时间:2008-07-21)
40 | 41 | ## Schedule 42 | 43 | 争取在一个礼拜左右整理完初稿,两个礼拜内整理完成后正式对外发布 Review 版本,一个月内正式发布 0.1 版本。 44 | 45 | 争取在 1.0 版本时参考[PLEAC项目](http://pleac.sourceforge.net/)完成该书的大部分目标,并争取出版社的支持,进行出版。 46 | 47 | ## References 48 | 49 | 相关内容发布在兰大开源社区以及 Blog 中: 50 | 51 | - [Shell 编程范例序列](http://www.tinylab.org/shell-programming-paradigm-series-index-review/) 52 | -------------------------------------------------------------------------------- /pic/Linux_FileSystem_Architecture-150x150.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tinyclub/open-shell-book/752ffde2badb9b2291af79208fa2f85d958ca9b1/pic/Linux_FileSystem_Architecture-150x150.jpg -------------------------------------------------------------------------------- /pic/Linux_FileSystem_Architecture-197x300.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tinyclub/open-shell-book/752ffde2badb9b2291af79208fa2f85d958ca9b1/pic/Linux_FileSystem_Architecture-197x300.jpg -------------------------------------------------------------------------------- /pic/Linux_FileSystem_Architecture.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tinyclub/open-shell-book/752ffde2badb9b2291af79208fa2f85d958ca9b1/pic/Linux_FileSystem_Architecture.jpg -------------------------------------------------------------------------------- /pic/MBR_Architecture-150x150.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tinyclub/open-shell-book/752ffde2badb9b2291af79208fa2f85d958ca9b1/pic/MBR_Architecture-150x150.jpg -------------------------------------------------------------------------------- /pic/MBR_Architecture-300x139.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tinyclub/open-shell-book/752ffde2badb9b2291af79208fa2f85d958ca9b1/pic/MBR_Architecture-300x139.jpg -------------------------------------------------------------------------------- /pic/MBR_Architecture.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tinyclub/open-shell-book/752ffde2badb9b2291af79208fa2f85d958ca9b1/pic/MBR_Architecture.jpg -------------------------------------------------------------------------------- /pic/Network_Architecture-150x150.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tinyclub/open-shell-book/752ffde2badb9b2291af79208fa2f85d958ca9b1/pic/Network_Architecture-150x150.jpg -------------------------------------------------------------------------------- /pic/Network_Architecture-300x176.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tinyclub/open-shell-book/752ffde2badb9b2291af79208fa2f85d958ca9b1/pic/Network_Architecture-300x176.jpg -------------------------------------------------------------------------------- /pic/Network_Architecture.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tinyclub/open-shell-book/752ffde2badb9b2291af79208fa2f85d958ca9b1/pic/Network_Architecture.jpg -------------------------------------------------------------------------------- /pic/Network_Layer_Compare-150x150.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tinyclub/open-shell-book/752ffde2badb9b2291af79208fa2f85d958ca9b1/pic/Network_Layer_Compare-150x150.jpg -------------------------------------------------------------------------------- /pic/Network_Layer_Compare-300x118.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tinyclub/open-shell-book/752ffde2badb9b2291af79208fa2f85d958ca9b1/pic/Network_Layer_Compare-300x118.jpg -------------------------------------------------------------------------------- /pic/Network_Layer_Compare.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tinyclub/open-shell-book/752ffde2badb9b2291af79208fa2f85d958ca9b1/pic/Network_Layer_Compare.jpg -------------------------------------------------------------------------------- /pic/Network_Layer_OSI-150x150.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tinyclub/open-shell-book/752ffde2badb9b2291af79208fa2f85d958ca9b1/pic/Network_Layer_OSI-150x150.jpg -------------------------------------------------------------------------------- /pic/Network_Layer_OSI-300x221.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tinyclub/open-shell-book/752ffde2badb9b2291af79208fa2f85d958ca9b1/pic/Network_Layer_OSI-300x221.jpg -------------------------------------------------------------------------------- /pic/Network_Layer_OSI.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tinyclub/open-shell-book/752ffde2badb9b2291af79208fa2f85d958ca9b1/pic/Network_Layer_OSI.jpg -------------------------------------------------------------------------------- /pic/Network_Layer_TCP_IP-118x300.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tinyclub/open-shell-book/752ffde2badb9b2291af79208fa2f85d958ca9b1/pic/Network_Layer_TCP_IP-118x300.jpg -------------------------------------------------------------------------------- /pic/Network_Layer_TCP_IP-150x150.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tinyclub/open-shell-book/752ffde2badb9b2291af79208fa2f85d958ca9b1/pic/Network_Layer_TCP_IP-150x150.jpg -------------------------------------------------------------------------------- /pic/Network_Layer_TCP_IP.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tinyclub/open-shell-book/752ffde2badb9b2291af79208fa2f85d958ca9b1/pic/Network_Layer_TCP_IP.jpg -------------------------------------------------------------------------------- /pic/UI_Shell_and_GUI-150x150.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tinyclub/open-shell-book/752ffde2badb9b2291af79208fa2f85d958ca9b1/pic/UI_Shell_and_GUI-150x150.jpg -------------------------------------------------------------------------------- /pic/UI_Shell_and_GUI-300x190.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tinyclub/open-shell-book/752ffde2badb9b2291af79208fa2f85d958ca9b1/pic/UI_Shell_and_GUI-300x190.jpg -------------------------------------------------------------------------------- /pic/UI_Shell_and_GUI.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tinyclub/open-shell-book/752ffde2badb9b2291af79208fa2f85d958ca9b1/pic/UI_Shell_and_GUI.jpg -------------------------------------------------------------------------------- /pic/cover-tinylab.org.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tinyclub/open-shell-book/752ffde2badb9b2291af79208fa2f85d958ca9b1/pic/cover-tinylab.org.png -------------------------------------------------------------------------------- /pic/cover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tinyclub/open-shell-book/752ffde2badb9b2291af79208fa2f85d958ca9b1/pic/cover.png -------------------------------------------------------------------------------- /pic/tinylab-author.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tinyclub/open-shell-book/752ffde2badb9b2291af79208fa2f85d958ca9b1/pic/tinylab-author.jpg -------------------------------------------------------------------------------- /pic/tinylab-shop.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tinyclub/open-shell-book/752ffde2badb9b2291af79208fa2f85d958ca9b1/pic/tinylab-shop.jpg -------------------------------------------------------------------------------- /pic/tinylab-sina.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tinyclub/open-shell-book/752ffde2badb9b2291af79208fa2f85d958ca9b1/pic/tinylab-sina.jpg -------------------------------------------------------------------------------- /pic/tinylab-sponsor-1.68.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tinyclub/open-shell-book/752ffde2badb9b2291af79208fa2f85d958ca9b1/pic/tinylab-sponsor-1.68.jpg -------------------------------------------------------------------------------- /pic/tinylab-sponsor-3.69.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tinyclub/open-shell-book/752ffde2badb9b2291af79208fa2f85d958ca9b1/pic/tinylab-sponsor-3.69.jpg -------------------------------------------------------------------------------- /pic/tinylab-sponsor-6.88.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tinyclub/open-shell-book/752ffde2badb9b2291af79208fa2f85d958ca9b1/pic/tinylab-sponsor-6.88.jpg -------------------------------------------------------------------------------- /pic/tinylab-sponsor-8.99.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tinyclub/open-shell-book/752ffde2badb9b2291af79208fa2f85d958ca9b1/pic/tinylab-sponsor-8.99.jpg -------------------------------------------------------------------------------- /pic/tinylab-sponsor-9.68.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tinyclub/open-shell-book/752ffde2badb9b2291af79208fa2f85d958ca9b1/pic/tinylab-sponsor-9.68.jpg -------------------------------------------------------------------------------- /pic/tinylab-sponsor.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tinyclub/open-shell-book/752ffde2badb9b2291af79208fa2f85d958ca9b1/pic/tinylab-sponsor.jpg -------------------------------------------------------------------------------- /pic/tinylab-wechat.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tinyclub/open-shell-book/752ffde2badb9b2291af79208fa2f85d958ca9b1/pic/tinylab-wechat.jpg -------------------------------------------------------------------------------- /pic/tinylab-weixin.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tinyclub/open-shell-book/752ffde2badb9b2291af79208fa2f85d958ca9b1/pic/tinylab-weixin.jpg -------------------------------------------------------------------------------- /pic/tinylab-xingqiu.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tinyclub/open-shell-book/752ffde2badb9b2291af79208fa2f85d958ca9b1/pic/tinylab-xingqiu.jpg -------------------------------------------------------------------------------- /tools/.vim.format.rc: -------------------------------------------------------------------------------- 1 | :%s#\[\([0-9]*\)\]\[#\[\\[\1\\]\]\[##g 2 | :%s#[[:space:]]*$##g 3 | :'<,'>s#shell#Shell#g 4 | :'<,'>s#[` ]*\([a-zA-Z'"0-9 ~\$?%;()\*:/,!+-_><\&\|]\{1,\}\)[` ]*# `\1` #g 5 | :'<,'>s#( #(#g 6 | :'<,'>s#`,`#,#g 7 | :'<,'>s# )#)#g 8 | :'<,'>s#, #,#g 9 | :'<,'>s#`Shell`#Shell#g 10 | :'<,'>s#`Linux`#Linux#g 11 | :'<,'>s#Linux` #Linux#g 12 | :'<,'>s#^ `Linux`#Linux#g 13 | :'<,'>s#`C`#C#g 14 | :'<,'>s# `\[` #\[#g 15 | :'<,'>s# `\[ #\[#g 16 | :'<,'>s# \]` #\]#g 17 | :'<,'>s#`\[#\[#g 18 | :'<,'>s#\]`#]#g 19 | :'<,'>s#^ `#`#g 20 | :'<,'>s#^`- `#-#g 21 | :'<,'>s#^`-#-#g 22 | :'<,'>s# `(` #(#g 23 | :'<,'>s# `)` #)#g 24 | :'<,'>s#)` #)#g 25 | :'<,'>s# ` ##g 26 | :'<,'>s# `](#](#g 27 | :'<,'>s#`\([0-9]\{1,\}\)`#\1#g 28 | :%s# ,#,#g 29 | :'<,'>s#()##g 30 | -------------------------------------------------------------------------------- /tools/build-toc.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Build toc for markdown automatically 4 | # 5 | 6 | for i in *.markdown 7 | do 8 | # Generate a random toc id to avoid conflict 9 | toc="toc_${RANDOM}_${RANDOM}_" 10 | 11 | # Generate table of content 12 | sed -i -e '2i\\' $i 13 | 14 | grep "^###* " -ur $i | grep -n "^#" | \ 15 | sed -e "s/:/a/g;" |\ 16 | sed -e "s/\([0-9]*\)a\(#[^ ]*\) \(.*\)/\1a\2 [\3](#$toc\1)/g" |\ 17 | sed -e "s/#####/+ - /g;s/####/+ - /g" |\ 18 | sed -e "s/###/+ - /g;s/##/- /g" |\ 19 | xargs -i sed -i -e "{}" $i; 20 | 21 | sed -i -e '2i\\' $i 22 | sed -i -e "s/^+ / /g;" $i 23 | 24 | # Replace the #* with h* + id info 25 | t=0 26 | for line in `grep -n "^##" $i | cut -d':' -f1` 27 | do 28 | ((line+=t)) 29 | ((t++)) 30 | sed -i -e "${line}i" $i 31 | done 32 | done 33 | -------------------------------------------------------------------------------- /tools/convert2gitbook.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # convert2gitbook.sh -- convert the old markdown book support to gitbook 4 | # 5 | 6 | LANG=zh 7 | 8 | # Generate SUMMARY.md 9 | 10 | find $LANG/ -name "*.markdown*" | xargs -i grep -Hr "^# " {} \ 11 | | grep -v "define" | sort -t / -g -k 3 \ 12 | | sed -e "s/\(.*\):# \(.*\)/* [\2](\1)/g" > SUMMARY.md 13 | 14 | # Convert pic/cover.png to cover.jpg 15 | # Note: config/basic.yml doesn't use pic/cover.png currently. 16 | 17 | convert pic/cover.png cover.jpg 18 | 19 | # copy images to zh/ 20 | cp -r pic/ zh/chapters/ 21 | cp -r pic/ zh/preface/ 22 | -------------------------------------------------------------------------------- /tools/h2m: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | [ -z "$1" ] && echo "Usage: $0 xxx.html" && exit 1 4 | 5 | from=$1 6 | to=${from%.*}.markdown 7 | 8 | echo "Convert from $from to $to" 9 | pandoc -f html -t markdown $from -o $to 10 | -------------------------------------------------------------------------------- /tools/makepdf: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # -*- coding: utf-8 -*- 3 | 4 | require 'optparse' 5 | require 'fileutils' 6 | require 'erb' 7 | require 'yaml' 8 | require 'pathname' 9 | 10 | include FileUtils 11 | 12 | $here = File.expand_path(File.dirname(__FILE__)) 13 | $root = File.join($here, '..') 14 | $outDir = File.join($root, 'pdf') 15 | 16 | def main() 17 | options = { 18 | "build"=> "pdf", 19 | "lang" => "zh", 20 | "cover1" => "pic/cover.png", 21 | "cover_desc1" => "", 22 | "cover2" => "pic/cover.png", 23 | "cover_desc2" => "", 24 | "config" => "config/main.yml", 25 | "template" => "latex/template.tex", 26 | "chapter-files" => "*/*.markdown", 27 | "appendix-files"=> "*appendix/*.markdown", 28 | "jeykll" => false 29 | } 30 | 31 | configDir = File.join($root, '/config/') 32 | config_file = File.join(configDir, 'basic.yml') 33 | if File.exists? config_file 34 | config_options = YAML.load_file(config_file) 35 | options.merge!(config_options) 36 | end 37 | 38 | option_parser = OptionParser.new do |opts| 39 | executable_name = File.basename($PROGRAM_NAME) 40 | opts.banner = "make ebooks from markdown plain text 41 | Usage: #{executable_name} [options] 42 | " 43 | # Create a switch 44 | opts.on("-b","--build FORMAT","build format:epub,pdf,html. seperated by ','") do |format| 45 | formatOptions = format.split(",") 46 | formatOptions.each do | fmt | 47 | # puts fmt 48 | unless ["pdf","epub","html"].include?(fmt) 49 | raise ArgumentError,"FORMAT must be one of 'pdf,epub,html' format" 50 | end 51 | end 52 | options["build"] = format 53 | end 54 | opts.on("-d","--debug","debug") do 55 | options["debug"] = true 56 | end 57 | # Create a flag 58 | opts.on("-l","--lang LANG","language selection") do |lang| 59 | unless lang=="zh" or lang=="en" 60 | raise ArgumentError,"LANG must be in zh or en" 61 | end 62 | options["lang"] = lang 63 | end 64 | # Set the cover image 65 | opts.on("-C","--cover","cover") do 66 | options["cover"] = cover 67 | end 68 | opts.on("-c","--config CONFIG","config file") do |config| 69 | unless File.exists? config 70 | raise ArgumentError,"config file \"#{config}\" doesn't exist" 71 | end 72 | options["config"] = File.join($root, $config) 73 | end 74 | opts.on("-t","--template template.tex","latex template file") do |template| 75 | unless File.exists? template 76 | raise ArgumentError,"template file \"#{template}\" doesn't exist" 77 | end 78 | options["template"] = template 79 | end 80 | opts.on("-n","--name book name","book name") do |name| 81 | unless name =~ /^[a-zA-Z0-9]+$/ 82 | raise ArgumentError,"name should be [a-zA-Z0-9]" 83 | end 84 | options["name"] = name 85 | end 86 | opts.on("-g","--generate project","project name") do |name| 87 | unless name =~ /^[a-zA-Z0-9]+$/ 88 | raise ArgumentError,"name should be [a-zA-Z0-9]" 89 | end 90 | options["command"] = "generate" 91 | options["name"] = name 92 | end 93 | end 94 | 95 | option_parser.parse! 96 | 97 | #$here = File.expand_path(File.dirname(__FILE__)) 98 | options["name"] = File.basename(Dir.getwd) unless options["name"] 99 | options["outputformat"] = options["build"].split(',') 100 | 101 | puts options.inspect if options["debug"] 102 | 103 | if options["command"] == "generate" 104 | generate_project(options["name"]) 105 | exit 106 | end 107 | 108 | if options["outputformat"].include?("pdf") 109 | #puts "pdf" 110 | generate_pdf(options) 111 | end 112 | options["outputformat"].delete("pdf") 113 | if options["outputformat"].count>0 114 | generate_ebook(options) 115 | end 116 | end 117 | 118 | def figures(&block) 119 | begin 120 | Dir["#$root/pic/18333*.png"].each do |file| 121 | cp(file, file.sub(/18333fig0(\d)0?(\d+)\-tn/, '\1.\2')) 122 | end 123 | block.call 124 | ensure 125 | Dir["#$root/pic/18333*.png"].each do |file| 126 | rm(file.gsub(/18333fig0(\d)0?(\d+)\-tn/, '\1.\2')) 127 | end 128 | end 129 | end 130 | 131 | def command_exists?(command) 132 | if File.executable?(command) then 133 | return command 134 | end 135 | ENV['PATH'].split(File::PATH_SEPARATOR).map do |path| 136 | cmd = "#{path}/#{command}" 137 | File.executable?(cmd) || File.executable?("#{cmd}.exe") || File.executable?("#{cmd}.cmd") 138 | end.inject{|a, b| a || b} 139 | end 140 | 141 | def replace(string, &block) 142 | string.instance_eval do 143 | alias :s :gsub! 144 | instance_eval(&block) 145 | end 146 | string 147 | end 148 | 149 | def verbatim_sanitize(string) 150 | string.gsub('\\', '{\textbackslash}'). 151 | gsub('~', '{\textasciitilde}'). 152 | gsub(/([\$\#\_\^\%])/, '\\\\' + '\1{}') 153 | end 154 | 155 | def pre_pandoc(string, config) 156 | replace(string) do 157 | s /\#\#\#\#\# (.*?) \#\#\#\#\#/, 'PARASECTION: \1' 158 | # Pandoc discards #### subsubsections #### - this hack recovers them 159 | s /\#\#\#\# (.*?) \#\#\#\#/, 'SUBSUBSECTION: \1' 160 | 161 | # convert div figures to normal markdown format 162 | # http://johnmacfarlane.net/pandoc/README.html 163 | s /^
/, '![](pic/\1)\ ' 164 | 165 | # ![alt](../figures/LASO-tiny210-433.jpg) => ![alt](figures/LASO-tiny210-433.jpg) 166 | s /^!\[(.*)\]\([\.\/]*\/(pic\/.*)\)/, '![\1](\2)' 167 | # " 168 | 169 | # Turns URLs into clickable links 170 | s /\`(http:\/\/[A-Za-z0-9\/\%\&\=\-\_\\\.]+)\`/, '<\1>' 171 | s /(\n\n)\t(http:\/\/[A-Za-z0-9\/\%\&\=\-\_\\\.]+)\n([^\t]|\t\n)/, '\1<\2>\1' 172 | # ` 173 | # Process figures 174 | s /Insert\s18333fig\d+\.png\s*\n.*?\d{1,2}-\d{1,2}\. (.*)/, 'FIG: \1' 175 | end 176 | end 177 | 178 | def post_pandoc(string, config, lang, chapter=true) 179 | replace(string) do 180 | space = /\s/ 181 | 182 | # Reformat for the book documentclass as opposed to article 183 | s '\section', '\chap' 184 | s '\sub', '\\' 185 | s /SUBSUBSECTION: (.*)/, '\subsubsection{\1}' 186 | s /PARASECTION: (.*)/, '\paragraph{\1}' 187 | 188 | # Enable proper cross-reference 189 | s /#{config['fig'].gsub(space, '\s')}\s*(\d+)\-\-(\d+)/, '\imgref{\1.\2}' 190 | s /#{config['tab'].gsub(space, '\s')}\s*(\d+)\-\-(\d+)/, '\tabref{\1.\2}' 191 | s /#{config['prechap'].gsub(space, '\s')}\s*(\d+)(\s*)#{config['postchap'].gsub(space, '\s')}/, '\chapref{\1}\2' 192 | 193 | # Miscellaneous fixes 194 | s /FIG: (.*)/, '\img{\1}' 195 | s '\begin{enumerate}[1.]', '\begin{enumerate}' 196 | s /(\w)--(\w)/, '\1-\2' 197 | s /``(.*?)''/, "#{config['dql']}\\1#{config['dqr']}" 198 | 199 | # Typeset the maths in the book with TeX 200 | s '\verb!p = (n(n-1)/2) * (1/2^160))!', '$p = \frac{n(n-1)}{2} \times \frac{1}{2^{160}}$)' 201 | s '2\^{}80', '$2^{80}$' 202 | s /\sx\s10\\\^\{\}(\d+)/, '\e{\1}' 203 | 204 | # Convert inline-verbatims into \texttt (which is able to wrap) 205 | s /\\verb(\W)(.*?)\1/ do 206 | "{\\texttt{#{verbatim_sanitize($2)}}}" 207 | end 208 | 209 | # Ensure monospaced stuff is in a smaller font 210 | s /(\\verb(\W).*?\2)/, '{\footnotesize\1}' 211 | #s /(\\begin\{verbatim\}.*?\\end\{verbatim\})/m, '{\footnotesize\1}' 212 | 213 | # Shaded verbatim block 214 | #s /(\\begin\{verbatim\}.*?\\end\{verbatim\})/m, '\begin{shaded}\1\end{shaded}' 215 | #s /\t/m, ' ' 216 | 217 | # is using fancyvrb 218 | s /\\begin\{verbatim\}/,'\\begin{Verbatim}[tabsize=4,formatcom=\color{colorchapter},frame=lines]' 219 | s /\\end\{verbatim\}/,'\\end{Verbatim}' 220 | 221 | # try moreverb 222 | #s /\\begin\{verbatim\}/,'\\begin{verbatimtab}[4]' 223 | #s /\\end\{verbatim\}/,'\\end{verbatimtab}' 224 | 225 | if lang=="zh" 226 | # http://www.devdaily.com/blog/post/latex/control-line-spacing-in-itemize-enumerate-tags 227 | # http://wiki.ctex.org/index.php/LaTeX/%E5%88%97%E8%A1%A8 228 | # set the space of itemsize 229 | s /(\\begin\{itemize\})/m,'\begin{itemize}\setlength{\itemsep}{1pt}\setlength{\parskip}{0pt}\setlength{\parsep}{0pt}' 230 | s /(\\begin\{enumerate\})/m,'\begin{enumerate}\setlength{\itemsep}{1pt}\setlength{\parskip}{0pt}\setlength{\parsep}{0pt}' 231 | # hardcode for itemize to use * instead of dot, which is missed in some chinese fonts 232 | # and keep \item inside \enumerate env is not changed 233 | # \item -> \item[*] 234 | # solution is provided by Alexis, and it works under ruby 1.9+ only due to bug in 1.8.7 235 | # http://stackoverflow.com/questions/9115018/regular-expression-using-ruby-string-gsub-method-to-replace-multi-matches 236 | if RUBY_VERSION >= "1.9" 237 | s /^\\item(?=((?!\\begin\{itemize\}).)*\\end\{itemize\})/m, '\\item[*]' 238 | else 239 | s /(^\\item)/m,'\item[*]' 240 | end 241 | 242 | # change the width to standard .6 width 243 | s /\\includegraphics/m, '\\includegraphics[width=\\imgwidth]' 244 | end 245 | 246 | if chapter != true 247 | s /^\\chap\{(.*)\}/,'\chapter*{\1}'"\n"'\addcontentsline{toc}{chapter}{\1}' 248 | s /^\\section\{(.*)\}/,'\section*{\1}' 249 | s /^\\subsection\{(.*)\}/,'\subsection*{\1}' 250 | end 251 | 252 | s /\\ctable\[pos \= H/m, '\\ctable[pos = h' 253 | 254 | end 255 | end 256 | 257 | def check_jekyll(str) 258 | str.lines.to_a[4..-3].join 259 | end 260 | 261 | def generate_pdf(options) 262 | $config = YAML.load_file(options["config"]) 263 | template = ERB.new(File.read(options["template"])) 264 | languages = [options["lang"]] 265 | missing = ['pandoc', 'xelatex'].reject{|command| command_exists?(command)} 266 | unless missing.empty? 267 | puts "Missing dependencies: #{missing.join(', ')}." 268 | puts "Install these and try again." 269 | exit 1 270 | end 271 | 272 | puts "Will generate pdf for the following languages using template #{options["template"]}:" 273 | puts " #{languages}" 274 | puts 275 | 276 | figures do 277 | languages.each do |lang| 278 | config = $config['default'].merge($config[lang]) rescue $config['default'] 279 | 280 | puts "#{lang}:" 281 | 282 | cover_image1 = options['cover1'] 283 | puts "\n\tUsing Cover Image 1: #{options['cover1']}\n" 284 | 285 | cover_desc1 = options['cover_desc1'] 286 | puts "\tUsing Cover Description 1: #{options['cover_desc1']}\n\n" 287 | 288 | cover_image2 = options['cover2'] 289 | puts "\n\tUsing Cover Image 2: #{options['cover2']}\n" 290 | 291 | cover_desc2 = options['cover_desc2'] 292 | puts "\tUsing Cover Description 2: #{options['cover_desc2']}\n\n" 293 | 294 | prefacefiles = "#$root/#{lang}/#{options['preface-files']}" 295 | 296 | puts "\tParsing preface markdown... #{prefacefiles} " 297 | prefacemarkdown = Dir["#{prefacefiles}"].sort.map do |file| 298 | puts "\t =>"+file 299 | if options["jeykll"] 300 | check_jekyll(File.read(file)) 301 | else 302 | File.read(file) 303 | end 304 | end.join("\n\n") 305 | 306 | preface = IO.popen('pandoc -p --no-wrap -f markdown -t latex', 'w+') do |pipe| 307 | pipe.write(pre_pandoc(prefacemarkdown, config)) 308 | pipe.close_write 309 | post_pandoc(pipe.read, config, lang, false) 310 | end 311 | 312 | chapterfiles = "#$root/#{lang}/#{options['chapter-files']}" 313 | 314 | puts "\n\tParsing main chapters markdown... #{chapterfiles} " 315 | chaptersmarkdown = Dir["#{chapterfiles}"].sort{|a,b| [Integer(a[/\d+/]),a]<=>[Integer(b[/\d+/]),b]}.map do |file| 316 | puts "\t =>"+file 317 | if options["jeykll"] 318 | check_jekyll(File.read(file)) 319 | else 320 | File.read(file) 321 | end 322 | end.join("\n\n") 323 | # puts "done" 324 | 325 | latex = IO.popen('pandoc -p --no-wrap -f markdown -t latex', 'w+') do |pipe| 326 | pipe.write(pre_pandoc(chaptersmarkdown, config)) 327 | pipe.close_write 328 | post_pandoc(pipe.read, config, lang) 329 | end 330 | # puts "done" 331 | 332 | appendixfiles = "#$root/#{lang}/#{options['appendix-files']}" 333 | 334 | puts "\n\tParsing appendix markdown... #{appendixfiles} " 335 | appendixmarkdown = Dir["#{appendixfiles}"].sort.map do |file| 336 | puts "\t =>"+file 337 | if options["jeykll"] 338 | check_jekyll(File.read(file)) 339 | else 340 | File.read(file) 341 | end 342 | end.join("\n\n") 343 | 344 | appendix = IO.popen('pandoc -p --no-wrap -f markdown -t latex', 'w+') do |pipe| 345 | pipe.write(pre_pandoc(appendixmarkdown, config)) 346 | pipe.close_write 347 | post_pandoc(pipe.read, config, lang) 348 | end 349 | #puts "done" 350 | 351 | print "\n\tCreating main.tex for #{lang}... " 352 | dir = "latex/#{lang}" 353 | mkdir_p(dir) 354 | 355 | File.open("#{dir}/main.tex", 'w') do |file| 356 | file.write(template.result(binding)) 357 | end 358 | puts "done" 359 | 360 | abort = false 361 | 362 | puts "\tRunning XeTeX:" 363 | cd($root) 364 | 3.times do |i| 365 | print "\t\tPass #{i + 1}... " 366 | line = "" 367 | IO.popen("xelatex -output-directory=\"#{dir}\" \"#{dir}/main.tex\" 2>&1") do |pipe| 368 | unless $DEBUG 369 | while line = pipe.gets and not abort 370 | # print line.encoding.name 371 | # use different way to handle encoding issues for ruby 372 | if (RUBY_VERSION >= "1.9" and line.valid_encoding? and line =~ /^!\s/) or (RUBY_VERSION < "1.9" and line =~ /^!\s/) 373 | puts "failed with:\n\t\t\t#{line.strip}" 374 | puts "\tConsider running this again with --debug." 375 | abort = true 376 | end 377 | end 378 | 379 | #print line while line = pipe.gets 380 | else 381 | STDERR.print while pipe.gets rescue abort = true 382 | end 383 | end 384 | break if abort 385 | puts "done" 386 | end 387 | 388 | unless abort 389 | print "\tMoving output to #$outDir/#{options["name"]}.#{lang}.pdf... " 390 | mv("#{dir}/main.pdf", "#$outDir/#{options["name"]}.#{lang}.pdf") 391 | puts "done" 392 | else 393 | print "\tConvert error, exit !\n" 394 | exit 1 395 | end 396 | end 397 | end 398 | end 399 | 400 | def generate_ebook(options) 401 | name = File.basename(Dir.getwd) 402 | 403 | missing = ['pandoc'].reject{|command| command_exists?(command)} 404 | unless missing.empty? 405 | puts "Missing dependencies: #{missing.join(', ')}." 406 | puts "Install these and try again." 407 | exit 1 408 | end 409 | 410 | puts " " 411 | puts "Will generate ebooks [#{options["outputformat"].join(',')}] for the following languages #{options["lang"]}" 412 | puts " " 413 | 414 | options["lang"].split(',').each do |lang| 415 | puts "convert content for '#{lang}' language" 416 | 417 | if lang == 'zh' 418 | figure_title = '图' 419 | else 420 | figure_title = 'Figure' 421 | end 422 | 423 | dir = File.expand_path(File.join(Dir.getwd, lang)) 424 | #puts File.join(Dir.getwd, lang) 425 | book_content = "" 426 | Dir[File.join(dir, '**', '*.markdown')].sort.each do |input| 427 | puts "\tProcessing #{input}" 428 | content = File.read(input) 429 | content.gsub!(/Insert\s+(.*)(\.png)\s*\n?\s*#{figure_title}\s+(.*)/, '![\3](pic/\1-tn\2 "\3")') 430 | book_content << content 431 | end 432 | 433 | File.open("#{name}.#{lang}.markdown", 'w') do |output| 434 | output.write(book_content) 435 | end 436 | # pandoc -S -s --epub-metadata=metadata.xml -o sdcamp.zh.html --epub-stylesheet=epub/ProGit.css epub/title.txt sdcamp.zh.markdown 437 | options["outputformat"].each do | format | 438 | system('pandoc', 439 | '--standalone', 440 | '--toc', 441 | '--template=template.html', 442 | '--epub-metadata', 'epub/metadata.xml', 443 | '--epub-stylesheet', 'epub/book.css', # this doesn't work 444 | '--output', "#{name}.#{lang}.#{format}", 445 | "title.#{lang}.txt", # little strange, if this put under epub, pandoc reports error 446 | "#{name}.#{lang}.markdown") 447 | # pandoc -S --epub-metadata=metadata.xml -o progit.epub title.txt 448 | puts("#{name}.#{lang}.#{format} is generated") 449 | end 450 | end 451 | end 452 | 453 | # http://stackoverflow.com/questions/5074327/most-appropriate-way-to-generate-directory-of-files-from-directory-of-template-f 454 | def generate_project(project) 455 | destination = project 456 | source = File.dirname(__FILE__)+"/../templates" 457 | #puts "generate project \"#{destination}\" from source \"#{source}\"" 458 | FileUtils.rmtree(destination) 459 | FileUtils.mkdir_p(destination) 460 | sourceroot=Pathname.new(source) 461 | sourcerealpath = sourceroot.cleanpath 462 | puts "generate project \"#{destination}\" from source \"#{sourcerealpath}\"" 463 | Dir.glob(File.join(source, '**/*')).each do |path| 464 | pathname = Pathname.new(path) 465 | relative = pathname.relative_path_from(sourceroot) 466 | #puts "parent:" , sourceroot 467 | #puts "relative:", relative 468 | if File.directory?(pathname) 469 | destdir = File.join(destination, relative.dirname) 470 | #puts "create #{destdir} " 471 | FileUtils.mkdir_p(destdir) 472 | else 473 | FileUtils.mkdir_p(File.join(destination, relative.dirname)) 474 | if pathname.extname == '.erb' 475 | #puts pathname.basename.sub(/\.erb$/, '') 476 | #puts destination 477 | #puts File.join(destination,pathname.basename.sub(/\.erb$/, '')) 478 | File.open(File.join(destination,pathname.basename.sub(/\.erb$/, '')), 'w') do |file| 479 | file.puts(ERB.new(File.read(path)).result(binding)) 480 | end 481 | else 482 | print pathname.cleanpath, " => ", File.join(destination, relative.dirname),"\n" 483 | FileUtils.cp(pathname, File.join(destination, relative.dirname)) 484 | end 485 | end 486 | end 487 | end 488 | 489 | main 490 | 491 | -------------------------------------------------------------------------------- /zh/appendix/02-chapter1.markdown: -------------------------------------------------------------------------------- 1 | **关注作者公众号**: 2 |
3 | 4 |
5 | 6 | # 附录 7 | 8 | - [Shell 编程学习笔记](#toc_19246_27800_1) 9 | - [前言](#toc_19246_27800_2) 10 | - [执行 Shell 脚本的方式](#toc_19246_27800_3) 11 | - [范例:输入重定向到Bash](#toc_19246_27800_4) 12 | - [范例:以脚本名作为参数](#toc_19246_27800_5) 13 | - [范例:以 . 来执行](#toc_19246_27800_6) 14 | - [范例:直接执行](#toc_19246_27800_7) 15 | - [Shell 的执行原理](#toc_19246_27800_8) 16 | - [变量赋值](#toc_19246_27800_9) 17 | - [范例:获取当前的工作目录并存放到变量中](#toc_19246_27800_10) 18 | - [数组](#toc_19246_27800_11) 19 | - [范例:对数组元素赋值](#toc_19246_27800_12) 20 | - [范例:访问某个数组元素](#toc_19246_27800_13) 21 | - [范例:数组组合赋值](#toc_19246_27800_14) 22 | - [范例:列出数组中所有内容](#toc_19246_27800_15) 23 | - [范例:获取数组元素个数](#toc_19246_27800_16) 24 | - [参数传递](#toc_19246_27800_17) 25 | - [设置环境变量](#toc_19246_27800_18) 26 | - [键盘读起变量值](#toc_19246_27800_19) 27 | - [设置变量的只读属性](#toc_19246_27800_20) 28 | - [条件测试命令 test](#toc_19246_27800_21) 29 | - [范例:数值比较](#toc_19246_27800_22) 30 | - [范例:测试文件属性](#toc_19246_27800_23) 31 | - [范例:字符传属性以及比较](#toc_19246_27800_24) 32 | - [范例:串比较](#toc_19246_27800_25) 33 | - [整数算术或关系运算 expr](#toc_19246_27800_26) 34 | - [控制执行流程命令](#toc_19246_27800_27) 35 | - [范例:条件分支命令 if](#toc_19246_27800_28) 36 | - [范例:case 命令举例](#toc_19246_27800_29) 37 | - [范例:循环语句 while, until](#toc_19246_27800_30) 38 | - [范例:有限循环命令 for](#toc_19246_27800_31) 39 | - [函数](#toc_19246_27800_32) 40 | - [后记](#toc_19246_27800_33) 41 | 42 | 43 | 44 | ## Shell编程学习笔记 45 | 46 | 47 | ### 前言 48 | 49 | 这是作者早期的 Shell 编程学习笔记,主要包括 Shell 概述、 Shell 变量、位置参数、特殊符号、别名、各种控制语句、函数等 Shell 编程知识。 50 | 51 | 要想系统地学 Shell,应该找些较系统的资料,例如:[《Shell 编程范例》](http://www.tinylab.org/shell-programming-paradigm-series-index-review/)和[《鸟哥学习Shell Scripts》](http://www.chinaunix.net/jh/24/628472.html)。 52 | 53 | 54 | ### 执行 Shell 脚本的方式 55 | 56 | 57 | #### 范例:输入重定向到 Bash 58 | 59 | ``` 60 | $ bash < ex1 61 | ``` 62 | 63 | 可以读入 `ex1` 中的程序,并执行 64 | 65 | 66 | #### 范例:以脚本名作为参数 67 | 68 | 其一般形式是: 69 | 70 | ``` 71 | $ bash 脚本名 [参数] 72 | ``` 73 | 74 | 例如: 75 | 76 | ``` 77 | $ bash ex2 /usr/meng /usr/zhang 78 | ``` 79 | 80 | 其执行过程与上一种方式一样,但这种方式的好处是能在脚本名后面带有参数,从而将参数值传递给程序中的命令,使一个 Shell 脚本可以处理多种情况,就如同函数调用时可根据具体问题传递相应的实参。 81 | 82 | 83 | #### 范例:以 . 来执行 84 | 85 | 86 | 如果以当前 Shell (以 `·` 表示)执行一个 Shell 脚本,则可以使用如下简便形式: 87 | 88 | ``` 89 | $ · ex3[参数] 90 | ``` 91 | 92 | 93 | #### 范例:直接执行 94 | 95 | 将 Shell 脚本的权限设置为可执行,然后在提示符下直接执行它。 96 | 97 | 具体办法: 98 | 99 | ``` 100 | $ chmod a+x ex4 101 | $ ./ex4 102 | ``` 103 | 104 | 这个要求在 Shell 脚本的开头指明执行该脚本的具体 Shell,例如 `/bin/bash`: 105 | 106 | ``` 107 | #!/bin/bash 108 | ``` 109 | 110 | 111 | ### Shell 的执行原理 112 | 113 | 114 | Shell 接收用户输入的命令(脚本名),并进行分析。如果文件被标记为可执行,但不是被编译过的程序,Shell 就认为它是一个 Shell 脚本。 Shell 将读取其中的内容,并加以解释执行。所以,从用户的观点看,执行 Shell 脚本的方式与执行一般的可执行文件的方式相似。 115 | 116 | 因此,用户开发的 Shell 脚本可以驻留在命令搜索路径的目录之下(通常是 `/bin`、`/usr/bin`等,可通过 `PATH` 环境变量设置和查看),像普通命令一样使用。这样,也就开发出自己的新命令。如果打算反复使用编好的 Shell 脚本,那么采用这种方式就比较方便。 117 | 118 | 119 | ### 变量赋值 120 | 121 | 122 | 可以将一个命令的执行结果赋值给变量。有两种形式的命令替换:一种是使用倒引号引用命令,其一般形式是: `命令表`。 123 | 124 | 125 | #### 范例:获取当前的工作目录并存放到变量中 126 | 127 | 例如:将当前工作目录的全路径名存放到变量dir中,输入以下命令行: 128 | 129 | ``` 130 | $ dir=`pwd` 131 | ``` 132 | 133 | 另一种形式是:`$(命令表)`。上面的命令行也可以改写为: 134 | 135 | ``` 136 | $ dir=$(pwd) 137 | ``` 138 | 139 | 140 | ### 数组 141 | 142 | 143 | `Bash` 只提供一维数组,并且没有限定数组的大小。类似与 C 语言,数组元素的下标由 0 开始编号。获取数组中的元素要利用下标。下标可以是整数或算术表达式,其值应大于或等于 0 。用户可以使用赋值语句对数组变量赋值。 144 | 145 | 146 | #### 范例:对数组元素赋值 147 | 148 | 149 | 对数组元素赋值的一般形式是:`数组名[下标]=值`,例如: 150 | 151 | ``` 152 | $ city[0]=Beijing 153 | $ city[1]=Shanghai 154 | $ city[2]=Tianjin 155 | ``` 156 | 157 | 也可以用 `declare` 命令显式声明一个数组,一般形式是: 158 | 159 | ``` 160 | $ declare -a 数组名 161 | ``` 162 | 163 | 164 | #### 范例:访问某个数组元素 165 | 166 | 167 | 读取数组元素值的一般格式是: `${数组名[下标]}`,例如: 168 | 169 | ``` 170 | $ echo ${city[0]} 171 | Beijing 172 | ``` 173 | 174 | 175 | #### 范例:数组组合赋值 176 | 177 | 178 | 一个数组的各个元素可以利用上述方式一个元素一个元素地赋值,也可以组合赋值。定义一个数组并为其赋初值的一般形式是: 179 | 180 | ``` 181 | 数组名=(值1 值2 ... 值n) 182 | ``` 183 | 184 | 其中,各个值之间以空格分开。例如: 185 | 186 | ``` 187 | $ A=(this is an example of shell script) 188 | $ echo ${A[0]} ${A[2]} ${A[3]} ${A[6]} 189 | this an example script 190 | $ echo ${A[8]} 191 | ``` 192 | 193 | 由于值表中初值共有 7 个,所以 `A` 的元素个数也是 7 。 `A[8]` 超出了已赋值的数组 `A` 的范围,就认为它是一个新元素,由于预先没有赋值,所以它的值是空串。 194 | 195 | 若没有给出数组元素的下标,则数组名表示下标为 0 的数组元素,如 `city` 就等价于 `city[0]`。 196 | 197 | 198 | #### 范例:列出数组中所有内容 199 | 200 | 201 | 使用 `*` 或 `@` 做下标,则会以数组中所有元素取代。 202 | 203 | ``` 204 | $ echo ${A[*]} 205 | this is an example of shell script 206 | ``` 207 | 208 | 209 | #### 范例:获取数组元素个数 210 | 211 | 212 | ``` 213 | $ echo ${#A[*]} 214 | 7 215 | ``` 216 | 217 | 218 | 219 | ### 参数传递 220 | 221 | 222 | 假如要编写一个 Shell 来求两个数的和,可以怎么实现呢?为了介绍参数传递的用法,编写这样一个脚本: 223 | 224 | ``` 225 | $ cat > add 226 | let sum=$1+$2 227 | echo $sum 228 | ``` 229 | 230 | 保存后,执行一下: 231 | 232 | ``` 233 | $ chmod a+x ./add 234 | $ ./add 5 10 235 | 15 236 | ``` 237 | 238 | 可以看出 5 和 10 分别传给了 `$1` 和 `$2`,这是 Shell 自己预设的参数顺序,其实也可以先定义好变量,然后传递进去。 239 | 240 | 例如,修改上述脚本得到: 241 | 242 | ``` 243 | let sum=$X+$Y 244 | echo $sum 245 | ``` 246 | 247 | 再次执行: 248 | 249 | ``` 250 | $ X=5 Y=10 ./add 251 | 15 252 | ``` 253 | 254 | 可以发现,同样可以得到正确结果。 255 | 256 | 257 | ### 设置环境变量 258 | 259 | 260 | export一个环境变量: 261 | 262 | ``` 263 | $ export opid=True 264 | ``` 265 | 266 | 这样子就可以,如果要登陆后都生效,可以直接添加到 `/etc/profile` 或者 `~/.bashrc` 里头。 267 | 268 | 269 | ### 键盘读起变量值 270 | 271 | 272 | 可以通过 `read` 来读取变量值,例如,来等待用户输入一个值并且显示出来: 273 | 274 | ``` 275 | $ read -p "请输入一个值 : " input ; echo "你输入了一个值为 :" $input 276 | 请输入一个值 : 21500 277 | 你输入了一个值为 : 21500 278 | ``` 279 | 280 | 281 | 282 | ### 设置变量的只读属性 283 | 284 | 285 | 有些重要的 Shell 变量,赋值后不应该修改,那么可设置它为 `readonly` : 286 | 287 | ``` 288 | $ oracle_home=/usr/oracle7/bin 289 | $ readonly oracle_home 290 | ``` 291 | 292 | 293 | ### 条件测试命令 test 294 | 295 | 296 | 语法:`test 表达式` 297 | 如果表达式为真,则返回真,否则,返回假。 298 | 299 | 300 | #### 范例:数值比较 301 | 302 | 303 | 先给出数值比较时常见的比较符: 304 | 305 | > `-eg =;-ne !=;-gt >;-ge >=;-lt <;-le <=` 306 | 307 | ``` 308 | $ test var1 -gt var2 309 | ``` 310 | 311 | 312 | #### 范例:测试文件属性 313 | 314 | 315 | 文件的可读、可写、可执行,是否为普通文件,是否为目录分别对应: 316 | 317 | > `-r; -w; -x; -f; -d` 318 | 319 | ``` 320 | $ test -r filename 321 | ``` 322 | 323 | 324 | #### 范例:字符传属性以及比较 325 | 326 | 327 | > 串的长度为零:`-z`; 非零:`-n`,如: 328 | 329 | ``` 330 | $ test -z s1 331 | ``` 332 | 333 | 如果串 `s1` 长度为零,返回真。 334 | 335 | 336 | #### 范例:串比较 337 | 338 | 339 | > 相等`"s1"="s2"`; 不相等 `"s1"!="s2"` 340 | 341 | 还有一种比较串的方法(可以按字典序来比较): 342 | 343 | ``` 344 | $ if [[ 'abcde' < 'abcdf' ]]; then echo "yeah,果然是诶"; fi 345 | yeah,果然是诶 346 | ``` 347 | 348 | 349 | ### 整数算术或关系运算 expr 350 | 351 | 352 | 可用该命令进行的运算有: 353 | 354 | > 算术运算:`+ - * / %`;逻辑运算`:= ! < <= > >=` 355 | 356 | 如: 357 | 358 | ``` 359 | $ i=5;expr $i+5 360 | ``` 361 | 362 | 另外,`bc` 是一个命令行计算器,可以进行一些算术计算。 363 | 364 | 365 | ### 控制执行流程命令 366 | 367 | 368 | #### 范例:条件分支命令 if 369 | 370 | 371 | `if` 命令举例:如果第一个参数是一个普通文件名,那么分页打印该文件;否则,如果它为目录名,则进入该目录并打印该目录下的所有文件,如果也不是目录,那么提示相关信息。 372 | 373 | ``` 374 | if test -f $1 375 | then 376 | pr $1>/dev/lp0 377 | elif 378 | test-d $1 379 | then 380 | (cd $1;pr *>/dev/lp0) 381 | else 382 | echo $1 is neither a file nor a directory 383 | fi 384 | ``` 385 | 386 | 387 | #### 范例:case 命令举例 388 | 389 | 390 | `case` 命令是一个基于模式匹配的多路分支命令,下面将根据用户键盘输入情况决定下一步将执行那一组命令。 391 | 392 | ``` 393 | while [ $reply!="y" ] && [ $reply!="Y" ] #下面将学习的循环语句 394 | do 395 | echo "\nAre you want to continue?(Y/N)\c" 396 | read reply #读取键盘 397 | case $replay in 398 | (y|Y) break;; #退出循环 399 | (n|N) echo "\n\nTerminating\n" 400 | exit 0;; 401 | *) echo "\n\nPlease answer y or n" 402 | continue; #直接返回内层循环开始出继续 403 | esac 404 | done 405 | ``` 406 | 407 | 408 | #### 范例:循环语句 while, until 409 | 410 | 411 | 语法: 412 | 413 | ``` 414 | while/until 命令表1 415 | do 416 | 命令表2 417 | done 418 | ``` 419 | 420 | 区别是,前者执行命令表 1 后,如果退出状态为零,那么执行 `do` 后面的命令表 2,然后回到起始处,而后者执行命令表 1 后,如果退出状态非零,才执行类似操作。例子同上。 421 | 422 | 423 | #### 范例:有限循环命令 for 424 | 425 | 426 | 语法: 427 | 428 | ``` 429 | for 变量名 in 字符串表 430 | do 431 | 命令表 432 | done 433 | ``` 434 | 435 | 举例: 436 | 437 | ``` 438 | FILE="test1.c myfile1.f pccn.h" 439 | for i in $FILE 440 | do 441 | cd ./tmp 442 | cp $i $i.old 443 | echo "$i copied" 444 | done 445 | ``` 446 | 447 | 448 | ### 函数 449 | 450 | 451 | 现在来看看 Shell 里头的函数用法,先看个例子:写一个函数,然后调用它显示 `Hello, World!` 452 | 453 | ``` 454 | $ cat > show 455 | # 函数定义 456 | function show 457 | { 458 | echo $1$2; 459 | } 460 | H="Hello," 461 | W="World!" 462 | # 调用函数,并传给两个参数H和W 463 | show $H $W 464 | ``` 465 | 466 | 演示: 467 | 468 | ``` 469 | $ chmod 770 show 470 | $./show 471 | Hello,World! 472 | ``` 473 | 474 | 看出什么蹊跷了吗? 475 | 476 | ``` 477 | $ show $H $W 478 | ``` 479 | 480 | 咱们可以直接在函数名后面跟实参。 481 | 482 | 实参顺序对应“虚参”的 `$1,$2,$3`…… 483 | 484 | 注意:假如要传入一个参数,如果这个参数中间带空格,怎么办? 先试试看。 485 | 486 | 来显示 `Hello World` (两个单词之间有个空格) 487 | 488 | ``` 489 | function show 490 | { 491 | echo $1 492 | } 493 | HW="Hello World" 494 | show "$HW" 495 | ``` 496 | 497 | 如果直接 `show $HW`,肯定不行,因为 `$1` 只接受到了 `Hello`,所以结果只显示 `Hello`,原因是字符串变量必须用 `"` 包含起来。 498 | 499 | 500 | ### 后记 501 | 502 | 感兴趣的话继续学习吧! 503 | 504 | 还有好多强大的东西等着呢,比如 `cut`,`expr`,`sed`,`awk` 等等。 505 | -------------------------------------------------------------------------------- /zh/appendix/pic/mommy-tea.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tinyclub/open-shell-book/752ffde2badb9b2291af79208fa2f85d958ca9b1/zh/appendix/pic/mommy-tea.png -------------------------------------------------------------------------------- /zh/chapters/01-chapter1.markdown: -------------------------------------------------------------------------------- 1 | **关注作者公众号**: 2 |
3 | 4 |
5 | 6 | # 准备工作 7 | 8 | - [前言](#toc_22391_32127_1) 9 | - [什么是 Shell](#toc_22391_32127_2) 10 | - [搭建运行环境](#toc_22391_32127_3) 11 | - [基本语法介绍](#toc_22391_32127_4) 12 | - [Shell 程序设计过程](#toc_22391_32127_5) 13 | - [调试方法介绍](#toc_22391_32127_6) 14 | - [小结](#toc_22391_32127_7) 15 | - [参考资料](#toc_22391_32127_8) 16 | 17 | 18 | 19 | ## 前言 20 | 21 | 到最后一节来写“开篇”,确实有点古怪。不过,在[第一篇(数值操作)][200]的开头实际上也算是一个小的开篇,那里提到整个系列的前提是需要有一定的 Shell 编程基础,因此,为了能够让没有 Shell 编程基础的读者也可以阅读这个系列,我到最后来重写这个开篇。开篇主要介绍什么是 Shell,Shell 运行环境,Shell 基本语法和调试技巧。 22 | 23 | 24 | [200]: 01-chapter2.markdown 25 | 26 | 27 | ## 什么是 Shell 28 | 29 | 首先让我们从下图看看 Shell 在整个操作系统中所处的位置吧,该图的外圆描述了整个操作系统(比如 `Debian/Ubuntu/Slackware` 等),内圆描述了操作系统的核心(比如 `Linux Kernel`),而 `Shell` 和 `GUI` 一样作为用户和操作系统之间的接口。 30 | 31 | ![Shell和GUI用户接口](pic/UI_Shell_and_GUI.jpg) 32 | 33 | `GUI` 提供了一种图形化的用户接口,使用起来非常简便易学;而 `Shell` 则为用户提供了一种命令行的接口,接收用户的键盘输入,并分析和执行输入字符串中的命令,然后给用户返回执行结果,使用起来可能会复杂一些,但是由于占用的资源少,而且在操作熟练以后可能会提高工作效率,而且具有批处理的功能,因此在某些应用场合还非常流行。 34 | 35 | `Shell` 作为一种用户接口,它实际上是一个能够解释和分析用户键盘输入,执行输入中的命令,然后返回结果的一个解释程序(Interpreter,例如在 `linux` 下比较常用的 `Bash`),我们可以通过下面的命令查看当前的 `Shell` : 36 | 37 | ``` 38 | $ echo $SHELL 39 | /bin/bash 40 | $ ls -l /bin/bash 41 | -rwxr-xr-x 1 root root 702160 2008-05-13 02:33 /bin/bash 42 | ``` 43 | 44 | 该解释程序不仅能够解释简单的命令,而且可以解释一个具有特定语法结构的文件,这种文件被称作脚本(Script)。它具体是如何解释这些命令和脚本文件的,这里不深入分析,请看我在 2008 年写的另外一篇文章:[《Linux命令行上程序执行的一刹那》](http://tinylab.gitbooks.io/cbook/content/zh/chapters/02-chapter3.html)。 45 | 46 | 既然该程序可以解释具有一定语法结构的文件,那么我们就可以遵循某一语法来编写它,它有什么样的语法,如何运行,如何调试呢?下面我们以 `Bash` 为例来讨论这几个方面。 47 | 48 | 49 | ## 搭建运行环境 50 | 51 | 为了方便后面的练习,我们先搭建一个基本运行环境:在一个 Linux 操作系统中,有一个运行有 `Bash` 的命令行在等待我们键入命令,这个命令行可以是图形界面下的 `Terminal` (例如 `Ubuntu` 下非常厉害的 `Terminator`),也可以是字符界面的 `Console` (可以用 `CTRL+ALT+F1~6` 切换),如果你发现当前 `Shell` 不是 `Bash`,请用下面的方法替换它: 52 | 53 | ``` 54 | $ chsh $USER -s /bin/bash 55 | $ su $USER 56 | ``` 57 | 58 | 或者是简单地键入Bash: 59 | 60 | ``` 61 | $ bash 62 | $ echo $SHELL # 确认一下 63 | /bin/bash 64 | ``` 65 | 66 | 如果没有安装 Linux 操作系统,也可以考虑使用一些公共社区提供的 [Linux 虚拟实验服务](http://www.tinylab.org/free-online-linux-labs/),一般都有提供远程 `Shell`,你可以通过 `Telnet` 或者是 `Ssh` 的客户端登录上去进行练习。 67 | 68 | 有了基本的运行环境,那么如何来运行用户键入的命令或者是用户编写好的脚本文件呢 `?` 69 | 70 | 假设我们编写好了一个 Shell 脚本,叫 `test.sh` 。 71 | 72 | 第一种方法是确保我们执行的命令具有可执行权限,然后直接键入该命令执行它: 73 | 74 | ``` 75 | $ chmod +x /path/to/test.sh 76 | $ /path/to/test.sh 77 | ``` 78 | 79 | 第二种方法是直接把脚本作为 `Bash` 解释器的参数传入: 80 | 81 | ``` 82 | $ bash /path/to/test.sh 83 | ``` 84 | 85 | 或 86 | 87 | ``` 88 | $ source /path/to/test.sh 89 | ``` 90 | 91 | 或 92 | 93 | ``` 94 | $ . /path/to/test.sh 95 | ``` 96 | 97 | 98 | ## 基本语法介绍 99 | 100 | 先来一个 `Hello, World` 程序。 101 | 102 | 下面来介绍一个 Shell 程序的基本结构,以 `Hello, World` 为例: 103 | 104 | ``` 105 | #!/bin/bash -v 106 | # test.sh 107 | echo "Hello, World" 108 | ``` 109 | 110 | 把上述代码保存为 `test.sh`,然后通过上面两种不同方式运行,可以看到如下效果。 111 | 112 | 方法一: 113 | 114 | ``` 115 | $ chmod +x test.sh 116 | $ ./test.sh 117 | ./test.sh 118 | #!/bin/bash -v 119 | 120 | echo "Hello, World" 121 | Hello, World 122 | ``` 123 | 124 | 方法二: 125 | 126 | ``` 127 | $ bash test.sh 128 | Hello, World 129 | 130 | $ source test.sh 131 | Hello, World 132 | 133 | $ . test.sh 134 | Hello, World 135 | ``` 136 | 137 | 我们发现两者运行结果有区别,为什么呢?这里我们需要关注一下 `test.sh` 文件的内容,它仅仅有两行,第二行打印了 `Hello, World`,两种方法都达到了目的,但是第一种方法却多打印了脚本文件本身的内容,为什么呢? 138 | 139 | 原因在该文件的第一行,当我们直接运行该脚本文件时,该行告诉操作系统使用用`#!` 符号之后面的解释器以及相应的参数来解释该脚本文件,通过分析第一行,我们发现对应的解释器以及参数是 `/bin/bash -v`,而 `-v` 刚好就是要打印程序的源代码;但是我们在用第二种方法时没有给 `Bash` 传递任何额外的参数,因此,它仅仅解释了脚本文件本身。 140 | 141 | 其他语法细节请直接看[《Shell编程学习笔记》][100]即本书后面的附录一。 142 | 143 | [100]: ../appendix/02-chapter1.markdown 144 | 145 | 146 | ## Shell 程序设计过程 147 | 148 | Shell 语言作为解释型语言,它的程序设计过程跟编译型语言有些区别,其基本过程如下: 149 | 150 | - 设计算法 151 | - 用 Shell 编写脚本程序实现算法 152 | - 直接运行脚本程序 153 | 154 | 可见它没有编译型语言的"麻烦的"编译和链接过程,不过正是因为这样,它出错时调试起来不是很方便,因为语法错误和逻辑错误都在运行时出现。下面我们简单介绍一下调试方法。 155 | 156 | 157 | ## 调试方法介绍 158 | 159 | 可以直接参考资料:[Shell 脚本调试技术](http://www.ibm.com/developerworks/cn/linux/l-cn-shell-debug/index.html) 或者 [BASH 的调试手段](http://www.tinylab.org/bash-debugging-tools/)。 160 | 161 | 162 | ## 小结 163 | 164 | Shell 语言作为一门解释型语言,可以使用大量的现有工具,包括数值计算、符号处理、文件操作、网络操作等,因此,编写过程可能更加高效,但是因为它是解释型的,需要在执行过程中从磁盘上不断调用外部的程序并进行进程之间的切换,在运行效率方面可能有劣势,所以我们应该根据应用场合选择使用 Shell 或是用其他的语言来编程。 165 | 166 | 167 | ## 参考资料 168 | 169 | - [Linux命令行上程序执行的一刹那](http://tinylab.gitbooks.io/cbook/content/zh/chapters/02-chapter3.html) 170 | - [Linux Shell编程学习笔记][100] 171 | - [Shell 脚本调试技术](http://www.ibm.com/developerworks/cn/linux/l-cn-shell-debug/index.html) 172 | - [BASH 的调试手段](http://www.tinylab.org/bash-debugging-tools/) 173 | -------------------------------------------------------------------------------- /zh/chapters/01-chapter10.markdown: -------------------------------------------------------------------------------- 1 | **关注作者公众号**: 2 |
3 | 4 |
5 | 6 | # 总结 7 | 8 | - [前言](#toc_30143_27506_1) 9 | - [Shell 编程范例回顾](#toc_30143_27506_2) 10 | - [常用 Shell 编程“框架”](#toc_30143_27506_3) 11 | - [程序优化技巧](#toc_30143_27506_4) 12 | - [其他注意事项](#toc_30143_27506_5) 13 | 14 | 15 | 16 | ## 前言 17 | 18 | 到这里,整个 Shell 编程系列就要结束了,作为总结篇,主要回顾一下各个小节的主要内容,并总结出 Shell 编程的一些常用框架和相关注意事项等。 19 | 20 | 21 | ## Shell 编程范例回顾 22 | 23 | TODO:主要回顾各小节的内容。 24 | 25 | 26 | ## 常用 Shell 编程“框架” 27 | 28 | TODO:通过分析一些实例总结各种常见问题的解决办法,比如如何保证同一时刻每个程序只有一个运行实体(进程)。 29 | 30 | 31 | ## 程序优化技巧 32 | 33 | TODO:多思考,总会有更简洁和高效的方式。 34 | 35 | 36 | ## 其他注意事项 37 | 38 | TODO:比如小心 `rm -rf` 的用法,如何查看系统帮助等。 39 | 40 | ### 正确使用 `source` 和 `.` 41 | 42 | 仅使用 `source` 和 `.` 来执行你的环境配置等功能,建议不要用于其它用途。 43 | 在Shell中使用脚本时,使用 `bash your_script.sh` 而不是 `source your_script.sh` 或 44 | `. your_script.sh`。 45 | 46 | 当使用 `bash` 的时候,当前的Shell会创建一个新的子进程执行你的脚本;当使用 47 | `source` 和 `.` 时,当前的Shell会直接解释执行 `your_script.sh` 中的代码。如果 `your_script.sh` 48 | 中包含了类似 `exit 0` 这样的代码,使用`source` 和 `.` 执行会导致当前Shell意外地退出。 49 | -------------------------------------------------------------------------------- /zh/chapters/01-chapter2.markdown: -------------------------------------------------------------------------------- 1 | **关注作者公众号**: 2 |
3 | 4 |
5 | 6 | # 数值运算 7 | 8 | - [前言](#toc_22467_18587_1) 9 | - [整数运算](#toc_22467_18587_2) 10 | - [范例:对某个数加 1](#toc_22467_18587_3) 11 | - [范例:从 1 加到某个数](#toc_22467_18587_4) 12 | - [范例:求模](#toc_22467_18587_5) 13 | - [范例:求幂](#toc_22467_18587_6) 14 | - [范例:进制转换](#toc_22467_18587_7) 15 | - [范例:ascii 字符编码](#toc_22467_18587_8) 16 | - [浮点运算](#toc_22467_18587_9) 17 | - [范例:求 1 除以 13,保留 3 位有效数字](#toc_22467_18587_10) 18 | - [范例:余弦值转角度](#toc_22467_18587_11) 19 | - [范例:有一组数据,求人均月收入最高家庭](#toc_22467_18587_12) 20 | - [随机数](#toc_22467_18587_13) 21 | - [范例:获取一个随机数](#toc_22467_18587_14) 22 | - [范例:随机产生一个从 0 到 255 之间的数字](#toc_22467_18587_15) 23 | - [其他运算](#toc_22467_18587_16) 24 | - [范例:获取一系列数](#toc_22467_18587_17) 25 | - [范例:统计字符串中各单词出现次数](#toc_22467_18587_18) 26 | - [范例:统计指定单词出现次数](#toc_22467_18587_19) 27 | - [小结](#toc_22467_18587_20) 28 | - [资料](#toc_22467_18587_21) 29 | - [后记](#toc_22467_18587_22) 30 | 31 | 32 | 33 | ## 前言 34 | 35 | 从本文开始,打算结合平时积累和进一步实践,通过一些范例来介绍Shell编程。因为范例往往能够给人以学有所用的感觉,而且给人以动手实践的机会,从而激发人的学习热情。 36 | 37 | 考虑到易读性,这些范例将非常简单,但是实用,希望它们能够成为我们解决日常问题的参照物或者是“茶余饭后”的小点心,当然这些“点心”肯定还有值得探讨、优化的地方。 38 | 39 | 更复杂有趣的例子请参考 [Advanced Bash-Scripting Guide][2] (一本深入学习 Shell 脚本艺术的书籍)。 40 | 41 | [2]: http://www.tldp.org/LDP/abs/html/ 42 | 43 | 该系列概要: 44 | 45 | * 目的:享受用 Shell 解决问题的乐趣;和朋友们一起交流和探讨。 46 | * 计划:先零散地写些东西,之后再不断补充,最后整理成册。 47 | * 读者:熟悉 Linux 基本知识,如文件系统结构、常用命令行工具、Shell 编程基础等。 48 | * 建议:看范例时,可参考[《Shell基础十二篇》][3]和[《Shell十三问》][4]。 49 | * 环境:如没特别说明,该系列使用的 Shell 将特指 Bash,版本在 3.1.17 以上。 50 | * 说明:该系列不是依据 Shell 语法组织,而是面向某些潜在的操作对象和操作本身,它们反应了现实应用。当然,在这个过程中肯定会涉及到 Shell 的语法。 51 | 52 | [3]: http://bbs.chinaunix.net/forum.php?mod=viewthread&tid=2198159 53 | [4]: http://bbs.chinaunix.net/thread-218853-1-1.html 54 | [5]: http://bbs.chinaunix.net/forum.php?mod=forumdisplay&fid=24&page=1 55 | 56 | 这一篇打算讨论一下 Shell 编程中的基本数值运算,这类运算包括: 57 | 58 | * 数值(包括整数和浮点数)间的加、减、乘、除、求幂、求模等 59 | * 产生指定范围的随机数 60 | * 产生指定范围的数列 61 | 62 | Shell 本身可以做整数运算,复杂一些的运算要通过外部命令实现,比如 `expr`,`bc`,`awk` 等。另外,可通过 `RANDOM` 环境变量产生一个从 0 到 32767 的随机数,一些外部工具,比如 `awk` 可以通过 `rand()` 函数产生随机数。而 `seq` 命令可以用来产生一个数列。下面对它们分别进行介绍。 63 | 64 | 65 | ## 整数运算 66 | 67 | 68 | ### 范例:对某个数加 1 69 | 70 | ``` 71 | $ i=0; 72 | $ ((i++)) 73 | $ echo $i 74 | 1 75 | 76 | $ let i++ 77 | $ echo $i 78 | 2 79 | 80 | $ expr $i + 1 81 | 3 82 | $ echo $i 83 | 2 84 | 85 | $ echo $i 1 | awk '{printf $1+$2}' 86 | 3 87 | ``` 88 | 89 | 说明: `expr` 之后的 `$i`,`+`,1 之间有空格分开。如果进行乘法运算,需要对运算符进行转义,否则 Shell 会把乘号解释为通配符,导致语法错误; `awk` 后面的 `$1` 和 `$2` 分别指 `$i` 和 1,即从左往右的第 1 个和第 2 个数。 90 | 91 | 用 Shell 的内置命令查看各个命令的类型如下: 92 | 93 | ``` 94 | $ type type 95 | type is a shell builtin 96 | $ type let 97 | let is a shell builtin 98 | $ type expr 99 | expr is hashed (/usr/bin/expr) 100 | $ type bc 101 | bc is hashed (/usr/bin/bc) 102 | $ type awk 103 | awk is /usr/bin/awk 104 | ``` 105 | 106 | 从上述演示可看出: `let` 是 Shell 内置命令,其他几个是外部命令,都在 `/usr/bin` 目录下。而 `expr` 和 `bc` 因为刚用过,已经加载在内存的 `hash` 表中。这将有利于我们理解在上一章介绍的脚本多种执行方法背后的原理。 107 | 108 | 说明:如果要查看不同命令的帮助,对于 `let` 和 `type` 等 Shell 内置命令,可以通过 Shell 的一个内置命令 `help` 来查看相关帮助,而一些外部命令可以通过 Shell 的一个外部命令 `man` 来查看帮助,用法诸如 `help let`,`man expr` 等。 109 | 110 | 111 | ### 范例:从 1 加到某个数 112 | 113 | ``` 114 | #!/bin/bash 115 | # calc.sh 116 | 117 | i=0; 118 | while [ $i -lt 10000 ] 119 | do 120 | ((i++)) 121 | done 122 | echo $i 123 | ``` 124 | 125 | 说明:这里通过 `while [ 条件表达式 ]; do .... done` 循环来实现。`-lt` 是小于号 `<`,具体见 `test` 命令的用法:`help test`。 126 | 127 | 如何执行该脚本? 128 | 129 | 办法一:直接把脚本文件当成子 Shell (Bash)的一个参数传入 130 | 131 | ``` 132 | $ bash calc.sh 133 | $ type bash 134 | bash is hashed (/bin/bash) 135 | ``` 136 | 137 | 办法二:是通过 `bash` 的内置命令 `.` 或 `source` 执行 138 | 139 | ``` 140 | $ . ./calc.sh 141 | ``` 142 | 143 | 或 144 | 145 | ``` 146 | $ source ./calc.sh 147 | $ type . 148 | . is a shell builtin 149 | $ type source 150 | source is a shell builtin 151 | ``` 152 | 153 | 办法三:是修改文件为可执行,直接在当前 Shell 下执行 154 | 155 | ``` 156 | $ chmod ./calc.sh 157 | $ ./calc.sh 158 | ``` 159 | 160 | 下面,逐一演示用其他方法计算变量加一,即把 `((i++))` 行替换成下面的某一个: 161 | 162 | ``` 163 | let i++; 164 | 165 | i=$(expr $i + 1) 166 | 167 | i=$(echo $i+1|bc) 168 | 169 | i=$(echo "$i 1" | awk '{printf $1+$2;}') 170 | ``` 171 | 172 | 比较计算时间如下: 173 | 174 | ``` 175 | $ time calc.sh 176 | 10000 177 | 178 | real 0m1.319s 179 | user 0m1.056s 180 | sys 0m0.036s 181 | $ time calc_let.sh 182 | 10000 183 | 184 | real 0m1.426s 185 | user 0m1.176s 186 | sys 0m0.032s 187 | $ time calc_expr.sh 188 | 1000 189 | 190 | real 0m27.425s 191 | user 0m5.060s 192 | sys 0m14.177s 193 | $ time calc_bc.sh 194 | 1000 195 | 196 | real 0m56.576s 197 | user 0m9.353s 198 | sys 0m24.618s 199 | $ time ./calc_awk.sh 200 | 100 201 | 202 | real 0m11.672s 203 | user 0m2.604s 204 | sys 0m2.660s 205 | ``` 206 | 207 | 说明: `time` 命令可以用来统计命令执行时间,这部分时间包括总的运行时间,用户空间执行时间,内核空间执行时间,它通过 `ptrace` 系统调用实现。 208 | 209 | 通过上述比较可以发现 `(())` 的运算效率最高。而 `let` 作为 Shell 内置命令,效率也很高,但是 `expr`,`bc`,`awk` 的计算效率就比较低。所以,在 Shell 本身能够完成相关工作的情况下,建议优先使用 Shell 本身提供的功能。但是 Shell 本身无法完成的功能,比如浮点运算,所以就需要外部命令的帮助。另外,考虑到 Shell 脚本的可移植性,在性能不是很关键的情况下,不要使用某些 Shell 特有的语法。 210 | 211 | `let`,`expr`,`bc` 都可以用来求模,运算符都是 `%`,而 `let` 和 `bc` 可以用来求幂,运算符不一样,前者是 `**`,后者是 `^` 。例如: 212 | 213 | 214 | ### 范例:求模 215 | 216 | ``` 217 | $ expr 5 % 2 218 | 1 219 | 220 | $ let i=5%2 221 | $ echo $i 222 | 1 223 | 224 | $ echo 5 % 2 | bc 225 | 1 226 | 227 | $ ((i=5%2)) 228 | $ echo $i 229 | 1 230 | ``` 231 | 232 | 233 | ### 范例:求幂 234 | 235 | ``` 236 | $ let i=5**2 237 | $ echo $i 238 | 25 239 | 240 | $ ((i=5**2)) 241 | $ echo $i 242 | 243 | 25 244 | $ echo "5^2" | bc 245 | 25 246 | ``` 247 | 248 | 249 | ### 范例:进制转换 250 | 251 | 进制转换也是比较常用的操作,可以用 `Bash` 的内置支持也可以用 `bc` 来完成,例如把 8 进制的 11 转换为 10 进制,则可以: 252 | 253 | ``` 254 | $ echo "obase=10;ibase=8;11" | bc -l 255 | 9 256 | 257 | $ echo $((8#11)) 258 | 9 259 | ``` 260 | 261 | 上面都是把某个进制的数转换为 10 进制的,如果要进行任意进制之间的转换还是 `bc` 比较灵活,因为它可以直接用 `ibase` 和 `obase` 分别指定进制源和进制转换目标。 262 | 263 | 264 | ### 范例:ascii 字符编码 265 | 266 | 如果要把某些字符串以特定的进制表示,可以用 `od` 命令,例如默认的分隔符 `IFS` 包括空格、 `TAB` 以及换行,可以用 `man ascii` 佐证。 267 | 268 | ``` 269 | $ echo -n "$IFS" | od -c 270 | 0000000 t n 271 | 0000003 272 | $ echo -n "$IFS" | od -b 273 | 0000000 040 011 012 274 | 0000003 275 | ``` 276 | 277 | 278 | ## 浮点运算 279 | 280 | `let` 和 `expr` 都无法进行浮点运算,但是 `bc` 和 `awk` 可以。 281 | 282 | 283 | ### 范例:求 1 除以 13,保留 3 位有效数字 284 | 285 | ``` 286 | $ echo "scale=3; 1/13" | bc 287 | .076 288 | 289 | $ echo "1 13" | awk '{printf("%0.3f\n",$1/$2)}' 290 | 0.077 291 | ``` 292 | 293 | 说明: `bc` 在进行浮点运算时需指定精度,否则默认为 0,即进行浮点运算时,默认结果只保留整数。而 `awk` 在控制小数位数时非常灵活,仅仅通过 `printf` 的格式控制就可以实现。 294 | 295 | 补充:在用 `bc` 进行运算时,如果不用 `scale` 指定精度,而在 `bc` 后加上 `-l` 选项,也可以进行浮点运算,只不过这时的默认精度是 20 位。例如: 296 | 297 | ``` 298 | $ echo 1/13100 | bc -l 299 | .00007633587786259541 300 | ``` 301 | 302 | 303 | ### 范例:余弦值转角度 304 | 305 | 用 `bc -l` 计算,可以获得高精度: 306 | 307 | ``` 308 | $ export cos=0.996293; echo "scale=100; a(sqrt(1-$cos^2)/$cos)*180/(a(1)*4)" | bc -l 309 | 4.934954755411383632719834036931840605159706398655243875372764917732 310 | 5495504159766011527078286004072131 311 | ``` 312 | 313 | 当然也可以用 `awk` 来计算: 314 | 315 | ``` 316 | $ echo 0.996293 | awk '{ printf("%s\n", atan2(sqrt(1-$1^2),$1)*180/3.1415926535);}' 317 | 4.93495 318 | ``` 319 | 320 | 321 | ### 范例:有一组数据,求人均月收入最高家庭 322 | 323 | 在这里随机产生了一组测试数据,文件名为 `income.txt`。 324 | 325 | ``` 326 | 1 3 4490 327 | 2 5 3896 328 | 3 4 3112 329 | 4 4 4716 330 | 5 4 4578 331 | 6 6 5399 332 | 7 3 5089 333 | 8 6 3029 334 | 9 4 6195 335 | 10 5 5145 336 | ``` 337 | 338 | 说明:上面的三列数据分别是家庭编号、家庭人数、家庭月总收入。 339 | 340 | 分析:为了求月均收入最高家庭,需要对后面两列数进行除法运算,即求出每个家庭的月均收入,然后按照月均收入排序,找出收入最高家庭。 341 | 342 | 实现: 343 | 344 | ``` 345 | #!/bin/bash 346 | # gettopfamily.sh 347 | 348 | [ $# -lt 1 ] && echo "please input the income file" && exit -1 349 | [ ! -f $1 ] && echo "$1 is not a file" && exit -1 350 | 351 | income=$1 352 | awk '{ 353 | printf("%d %0.2f\n", $1, $3/$2); 354 | }' $income | sort -k 2 -n -r 355 | ``` 356 | 357 | 说明: 358 | 359 | * `[ $# -lt 1 ]`:要求至少输入一个参数,`$#` 是 Shell 中传入参数的个数 360 | * `[ ! -f $1 ]`:要求输入参数是一个文件,`-f` 的用法见 `test` 命令,`help test` 361 | * `income=$1`:把输入参数赋给 income 变量,再作为 `awk` 的参数,即需处理的文件 362 | * `awk`:用文件第三列除以第二列,求出月均收入,考虑到精确性,保留了两位精度 363 | * `sort -k 2 -n -r`:这里对结果的 `awk` 结果的第二列 `-k 2`,即月均收入进行排序,按照数字排序 `-n`,并按照递减的顺序排序 `-r`。 364 | 365 | 演示: 366 | 367 | ``` 368 | $ ./gettopfamily.sh income.txt 369 | 7 1696.33 370 | 9 1548.75 371 | 1 1496.67 372 | 4 1179.00 373 | 5 1144.50 374 | 10 1029.00 375 | 6 899.83 376 | 2 779.20 377 | 3 778.00 378 | 8 504.83 379 | ``` 380 | 381 | 补充:之前的 `income.txt` 数据是随机产生的。在做一些实验时,往往需要随机产生一些数据,在下一小节,我们将详细介绍它。这里是产生 `income.txt` 数据的脚本: 382 | 383 | ``` 384 | #!/bin/bash 385 | # genrandomdata.sh 386 | 387 | for i in $(seq 1 10) 388 | do 389 | echo $i $(($RANDOM/8192+3)) $((RANDOM/10+3000)) 390 | done 391 | ``` 392 | 393 | 说明:上述脚本中还用到`seq`命令产生从1到10的一列数,这个命令的详细用法在该篇最后一节也会进一步介绍。 394 | 395 | 396 | ## 随机数 397 | 398 | 环境变量 `RANDOM` 产生从 0 到 32767 的随机数,而 `awk` 的 `rand()` 函数可以产生 0 到 1 之间的随机数。 399 | 400 | 401 | ### 范例:获取一个随机数 402 | 403 | ``` 404 | $ echo $RANDOM 405 | 81 406 | 407 | $ echo "" | awk '{srand(); printf("%f", rand());}' 408 | 0.237788 409 | ``` 410 | 411 | 说明: `srand()` 在无参数时,采用当前时间作为 `rand()` 随机数产生器的一个 `seed` 。 412 | 413 | 414 | ### 范例:随机产生一个从 0 到 255 之间的数字 415 | 416 | 可以通过 `RANDOM` 变量的缩放和 `awk` 中 `rand()` 的放大来实现。 417 | 418 | ``` 419 | $ expr $RANDOM / 128 420 | 421 | $ echo "" | awk '{srand(); printf("%d\n", rand()*255);}' 422 | ``` 423 | 424 | 思考:如果要随机产生某个 IP 段的 IP 地址,该如何做呢?看例子:友善地获取一个可用的 IP 地址。 425 | 426 | ``` 427 | #!/bin/bash 428 | # getip.sh -- get an usable ipaddress automatically 429 | # author: falcon <zhangjinw@gmail.com> 430 | # update: Tue Oct 30 23:46:17 CST 2007 431 | 432 | # set your own network, default gateway, and the time out of "ping" command 433 | net="192.168.1" 434 | default_gateway="192.168.1.1" 435 | over_time=2 436 | 437 | # check the current ipaddress 438 | ping -c 1 $default_gateway -W $over_time 439 | [ $? -eq 0 ] && echo "the current ipaddress is okey!" && exit -1; 440 | 441 | while :; do 442 | # clear the current configuration 443 | ifconfig eth0 down 444 | # configure the ip address of the eth0 445 | ifconfig eth0 \ 446 | $net.$(($RANDOM /130 +2)) \ 447 | up 448 | # configure the default gateway 449 | route add default gw $default_gateway 450 | # check the new configuration 451 | ping -c 1 $default_gateway -W $over_time 452 | # if work, finish 453 | [ $? -eq 0 ] && break 454 | done 455 | ``` 456 | 457 | 说明:如果你的默认网关地址不是 `192.168.1.1`,请自行配置 `default_gateway`(可以用 `route -n` 命令查看),因为用 `ifconfig` 配置地址时不能配置为网关地址,否则你的IP地址将和网关一样,导致整个网络不能正常工作。 458 | 459 | 460 | ## 其他运算 461 | 462 | 其实通过一个循环就可以产生一系列数,但是有相关工具为什么不用呢!`seq` 就是这么一个小工具,它可以产生一系列数,你可以指定数的递增间隔,也可以指定相邻两个数之间的分割符。 463 | 464 | 465 | ### 范例:获取一系列数 466 | 467 | ``` 468 | $ seq 5 469 | 1 470 | 2 471 | 3 472 | 4 473 | 5 474 | $ seq 1 5 475 | 1 476 | 2 477 | 3 478 | 4 479 | 5 480 | $ seq 1 2 5 481 | 1 482 | 3 483 | 5 484 | $ seq -s: 1 2 5 485 | 1:3:5 486 | $ seq 1 2 14 487 | 1 488 | 3 489 | 5 490 | 7 491 | 9 492 | 11 493 | 13 494 | $ seq -w 1 2 14 495 | 01 496 | 03 497 | 05 498 | 07 499 | 09 500 | 11 501 | 13 502 | $ seq -s: -w 1 2 14 503 | 01:03:05:07:09:11:13 504 | $ seq -f "0x%g" 1 5 505 | 0x1 506 | 0x2 507 | 0x3 508 | 0x4 509 | 0x5 510 | ``` 511 | 512 | 一个比较典型的使用 `seq` 的例子,构造一些特定格式的链接,然后用 `wget` 下载这些内容: 513 | 514 | ``` 515 | $ for i in `seq -f"http://thns.tsinghua.edu.cn/thnsebooks/ebook73/%02g.pdf" 1 21`;do wget -c $i; done 516 | ``` 517 | 518 | 或者 519 | 520 | ``` 521 | $ for i in `seq -w 1 21`;do wget -c "http://thns.tsinghua.edu.cn/thnsebooks/ebook73/$i"; done 522 | ``` 523 | 524 | 补充:在 `Bash` 版本 3 以上,在 `for` 循环的 `in` 后面,可以直接通过 `{1..5}` 更简洁地产生自 1 到 5 的数字(注意,1 和 5 之间只有两个点),例如: 525 | 526 | ``` 527 | $ for i in {1..5}; do echo -n "$i "; done 528 | 1 2 3 4 5 529 | ``` 530 | 531 | 532 | ### 范例:统计字符串中各单词出现次数 533 | 534 | 我们先给单词一个定义:由字母组成的单个或者多个字符系列。 535 | 536 | 首先,统计每个单词出现的次数: 537 | 538 | ``` 539 | $ wget -c http://tinylab.org 540 | $ cat index.html | sed -e "s/[^a-zA-Z]/\n/g" | grep -v ^$ | sort | uniq -c 541 | ``` 542 | 543 | 接着,统计出现频率最高的前10个单词: 544 | 545 | ``` 546 | $ wget -c http://tinylab.org 547 | $ cat index.html | sed -e "s/[^a-zA-Z]/\n/g" | grep -v ^$ | sort | uniq -c | sort -n -k 1 -r | head -10 548 | 524 a 549 | 238 tag 550 | 205 href 551 | 201 class 552 | 193 http 553 | 189 org 554 | 175 tinylab 555 | 174 www 556 | 146 div 557 | 128 title 558 | ``` 559 | 560 | 说明: 561 | 562 | * `cat index.html`: 输出 index.html 文件里的内容 563 | * `sed -e "s/[^a-zA-Z]/\n/g"`: 把非字母字符替换成空格,只保留字母字符 564 | * `grep -v ^$`: 去掉空行 565 | * `sort`: 排序 566 | * `uniq -c`:统计相同行的个数,即每个单词的个数 567 | * `sort -n -k 1 -r`:按照第一列 `-k 1` 的数字 `-n` 逆序 `-r` 排序 568 | * `head -10`:取出前十行 569 | 570 | 571 | ### 范例:统计指定单词出现次数 572 | 573 | 可以考虑采取两种办法: 574 | 575 | * 只统计那些需要统计的单词 576 | * 用上面的算法把所有单词的个数都统计出来,然后再返回那些需要统计的单词给用户 577 | 578 | 不过,这两种办法都可以通过下面的结构来实现。先看办法一: 579 | 580 | ``` 581 | #!/bin/bash 582 | # statistic_words.sh 583 | 584 | if [ $# -lt 1 ]; then 585 | echo "Usage: basename $0 FILE WORDS ...." 586 | exit -1 587 | fi 588 | 589 | FILE=$1 590 | ((WORDS_NUM=$#-1)) 591 | 592 | for n in $(seq $WORDS_NUM) 593 | do 594 | shift 595 | cat $FILE | sed -e "s/[^a-zA-Z]/\n/g" \ 596 | | grep -v ^$ | sort | grep ^$1$ | uniq -c 597 | done 598 | ``` 599 | 600 | 说明: 601 | 602 | * `if 条件部分`:要求至少两个参数,第一个单词文件,之后参数为要统计的单词 603 | * `FILE=$1`: 获取文件名,即脚本之后的第一个字符串 604 | * `((WORDS_NUM=$#-1))`:获取单词个数,即总的参数个数 `$#` 减去文件名参数(1个) 605 | * `for 循环部分`:首先通过 `seq` 产生需要统计的单词个数系列,`shift` 是 Shell 内置变量(请通过 `help shift` 获取帮助),它把用户从命令行中传入的参数依次往后移动位置,并把当前参数作为第一个参数即 `$1`,这样通过 `$1`就可以遍历用户所有输入的单词(仔细一想,这里貌似有数组下标的味道)。你可以考虑把 `shift` 之后的那句替换成 `echo $1` 测试 `shift` 的用法 606 | 607 | 演示: 608 | 609 | ``` 610 | $ chmod +x statistic_words.sh 611 | $ ./statistic_words.sh index.html tinylab linux python 612 | 175 tinylab 613 | 43 linux 614 | 3 python 615 | ``` 616 | 617 | 再看办法二,我们只需要修改 `shift` 之后的那句即可: 618 | 619 | ``` 620 | #!/bin/bash 621 | # statistic_words.sh 622 | 623 | if [ $# -lt 1 ]; then 624 | echo "ERROR: you should input 2 words at least"; 625 | echo "Usage: basename $0 FILE WORDS ...." 626 | exit -1 627 | fi 628 | 629 | FILE=$1 630 | ((WORDS_NUM=$#-1)) 631 | 632 | for n in $(seq $WORDS_NUM) 633 | do 634 | shift 635 | cat $FILE | sed -e "s/[^a-zA-Z]/\n/g" \ 636 | | grep -v ^$ | sort | uniq -c | grep " $1$" 637 | done 638 | ``` 639 | 640 | 演示: 641 | 642 | ``` 643 | $ ./statistic_words.sh index.html tinylab linux python 644 | 175 tinylab 645 | 43 linux 646 | 3 python 647 | ``` 648 | 649 | 说明:很明显,办法一的效率要高很多,因为它提前找出了需要统计的单词,然后再统计,而后者则不然。实际上,如果使用 `grep` 的 `-E` 选项,我们无须引入循环,而用一条命令就可以搞定: 650 | 651 | ``` 652 | $ cat index.html | sed -e "s/[^a-zA-Z]/\n/g" | grep -v ^$ | sort | grep -E "^tinylab$|^linux$" | uniq -c 653 | 43 linux 654 | 175 tinylab 655 | ``` 656 | 657 | 或者 658 | 659 | ``` 660 | $ cat index.html | sed -e "s/[^a-zA-Z]/\n/g" | grep -v ^$ | sort | egrep "^tinylab$|^linux$" | uniq -c 661 | 43 linux 662 | 175 tinylab 663 | ``` 664 | 665 | 说明:需要注意到 `sed` 命令可以直接处理文件,而无需通过 `cat` 命令输出以后再通过管道传递,这样可以减少一个不必要的管道操作,所以上述命令可以简化为: 666 | 667 | ``` 668 | $ sed -e "s/[^a-zA-Z]/\n/g" index.html | grep -v ^$ | sort | egrep "^tinylab$|^linux$" | uniq -c 669 | 43 linux 670 | 175 tinylab 671 | ``` 672 | 673 | 所以,可见这些命令 `sed`,`grep`,`uniq`,`sort` 是多么有用,它们本身虽然只完成简单的功能,但是通过一定的组合,就可以实现各种五花八门的事情啦。对了,统计单词还有个非常有用的命令 `wc -w`,需要用到的时候也可以用它。 674 | 675 | 补充:在 [Advanced Bash-Scripting Guide][2] 一书中还提到 `jot` 命令和 `factor` 命令,由于机器上没有,所以没有测试,`factor` 命令可以产生某个数的所有素数。如: 676 | 677 | ``` 678 | $ factor 100 679 | 100: 2 2 5 5 680 | ``` 681 | 682 | 683 | 684 | ## 小结 685 | 686 | 到这里,Shell 编程范例之数值计算就结束啦。该篇主要介绍了: 687 | 688 | * Shell 编程中的整数运算、浮点运算、随机数的产生、数列的产生 689 | * Shell 的内置命令、外部命令的区别,以及如何查看他们的类型和帮助 690 | * Shell 脚本的几种执行办法 691 | * 几个常用的 Shell 外部命令: `sed`,`awk`,`grep`,`uniq`,`sort` 等 692 | * 范例:数字递增;求月均收入;自动获取 `IP` 地址;统计单词个数 693 | * 其他:相关用法如命令列表,条件测试等在上述范例中都已涉及,请认真阅读之 694 | 695 | 如果您有时间,请温习之。 696 | 697 | 698 | ## 资料 699 | 700 | * [Advanced Bash-Scripting Guide][2] 701 | * [shell 十三问][4] 702 | * [shell 基础十二篇][3] 703 | * SED 手册 704 | * AWK 使用手册 705 | * 几个 Shell 讨论区 706 | * [LinuxSir.org][6] 707 | * [ChinaUnix.net][7] 708 | 709 | [6]: http://www.linuxsir.org/bbs/forumdisplay.php?f=60 710 | [7]: http://bbs.chinaunix.net/forum-24-1.html 711 | 712 | 713 | ## 后记 714 | 715 | 大概花了 3 个多小时才写完,目前是 23:33,该回宿舍睡觉啦,明天起来修改错别字和补充一些内容,朋友们晚安! 716 | 717 | 10 月 31 号,修改部分措辞,增加一篇统计家庭月均收入的范例,添加总结和参考资料,并用附录所有代码。 718 | 719 | Shell 编程是一件非常有趣的事情,如果您想一想:上面计算家庭月均收入的例子,然后和用 `M$ Excel` 来做这个工作比较,你会发现前者是那么简单和省事,而且给您以运用自如的感觉。 720 | -------------------------------------------------------------------------------- /zh/chapters/01-chapter3.markdown: -------------------------------------------------------------------------------- 1 | **关注作者公众号**: 2 |
3 | 4 |
5 | 6 | # 布尔运算 7 | 8 | - [前言](#toc_17877_9500_1) 9 | - [常规的布尔运算](#toc_17877_9500_2) 10 | - [在 Shell 下如何进行逻辑运算](#toc_17877_9500_3) 11 | - [范例:true or false](#toc_17877_9500_4) 12 | - [范例:与运算](#toc_17877_9500_5) 13 | - [范例:或运算](#toc_17877_9500_6) 14 | - [范例:非运算,即取反](#toc_17877_9500_7) 15 | - [Bash 里头的 true 和 false 是我们通常认为的 1 和 0 么?](#toc_17877_9500_8) 16 | - [范例:返回值 v.s. 逻辑值](#toc_17877_9500_9) 17 | - [范例:查看 true 和 false 帮助和类型](#toc_17877_9500_10) 18 | - [条件测试](#toc_17877_9500_11) 19 | - [条件测试基本使用](#toc_17877_9500_12) 20 | - [范例:数值测试](#toc_17877_9500_13) 21 | - [范例:字符串测试](#toc_17877_9500_14) 22 | - [范例:文件测试](#toc_17877_9500_15) 23 | - [各种逻辑测试的组合](#toc_17877_9500_16) 24 | - [范例:如果 a,b,c 都等于下面对应的值,那么打印 YES,通过 -a 进行与测试](#toc_17877_9500_17) 25 | - [范例:测试某个“东西”是文件或者目录,通过 -o 进行“或”运算](#toc_17877_9500_18) 26 | - [范例:测试某个“东西”是否为文件,测试 `!` 非运算](#toc_17877_9500_19) 27 | - [比较 -a 与 &&, -o 与 ||, ! test 与 test !](#toc_17877_9500_20) 28 | - [范例:要求某文件可执行且有内容,用 -a 和 && 分别实现](#toc_17877_9500_21) 29 | - [范例:要求某个字符串要么为空,要么和某个字符串相等](#toc_17877_9500_22) 30 | - [范例:测试某个数字不满足指定的所有条件](#toc_17877_9500_23) 31 | - [命令列表](#toc_17877_9500_24) 32 | - [命令列表的执行规律](#toc_17877_9500_25) 33 | - [范例:如果 ping 通 www.lzu.edu.cn,那么打印连通信息](#toc_17877_9500_26) 34 | - [命令列表的作用](#toc_17877_9500_27) 35 | - [范例:在脚本里判断程序的参数个数,和参数类型](#toc_17877_9500_28) 36 | - [小结](#toc_17877_9500_29) 37 | 38 | 39 | 40 | ## 前言 41 | 42 | 上个礼拜介绍了[Shell编程范例之数值运算][100],对 Shell 下基本数值运算方法做了简单的介绍,这周将一起探讨布尔运算,即如何操作“真假值”。 43 | 44 | [100]: 01-chapter2.markdown 45 | 46 | 在 Bash 里有这样的常量(实际上是两个内置命令,在这里我们姑且这么认为,后面将介绍),即 true 和 false,一个表示真,一个表示假。对它们可以进行与、或、非运算等常规的逻辑运算,在这一节,我们除了讨论这些基本逻辑运算外,还将讨论Shell编程中的**条件测试**和**命令列表**,并介绍它们和布尔运算的关系。 47 | 48 | 49 | ## 常规的布尔运算 50 | 51 | 这里主要介绍 `Bash` 里头常规的逻辑运算,与、或、非。 52 | 53 | 54 | ### 在 Shell 下如何进行逻辑运算 55 | 56 | 57 | #### 范例:true or false 58 | 59 | 单独测试 `true` 和 `false`,可以看出 `true` 是真值,`false` 为假 60 | 61 | $ if true;then echo "YES"; else echo "NO"; fi 62 | YES 63 | $ if false;then echo "YES"; else echo "NO"; fi 64 | NO 65 | 66 | 67 | #### 范例:与运算 68 | 69 | $ if true && true;then echo "YES"; else echo "NO"; fi 70 | YES 71 | $ if true && false;then echo "YES"; else echo "NO"; fi 72 | NO 73 | $ if false && false;then echo "YES"; else echo "NO"; fi 74 | NO 75 | $ if false && true;then echo "YES"; else echo "NO"; fi 76 | NO 77 | 78 | 79 | #### 范例:或运算 80 | 81 | $ if true || true;then echo "YES"; else echo "NO"; fi 82 | YES 83 | $ if true || false;then echo "YES"; else echo "NO"; fi 84 | YES 85 | $ if false || true;then echo "YES"; else echo "NO"; fi 86 | YES 87 | $ if false || false;then echo "YES"; else echo "NO"; fi 88 | NO 89 | 90 | 91 | #### 范例:非运算,即取反 92 | 93 | $ if ! false;then echo "YES"; else echo "NO"; fi 94 | YES 95 | $ if ! true;then echo "YES"; else echo "NO"; fi 96 | NO 97 | 98 | 可以看出 `true` 和 `false` 按照我们对逻辑运算的理解进行着,但是为了能够更好的理解 Shell 对逻辑运算的实现,我们还得弄清楚,`true` 和 `false` 是怎么工作的? 99 | 100 | 101 | ### Bash 里头的 true 和 false 是我们通常认为的 1 和 0 么? 102 | 103 | 回答是:否。 104 | 105 | 106 | #### 范例:返回值 v.s. 逻辑值 107 | 108 | `true` 和 `false` 它们本身并非逻辑值,它们都是 Shell 的内置命令,只是它们的返回值是一个“逻辑值”: 109 | 110 | $ true 111 | $ echo $? 112 | 0 113 | $ false 114 | $ echo $? 115 | 1 116 | 117 | 可以看到 `true` 返回了 0,而 `false` 则返回了 1 。跟我们离散数学里学的真值 1 和 0 并不是对应的,而且相反的。 118 | 119 | 120 | #### 范例:查看 true 和 false 帮助和类型 121 | 122 | $ help true false 123 | true: true 124 | Return a successful result. 125 | false: false 126 | Return an unsuccessful result. 127 | $ type true false 128 | true is a shell builtin 129 | false is a shell builtin 130 | 131 | 说明:`$?` 是一个特殊变量,存放有上一次进程的结束状态(退出状态码)。 132 | 133 | 从上面的操作不难联想到在 C 语言程序设计中为什么会强调在 `main` 函数前面加上 `int`,并在末尾加上 `return 0` 。因为在 Shell 里,将把 0 作为程序是否成功结束的标志,这就是 Shell 里头 `true` 和 `false` 的实质,它们用以反应某个程序是否正确结束,而并非传统的真假值(1 和 0),相反地,它们返回的是 0 和 1 。不过庆幸地是,我们在做逻辑运算时,无须关心这些。 134 | 135 | 136 | ## 条件测试 137 | 138 | 从上节中,我们已经清楚地了解了 Shell 下的“逻辑值”是什么:是进程退出时的返回值,如果成功返回,则为真,如果不成功返回,则为假。 139 | 140 | 而条件测试正好使用了 `test` 这么一个指令,它用来进行数值测试(各种数值属性测试)、字符串测试(各种字符串属性测试)、文件测试(各种文件属性测试),我们通过判断对应的测试是否成功,从而完成各种常规工作,再加上各种测试的逻辑组合后,将可以完成更复杂的工作。 141 | 142 | 143 | ### 条件测试基本使用 144 | 145 | 146 | #### 范例:数值测试 147 | 148 | $ if test 5 -eq 5;then echo "YES"; else echo "NO"; fi 149 | YES 150 | $ if test 5 -ne 5;then echo "YES"; else echo "NO"; fi 151 | NO 152 | 153 | 154 | #### 范例:字符串测试 155 | 156 | $ if test -n "not empty";then echo "YES"; else echo "NO"; fi 157 | YES 158 | $ if test -z "not empty";then echo "YES"; else echo "NO"; fi 159 | NO 160 | $ if test -z "";then echo "YES"; else echo "NO"; fi 161 | YES 162 | $ if test -n "";then echo "YES"; else echo "NO"; fi 163 | NO 164 | 165 | 166 | #### 范例:文件测试 167 | 168 | $ if test -f /boot/System.map; then echo "YES"; else echo "NO"; fi 169 | YES 170 | $ if test -d /boot/System.map; then echo "YES"; else echo "NO"; fi 171 | NO 172 | 173 | 174 | ### 各种逻辑测试的组合 175 | 176 | 177 | #### 范例:如果 a,b,c 都等于下面对应的值,那么打印 YES,通过 -a 进行"与"测试 178 | 179 | $ a=5;b=4;c=6; 180 | $ if test $a -eq 5 -a $b -eq 4 -a $c -eq 6; then echo "YES"; else echo "NO"; fi 181 | YES 182 | 183 | 184 | #### 范例:测试某个“东西”是文件或者目录,通过 -o 进行“或”运算 185 | 186 | $ if test -f /etc/profile -o -d /etc/profile;then echo "YES"; else echo "NO"; fi 187 | YES 188 | 189 | 190 | #### 范例:测试某个“东西”是否为文件,测试 `!` 非运算 191 | 192 | $ if test ! -f /etc/profile; then echo "YES"; else echo "NO"; fi 193 | NO 194 | 195 | 上面仅仅演示了 `test` 命令一些非常简单的测试,你可以通过 `help test` 获取 `test` 的更多用法。需要注意的是,`test` 命令内部的逻辑运算和 Shell 的逻辑运算符有一些区别,对应的为 `-a` 和 `&&`,`-o` 与 `||`,这两者不能混淆使用。而非运算都是 `!`,下面对它们进行比较。 196 | 197 | 198 | ### 比较 -a 与 &&, -o 与 ||, ! test 与 test ! 199 | 200 | 201 | #### 范例:要求某文件可执行且有内容,用 -a 和 && 分别实现 202 | 203 | $ cat > test.sh 204 | #!/bin/bash 205 | echo "test" 206 | [CTRL+D] # 按下组合键CTRL与D结束cat输入,后同,不再注明 207 | $ chmod +x test.sh 208 | $ if test -s test.sh -a -x test.sh; then echo "YES"; else echo "NO"; fi 209 | YES 210 | $ if test -s test.sh && test -x test.sh; then echo "YES"; else echo "NO"; fi 211 | YES 212 | 213 | 214 | #### 范例:要求某个字符串要么为空,要么和某个字符串相等 215 | 216 | $ str1="test" 217 | $ str2="test" 218 | $ if test -z "$str2" -o "$str2" == "$str1"; then echo "YES"; else echo "NO"; fi 219 | YES 220 | $ if test -z "$str2" || test "$str2" == "$str1"; then echo "YES"; else echo "NO"; fi 221 | YES 222 | 223 | 224 | #### 范例:测试某个数字不满足指定的所有条件 225 | 226 | $ i=5 227 | $ if test ! $i -lt 5 -a $i -ne 6; then echo "YES"; else echo "NO"; fi 228 | YES 229 | $ if ! test $i -lt 5 -a $i -eq 6; then echo "YES"; else echo "NO"; fi 230 | YES 231 | 232 | 很容易找出它们的区别,`-a` 和 `-o` 作为测试命令的参数用在测试命令的内部,而 `&&` 和 `||` 则用来运算测试的返回值,`!` 为两者通用。需要关注的是: 233 | 234 | - 有时可以不用 `!` 运算符,比如 `-eq` 和 `-ne` 刚好相反,可用于测试两个数值是否相等; `-z` 与 `-n` 也是对应的,用来测试某个字符串是否为空 235 | - 在 `Bash` 里,`test` 命令可以用[] 运算符取代,但是需要注意,[` 之后与 `] 之前需要加上额外的空格 236 | - 在测试字符串时,所有变量建议用双引号包含起来,以防止变量内容为空时出现仅有测试参数,没有测试内容的情况 237 | 238 | 下面我们用实例来演示上面三个注意事项: 239 | 240 | - `-ne` 和 `-eq` 对应的,我们有时候可以免去 `!` 运算 241 | 242 | $ i=5 243 | $ if test $i -eq 5; then echo "YES"; else echo "NO"; fi 244 | YES 245 | $ if test $i -ne 5; then echo "YES"; else echo "NO"; fi 246 | NO 247 | $ if test ! $i -eq 5; then echo "YES"; else echo "NO"; fi 248 | NO 249 | 250 | - 用 `[ ]` 可以取代 `test`,这样看上去会“美观”很多 251 | 252 | $ if [ $i -eq 5 ]; then echo "YES"; else echo "NO"; fi 253 | YES 254 | $ if [ $i -gt 4 ] && [ $i -lt 6 ]; then echo "YES"; else echo "NO"; fi 255 | YES 256 | 257 | - 记得给一些字符串变量加上 `""`,记得 `[` 之后与 `]` 之前多加一个空格 258 | 259 | $ str="" 260 | $ if [ "$str" = "test"]; then echo "YES"; else echo "NO"; fi 261 | -bash: [: missing `]' 262 | NO 263 | $ if [ $str = "test" ]; then echo "YES"; else echo "NO"; fi 264 | -bash: [: =: unary operator expected 265 | NO 266 | $ if [ "$str" = "test" ]; then echo "YES"; else echo "NO"; fi 267 | NO 268 | 269 | 到这里,**条件测试**就介绍完了,下面介绍**命令列表**,实际上在上面我们已经使用过了,即多个test命令的组合,通过 `&&`,`||` 和 `!` 组合起来的命令序列。这种命令序列可以有效替换 `if/then` 的条件分支结构。这不难想到我们在 C 语言程序设计中经常做的如下的选择题(很无聊的例子,但是有意义):下面是否会打印 `j`,如果打印,将打印什么? 270 | 271 | #include 272 | int main() 273 | { 274 | int i, j; 275 | 276 | i=5;j=1; 277 | if ((i==5) && (j=5))  printf("%d\n", j); 278 | 279 | return 0; 280 | } 281 | 282 | 很容易知道将打印数字 5,因为 `i==5` 这个条件成立,而且随后是 `&&`,要判断整个条件是否成立,我们得进行后面的判断,可是这个判断并非常规的判断,而是先把 `j` 修改为 5,再转换为真值,所以条件为真,打印出 5 。因此,这句可以解释为:如果 `i` 等于 5,那么把 `j` 赋值为 5,如果 `j` 大于 1 (因为之前已经为真),那么打印出 `j` 的值。这样用 `&&` 连结起来的判断语句替代了两个 `if` 条件分支语句。 283 | 284 | 正是基于逻辑运算特有的性质,我们可以通过 `&&`,`||` 来取代 `if/then` 等条件分支结构,这样就产生了命令列表。 285 | 286 | 287 | ## 命令列表 288 | 289 | 290 | ### 命令列表的执行规律 291 | 292 | 命令列表的执行规律符合逻辑运算的运算规律,用 `&&` 连接起来的命令,如果前者成功返回,将执行后面的命令,反之不然;用 `||` 连接起来的命令,如果前者成功返回,将不执行后续命令,反之不然。 293 | 294 | 295 | #### 范例:如果 ping 通 www.lzu.edu.cn,那么打印连通信息 296 | 297 | $ ping -c 1 www.lzu.edu.cn -W 1 && echo "=======connected=======" 298 | 299 | 非常有趣的问题出来了,即我们上面已经提到的:为什么要让 C 程序在 `main()` 函数的最后返回 0 ?如果不这样,把这种程序放入命令列表会有什么样的结果?你自己写个简单的 C 程序,然后放入命令列表看看。 300 | 301 | 302 | ### 命令列表的作用 303 | 304 | 有时用命令列表取代 `if/then` 等条件分支结构可以省掉一些代码,而且使得程序比较美观、易读,例如: 305 | 306 | 307 | #### 范例:在脚本里判断程序的参数个数,和参数类型 308 | 309 | #!/bin/bash 310 | 311 | echo $# 312 | echo $1 313 | if [ $# -eq 1 ] && (echo $1 | grep '^[0-9]*$' >/dev/null);then 314 | echo "YES" 315 | fi 316 | 317 | 说明:上例要求参数个数为 1 并且类型为数字。 318 | 319 | 再加上 `exit 1`,我们将省掉 `if/then` 结构 320 | 321 | #!/bin/bash 322 | 323 | echo $# 324 | echo $1 325 | ! ([ $# -eq 1 ] && (echo $1 | grep '^[0-9]*$' >/dev/null)) && exit 1 326 | 327 | echo "YES" 328 | 329 | 这样处理后,对程序参数的判断仅仅需要简单的一行代码,而且变得更美观。 330 | 331 | 332 | ## 小结 333 | 334 | 这一节介绍了 Shell 编程中的逻辑运算,条件测试和命令列表。 335 | -------------------------------------------------------------------------------- /zh/chapters/01-chapter5.markdown: -------------------------------------------------------------------------------- 1 | **关注作者公众号**: 2 |
3 | 4 |
5 | 6 | # 文件操作 7 | 8 | - [前言](#toc_11738_32168_1) 9 | - [文件的各种属性](#toc_11738_32168_2) 10 | - [文件类型](#toc_11738_32168_3) 11 | - [范例:在命令行简单地区分各类文件](#toc_11738_32168_4) 12 | - [范例:简单比较它们的异同](#toc_11738_32168_5) 13 | - [范例:普通文件再分类](#toc_11738_32168_6) 14 | - [文件属主](#toc_11738_32168_7) 15 | - [范例:修改文件的属主](#toc_11738_32168_8) 16 | - [范例:查看文件的属主](#toc_11738_32168_9) 17 | - [范例:分析文件属主实现的背后原理](#toc_11738_32168_10) 18 | - [文件权限](#toc_11738_32168_11) 19 | - [范例:给文件添加读、写、可执行权限](#toc_11738_32168_12) 20 | - [范例:授权普通用户执行root所属命令](#toc_11738_32168_13) 21 | - [范例:给重要文件加锁](#toc_11738_32168_14) 22 | - [文件大小](#toc_11738_32168_15) 23 | - [范例:查看普通文件和链接文件](#toc_11738_32168_16) 24 | - [范例:查看设备文件](#toc_11738_32168_17) 25 | - [范例:查看目录](#toc_11738_32168_18) 26 | - [文件访问、更新、修改时间](#toc_11738_32168_19) 27 | - [文件名](#toc_11738_32168_20) 28 | - [文件的基本操作](#toc_11738_32168_21) 29 | - [范例:创建文件](#toc_11738_32168_22) 30 | - [范例:删除文件](#toc_11738_32168_23) 31 | - [范例:复制文件](#toc_11738_32168_24) 32 | - [范例:修改文件名](#toc_11738_32168_25) 33 | - [范例:编辑文件](#toc_11738_32168_26) 34 | - [范例:压缩/解压缩文件](#toc_11738_32168_27) 35 | - [范例:文件搜索(文件定位)](#toc_11738_32168_28) 36 | - [参考资料](#toc_11738_32168_29) 37 | - [后记](#toc_11738_32168_30) 38 | 39 | 40 | 41 | ## 前言 42 | 43 | 这周来探讨文件操作。 44 | 45 | 在日常学习和工作中,总是在不断地和各种文件打交道,这些文件包括普通文本文件,可以执行的程序,带有控制字符的文档、存放各种文件的目录、网络套接字文件、设备文件等。这些文件又具有诸如属主、大小、创建和修改日期等各种属性。文件对应文件系统的一些数据块,对应磁盘等存储设备的一片连续空间,对应于显示设备却是一些具有不同形状的字符集。 46 | 47 | 在这一节,为了把关注点定位在文件本身,不会深入探讨文件系统以及存储设备是如何组织文件的(在后续章节再深入探讨),而是探讨对它最熟悉的一面,即把文件当成是一系列的字符(一个 `byte`)集合看待。因此之前介绍的[《 Shell 编程范例之字符串操作》][100]在这里将会得到广泛的应用,关于普通文件的读写操作已经非常熟练,那就是“重定向”,这里会把这部分独立出来介绍。关于文件在 Linux 下的“数字化”(文件描述符)高度抽象,“一切皆为文件”的哲学在 Shell 编程里也得到了深刻的体现。 48 | 49 | [100]: 01-chapter4.markdown 50 | 51 | 下面先来介绍文件的各种属性,然后介绍普通文件的一般操作。 52 | 53 | 54 | ## 文件的各种属性 55 | 56 | 首先通过文件的结构体来看看文件到底有哪些属性: 57 | 58 | struct stat { 59 | dev_t st_dev; /* 设备   */ 60 | ino_t st_ino; /* 节点   */ 61 | mode_t st_mode; /* 模式   */ 62 | nlink_t st_nlink; /* 硬连接 */ 63 | uid_t st_uid; /* 用户ID */ 64 | gid_t st_gid; /* 组ID   */ 65 | dev_t st_rdev; /* 设备类型 */ 66 | off_t st_off; /* 文件字节数 */ 67 | unsigned long  st_blksize; /* 块大小 */ 68 | unsigned long st_blocks; /* 块数   */ 69 | time_t st_atime; /* 最后一次访问时间 */ 70 | time_t st_mtime; /* 最后一次修改时间 */ 71 | time_t st_ctime; /* 最后一次改变时间(指属性) */ 72 | }; 73 | 74 | 下面逐次来了解这些属性,如果需要查看某个文件属性,用 `stat` 命令就可,它会按照上面的结构体把信息列出来。另外,`ls` 命令在跟上一定参数后也可以显示文件的相关属性,比如 `-l` 参数。 75 | 76 | 77 | ### 文件类型 78 | 79 | 文件类型对应于上面的 `st_mode`, 文件类型有很多,比如常规文件、符号链接(硬链接、软链接)、管道文件、设备文件(符号设备、块设备)、socket文件等,不同的文件类型对应不同的功能和作用。 80 | 81 | 82 | #### 范例:在命令行简单地区分各类文件 83 | 84 | $ ls -l 85 | total 12 86 | drwxr-xr-x 2 root root 4096 2007-12-07 20:08 directory_file 87 | prw-r--r-- 1 root root 0 2007-12-07 20:18 fifo_pipe 88 | brw-r--r-- 1 root root 3, 1 2007-12-07 21:44 hda1_block_dev_file 89 | crw-r--r-- 1 root root 1, 3 2007-12-07 21:43 null_char_dev_file 90 | -rw-r--r-- 2 root root 506 2007-12-07 21:55 regular_file 91 | -rw-r--r-- 2 root root 506 2007-12-07 21:55 regular_file_hard_link 92 | lrwxrwxrwx 1 root root 12 2007-12-07 20:15 regular_file_soft_link -> regular_file 93 | $ stat directory_file/ 94 | File: `directory_file/' 95 | Size: 4096 Blocks: 8 IO Block: 4096 directory 96 | Device: 301h/769d Inode: 521521 Links: 2 97 | Access: (0755/drwxr-xr-x) Uid: ( 0/ root) Gid: ( 0/ root) 98 | Access: 2007-12-07 20:08:18.000000000 +0800 99 | Modify: 2007-12-07 20:08:18.000000000 +0800 100 | Change: 2007-12-07 20:08:18.000000000 +0800 101 | $ stat null_char_dev_file 102 | File: `null_char_dev_file' 103 | Size: 0 Blocks: 0 IO Block: 4096 character special file 104 | Device: 301h/769d Inode: 521240 Links: 1 Device type: 1,3 105 | Access: (0644/crw-r--r--) Uid: ( 0/ root) Gid: ( 0/ root) 106 | Access: 2007-12-07 21:43:38.000000000 +0800 107 | Modify: 2007-12-07 21:43:38.000000000 +0800 108 | Change: 2007-12-07 21:43:38.000000000 +0800 109 | 110 | 说明:通过 `ls` 命令结果每行的第一个字符可以看到,它们之间都不相同,这正好反应了不同文件的类型。 `d` 表示目录,`-` 表示普通文件(或者硬链接),`l` 表示符号链接,`p` 表示管道文件,`b` 和 `c` 分别表示块设备和字符设备(另外 `s` 表示 `socket` 文件)。在 `stat` 命令的结果中,可以在第二行的最后找到说明,从上面的操作可以看出,`directory_file` 是目录,`stat` 命令的结果中用 `directory` 表示,而 `null_char_dev_file` 它则用 `character special file` 说明。 111 | 112 | 113 | #### 范例:简单比较它们的异同 114 | 115 | 通常只会用到目录、普通文件、以及符号链接,很少碰到其他类型的文件,不过这些文件还是各有用处的,如果要做嵌入式开发或者进程通信等,可能会涉及到设备文件、有名管道(FIFO)。下面通过简单的操作来反应它们之间的区别(具体原理会在下一节[《Shell 编程范例之文件系统》][101]介绍,如果感兴趣,也可以提前到网上找找设备文件的作用、块设备和字符设备的区别、以及驱动程序中如何编写相关设备驱动等)。 116 | 117 | [101]: 01-chapter6.markdown 118 | 119 | 对于普通文件:就是一系列字符的集合,所以可以读、写等 120 | 121 | $ echo "hello, world" > regular_file 122 | $ cat regular_file 123 | hello, world 124 | 125 | 在目录中可以创建新文件,所以目录还有叫法:文件夹,到后面会分析目录文件的结构体,它实际上存放了它下面的各个文件的文件名。 126 | 127 | $ cd directory_file 128 | $ touch file1 file2 file3 129 | 130 | 对于有名管道,操作起来比较有意思:如果要读它,除非有内容,否则阻塞;如果要写它,除非有人来读,否则阻塞。它常用于进程通信中。可以打开两个终端 `terminal1` 和 `terminal2`,试试看: 131 | 132 | terminal1$ cat fifo_pipe #刚开始阻塞在这里,直到下面的写动作发生,才打印test字符串 133 | terminal2$ echo "test" > fifo_pipe 134 | 135 | 关于块设备,字符设备,设备文件对应于 `/dev/hda1` 和 `/dev/null`,如果用过 U 盘,或者是写过简单的脚本的话,这样的用法应该用过: 136 | :-) 137 | 138 | $ mount hda1_block_dev_file /mnt #挂载硬盘的第一个分区到/mnt下(关于挂载的原理,在下一节讨论) 139 | $ echo "fewfewfef" > /dev/null #/dev/null像个黑洞,什么东西丢进去都消失殆尽 140 | 141 | 最后两个文件分别是 `regular_file` 文件的硬链接和软链接,去读写它们,他们的内容是相同的,不过去删除它们,他们却互不相干,硬链接和软链接又有何不同呢?前者可以说就是原文件,后者呢只是有那么一个 `inode`,但没有实际的存储空间,建议用 `stat` 命令查看它们之间的区别,包括它们的 `Blocks`,`inode` 等值,也可以考虑用 `diff` 比较它们的大小。 142 | 143 | $ ls regular_file* 144 | ls regular_file* -l 145 | -rw-r--r-- 2 root root 204800 2007-12-07 22:30 regular_file 146 | -rw-r--r-- 2 root root 204800 2007-12-07 22:30 regular_file_hard_link 147 | lrwxrwxrwx 1 root root 12 2007-12-07 20:15 regular_file_soft_link -> regular_file 148 | $ rm regular_file # 删除原文件 149 | $ cat regular_file_hard_link # 硬链接还在,而且里头的内容还有呢 150 | fefe 151 | $ cat regular_file_soft_link 152 | cat: regular_file_soft_link: No such file or directory 153 | 154 | 虽然软链接文件本身还在,不过因为它本身不存储内容,所以读不到东西,这就是软链接和硬链接的区别。 155 | 156 | 需要注意的是,硬链接不可以跨文件系统,而软链接则可以。另外,也不允许给目录创建硬链接。 157 | 158 | 159 | #### 范例:普通文件再分类 160 | 161 | 文件类型从 Linux 文件系统那么一个级别分了以上那么多类型,不过普通文件还是可以再分的(根据文件内容的”数据结构“分),比如常见的文本文件,可执行的 `ELF` 文件,`odt` 文档,`jpg` 图片格式,`swap` 分区文件,`pdf` 文件。除了文本文件外,它们大多是二进制文件,有特定的结构,因此需要有专门的工具来创建和编辑它们。关于各类文件的格式,可以参考相关文档标准。不过非常值得深入了解 Linux 下可执行的 `ELF` 文件的工作原理,如果有兴趣,建议阅读一下参考资料中和 `ELF` 文件相关部分,这一部分对于嵌入式 Linux 工程师至关重要。 162 | 163 | 虽然各类普通文件都有专属的操作工具,但是还是可以直接读、写它们,这里先提到这么几个工具,回头讨论细节。 164 | 165 | - `od` :以八进制或者其他格式“导出”文件内容。 166 | - `strings` :读出文件中的字符(可打印的字符) 167 | - `gcc`,`gdb`,`readelf`,objdump` 等: `ELF` 文件分析、处理工具(`gcc` 编译器、 `gdb` 调试器、 `readelf` 分析 ELF 文件,`objdump` 反编译工具) 168 | 169 | 再补充一个非常重要的命令,`file`,这个命令用来查看各类文件的属性。和 `stat` 命令相比,它可以进一步识别普通文件,即 `stat` 命令显示的 `regular file` 。因为 `regular file` 可以有各种不同的结构,因此在操作系统的支持下得到不同的解释,执行不同的动作。虽然,Linux 下,文件也会加上特定的后缀以便用户能够方便地识别文件的类型,但是 Linux 操作系统根据文件头识别各类文件,而不是文件后缀,这样在解释相应的文件时就更不容易出错。下面简单介绍 `file` 命令的用法。 170 | 171 | $ file ./ 172 | ./: directory 173 | $ file /etc/profile 174 | /etc/profile: ASCII English text 175 | $ file /lib/libc-2.5.so 176 | /lib/libc-2.5.so: ELF 32-bit LSB shared object, Intel 80386, version 1 (SYSV), not stripped 177 | $ file /bin/test 178 | /bin/test: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), stripped 179 | $ file /dev/hda 180 | /dev/hda: block special (3/0) 181 | $ file /dev/console 182 | /dev/console: character special (5/1) 183 | $ cp /etc/profile . 184 | $ tar zcf profile.tar.gz profile 185 | $ file profile.tar.gz 186 | profile.tar.gz: gzip compressed data, from Unix, last modified: Tue Jan 4 18:53:53 2000 187 | $ mkfifo fifo_test 188 | $ file fifo_test 189 | fifo_test: fifo (named pipe) 190 | 191 | 更多用法见 `file` 命令的手册,关于 `file` 命令的实现原理,请参考 `magic` 的手册(看看 `/etc/file/magic` 文件,了解什么是文件的 `magic number` 等)。 192 | 193 | 194 | ### 文件属主 195 | 196 | Linux 作为一个多用户系统,为多用户使用同一个系统提供了极大的方便,比如对于系统上的文件,它通过属主来区分不同的用户,以便分配它们对不同文件的操作权限。为了更方便地管理,文件属主包括该文件所属用户,以及该文件所属的用户组,因为用户可以属于多个组。先来简单介绍 Linux 下用户和组的管理。 197 | 198 | Linux 下提供了一组命令用于管理用户和组,比如用于创建用户的 `useradd` 和 `groupadd`,用于删除用户的 `userdel` 和 `groupdel`,另外,`passwd` 命令用于修改用户密码。当然,Linux 还提供了两个相应的配置,即 `/etc/passwd` 和 `/etc/group`,另外,有些系统还把密码单独放到了配置文件 `/etc/shadow` 中。关于它们的详细用法请参考后面的资料,这里不再介绍,仅介绍文件和用户之间的一些关系。 199 | 200 | 201 | #### 范例:修改文件的属主 202 | 203 | $ chown 用户名:组名 文件名 204 | 205 | 如果要递归地修改某个目录下所有文件的属主,可以添加 `-R` 选项。 206 | 207 | 从本节开头列出的文件结构体中,可以看到仅仅有用户 `ID` 和组 `ID` 的信息,但 `ls -l` 的结果却显示了用户名和组名信息,这个是怎么实现的呢?下面先看看 `-n` 的结果: 208 | 209 | 210 | #### 范例:查看文件的属主 211 | 212 | $ ls -n regular_file 213 | -rw-r--r-- 1 0 0 115 2007-12-07 23:45 regular_file 214 | $ ls -l regular_file 215 | -rw-r--r-- 1 root root 115 2007-12-07 23:45 regular_file 216 | 217 | 218 | #### 范例:分析文件属主实现的背后原理 219 | 220 | 可以看到,`ls -n` 显示了用户 `ID` 和组 `ID`,而 `ls -l` 显示了它们的名字。还记得上面提到的两个配置文件 `/etc/passwd` 和 `/etc/group` 文件么?它们分别存放了用户 `ID` 和用户名,组 `ID` 和组名的对应关系,因此很容易想到 `ls -l` 命令在实现时是如何通过文件结构体的 `ID` 信息找到它们对应的名字信息的。如果想对 `ls -l` 命令的实现有更进一步的了解,可以用 `strace` 跟踪看看它是否读取了这两个配置文件。 221 | 222 | $ strace -f -o strace.log ls -l regular_file 223 | $ cat strace.log | egrep "passwd|group|shadow" 224 | 2989 open("/etc/passwd", O_RDONLY) = 3 225 | 2989 open("/etc/group", O_RDONLY) = 3 226 | 227 | 说明: `strace` 可以用来跟踪系统调用和信号。如同 `gdb` 等其他强大的工具一样,它基于系统的 `ptrace` 系统调用实现。 228 | 229 | 实际上,把属主和权限分开介绍不太好,因为只有它们两者结合才使得多用户系统成为可能,否则无法隔离不同用户对某个文件的操作,所以下面来介绍文件操作权限。 230 | 231 | 232 | ### 文件权限 233 | 234 | 从 `ls -l` 命令的结果的第一列的后 9 个字符中,可以看到类似这样的信息 `rwxr-xr-x`,它们对应于文件结构体的 `st_mode` 部分(`st_mode` 包含文件类型信息和文件权限信息两部分)。这类信息可以分成三部分,即 `rwx`,`r-x`,`r-x`,分别对应该文件所属用户、所属组、其他组对该文件的操作权限,如果有 `rwx` 中任何一个表示可读、可写、可执行,如果为 `-` 表示没有这个权限。对应地,可以用八进制来表示它,比如 `rwxr-xr-x` 就可表示成二进制 111101101,对应的八进制则为 755 。正因为如此,要修改文件的操作权限,也可以有多种方式来实现,它们都可通过 `chmod` 命令来修改。 235 | 236 | 237 | #### 范例:给文件添加读、写、可执行权限 238 | 239 | 比如,把 `regular_file` 的文件权限修改为所有用户都可读、可写、可执行,即 `rwxrwxrwx`,也可表示为 111111111,翻译成八进制,则为 777 。这样就可以通过两种方式修改这个权限。 240 | 241 | $ chmod a+rwx regular_file 242 | 243 | 或 244 | 245 | $ chmod 777 regular_file 246 | 247 | 说明: `a` 指所有用户,如果只想给用户本身可读可写可执行权限,那么可以把 `a` 换成 `u` ;而 `+` 就是添加权限,相反的,如果想去掉某个权限,用 `-`,而 `rwx` 则对应可读、可写、可执行。更多用法见 `chmod` 命令的帮助。 248 | 249 | 实际上除了这些权限外,还有两个涉及到安全方面的权限,即 `setuid/setgid` 和只读控制等。 250 | 251 | 如果设置了文件(程序或者命令)的 `setuid/setgid` 权限,那么用户将可用 `root` 身份去执行该文件,因此,这将可能带来安全隐患;如果设置了文件的只读权限,那么用户将仅仅对该文件将有可读权限,这为避免诸如 `rm -rf` 的“可恶”操作带来一定的庇佑。 252 | 253 | 254 | #### 范例:授权普通用户执行root所属命令 255 | 256 | 默认情况下,系统是不允许普通用户执行 `passwd` 命令的,通过 `setuid/setgid`,可以授权普通用户执行它。 257 | 258 | $ ls -l /usr/bin/passwd 259 | -rwx--x--x 1 root root 36092 2007-06-19 14:59 /usr/bin/passwd 260 | $ su #切换到root用户,给程序或者命令添加“粘着位” 261 | $ chmod +s /usr/bin/passwd 262 | $ ls -l /usr/bin/passwd 263 | -rws--s--x 1 root root 36092 2007-06-19 14:59 /usr/bin/passwd 264 | $ exit 265 | $ passwd #普通用户通过执行该命令,修改自己的密码 266 | 267 | 说明: 268 | 269 | > `setuid` 和 `setgid` 位是让普通用户可以以 `root` 用户的角色运行只有 `root` 帐号才能运行的程序或命令。 270 | 271 | 虽然这在一定程度上为管理提供了方便,比如上面的操作让普通用户可以修改自己的帐号,而不是要 `root` 帐号去为每个用户做这些工作。关于 `setuid/setgid` 的更多详细解释,请参考最后推荐的资料。 272 | 273 | 274 | #### 范例:给重要文件加锁 275 | 276 | 只读权限示例:给重要文件加锁(添加不可修改位 [immutable])),以避免各种误操作带来的灾难性后果(例如 `:` 277 | `rm -rf`) 278 | 279 | $ chattr +i regular_file 280 | $ lsattr regular_file 281 | ----i-------- regular_file 282 | $ rm regular_file #加immutable位后就无法对文件进行任何“破坏性”的活动啦 283 | rm: remove write-protected regular file `regular_file'? y 284 | rm: cannot remove `regular_file': Operation not permitted 285 | $ chattr -i regular_file #如果想对它进行常规操作,那么可以把这个位去掉 286 | $ rm regular_file 287 | 288 | 说明: `chattr` 可以用于设置文件的特殊权限,更多用法请参考 `chattr` 的帮助。 289 | 290 | 291 | ### 文件大小 292 | 293 | 文件大小对于普通文件而言就是文件内容的大小,而目录作为一个特殊的文件,它存放的内容是以目录结构体组织的各类文件信息,所以目录的大小一般都是固定的,它存放的文件个数自然也就有上限,即它的大小除以文件名的长度。设备文件的“文件大小”则对应设备的主、次设备号,而有名管道文件因为特殊的读写性质,所以大小常是 0 。硬链接(目录文件不能创建硬链接)实质上是原文件的一个完整的拷贝,因此,它的大小就是原文件的大小。而软链接只是一个 `inode`,存放了一个指向原文件的指针,因此它的大小仅仅是原文件名的字节数。下面我们通过演示增加记忆。 294 | 295 | 296 | #### 范例:查看普通文件和链接文件 297 | 298 | 原文件,链接文件文件大小的示例: 299 | 300 | $ echo -n "abcde" > regular_file #往regular_file写入5字节 301 | $ ls -l regular_file* 302 | -rw-r--r-- 2 root root 5 2007-12-08 15:28 regular_file 303 | -rw-r--r-- 2 root root 5 2007-12-08 15:28 regular_file_hard_file 304 | lrwxrwxrwx 1 root root 12 2007-12-07 20:15 regular_file_soft_link -> regular_file 305 | lrwxrwxrwx 1 root root 22 2007-12-08 15:21 regular_file_soft_link_link -> regular_file_soft_link 306 | $ i="regular_file" 307 | $ j="regular_file_soft_link" 308 | $ echo ${#i} ${#j} #软链接存放的刚好是它们指向的原文件的文件名的字节数 309 | 12 22 310 | 311 | 312 | #### 范例:查看设备文件 313 | 314 | 设备号对应的文件大小:主、次设备号 315 | 316 | $ ls -l hda1_block_dev_file 317 | brw-r--r-- 1 root root 3, 1 2007-12-07 21:44 hda1_block_dev_file 318 | $ ls -l null_char_dev_file 319 | crw-r--r-- 1 root root 1, 3 2007-12-07 21:43 null_char_dev_file 320 | 321 | 补充:主 `(major)、次 `(minor)设备号的作用有不同。当一个设备文件被打开时,内核会根据主设备号(`major number`)去查找在内核中已经以主设备号注册的驱动(可以 `cat /proc/devices` 查看已经注册的驱动号和主设备号的对应情况),而次设备号(`minor number`)则是通过内核传递给了驱动本身(参考《The Linux Primer》第十章)。因此,对于内核而言,通过主设备号就可以找到对应的驱动去识别某个设备,而对于驱动而言,为了能够更复杂地访问设备,比如访问设备的不同部分(如硬件通过分区分成不同部分,而出现 `hda1`,`hda2`,`hda3` 等),比如产生不同要求的随机数(如 `/dev/random` 和 `/dev/urandom` 等)。 322 | 323 | 324 | #### 范例:查看目录 325 | 326 | 目录文件的大小,为什么是这样呢?看看下面的目录结构体的大小,目录文件的 Block 中存放了该目录下所有文件名的入口。 327 | 328 | $ ls -ld directory_file/ 329 | drwxr-xr-x 2 root root 4096 2007-12-07 23:14 directory_file/ 330 | 331 | 目录的结构体如下: 332 | 333 | struct dirent { 334 | long d_ino; 335 | off_t d_off; 336 | unsigned short d_reclen; 337 | char d_name[NAME_MAX+1]; /* 文件名称 */ 338 | } 339 | 340 | 341 | ### 文件访问、更新、修改时间 342 | 343 | 文件的时间属性可以记录用户对文件的操作信息,在系统管理、判断文件版本信息等情况下将为管理员提供参考。因此,在阅读文件时,建议用 `cat` 等阅读工具,不要用编辑工具 `vim` 去阅读,因为即使没有做任何修改操作,一旦执行了保存命令,将修改文件的时间戳信息。 344 | 345 | 346 | ### 文件名 347 | 348 | 文件名并没有存放在文件结构体内,而是存放在它所在的目录结构体中。所以,在目录的同一级别中,文件名必须是唯一的。 349 | 350 | 351 | ## 文件的基本操作 352 | 353 | 对于文件,常见的操作包括创建、删除、修改、读、写等。关于各种操作对应的“背后动作”将在下一章[《Shell编程范例之文件系统操作》][101]详细分析。 354 | 355 | 356 | ### 范例:创建文件 357 | 358 | `socket` 文件是一类特殊的文件,可以通过 C 语言创建,这里不做介绍(暂时不知道是否可以用命令直接创建),其他文件将通过命令创建。 359 | 360 | $ touch regular_file #创建普通文件 361 | $ mkdir directory_file #创建目录文件,目录文件里头可以包含更多文件 362 | $ ln regular_file regular_file_hard_link #硬链接,是原文件的一个完整拷比 363 | $ ln -s regular_file regular_file_soft_link #类似一个文件指针,指向原文件 364 | $ mkfifo fifo_pipe #或者通过 "mknod fifo_pipe p" 来创建,FIFO满足先进先出的特点 365 | $ mknod hda1_block_dev_file b 3 1 #块设备 366 | $ mknod null_char_dev_file c 1 3 #字符设备 367 | 368 | 创建一个文件实际上是在文件系统中添加了一个节点(`inode),该节点信息将保存到文件系统的节点表中。更形象地说,就是在一颗树上长了一颗新的叶子(文件)或者枝条(目录文件,上面还可以长叶子的那种),这些可以通过 `tree` 命令或者 `ls` 命令形象地呈现出来。文件系统从日常使用的角度,完全可以当成一颗倒立的树来看,因为它们太像了,太容易记忆啦。 369 | 370 | $ tree 当前目录 371 | 372 | 或者 373 | 374 | $ ls 当前目录 375 | 376 | 377 | ### 范例:删除文件 378 | 379 | 删除文件最直接的印象是这个文件再也不存在了,这同样可以通过 `ls` 或者 `tree` 命令呈现出来,就像树木被砍掉一个分支或者摘掉一片叶子一样。实际上,这些文件删除之后,并不是立即消失了,而是仅仅做了删除标记,因此,如果删除之后,没有相关的磁盘写操作把相应的磁盘空间“覆盖”,那么原理上是可以恢复的(虽然如此,但是这样的工作往往很麻烦,所以在删除一些重要数据时,请务必三思而后行,比如做好备份工作),相应的做法可以参考后续资料。 380 | 381 | 具体删除文件的命令有 `rm`,如果要删除空目录,可以用 `rmdir` 命令。例如: 382 | 383 | $ rm regular_file 384 | $ rmdir directory_file 385 | $ rm -r directory_file_not_empty 386 | 387 | `rm` 有两个非常重要的参数,一个是 `-f`,这个命令是非常“野蛮的”,它估计给很多 Linux user 带来了痛苦,另外一个是 `-i`,这个命令是非常“温柔的”,它估计让很多用户感觉烦躁不已。用哪个还是根据您的“心情”吧,如果做好了充分的备份工作,或者采取了一些有效避免灾难性后果的动作的话,您在做这些工作的时候就可以放心一些啦。 388 | 389 | 390 | ### 范例:复制文件 391 | 392 | 文件的复制通常是指文件内容的“临时”复制。通过这一节开头的介绍,我们应该了解到,文件的硬链接和软链接在某种意义上说也是“文件的复制”,前者同步复制文件内容,后者在读写的情况下同步“复制”文件内容。例如: 393 | 394 | 用 `cp` 命令常规地复制文件(复制目录需要 `-r` 选项) 395 | 396 | $ cp regular_file regular_file_copy 397 | $ cp -r diretory_file directory_file_copy 398 | 399 | 创建硬链接(`link` 和 `copy` 不同之处是:`link` 为同步更新,`copy` 则不然,复制之后两者不再相关) 400 | 401 | $ ln regular_file regular_file_hard_link 402 | 403 | 创建软链接 404 | 405 | $ ln -s regular_file regluar_file_soft_link 406 | 407 | 408 | ### 范例:修改文件名 409 | 410 | 修改文件名实际上仅仅修改了文件名标识符。可以通过 `mv` 命令来实现修改文件名操作(即重命名)。 411 | 412 | $ mv regular_file regular_file_new_name 413 | 414 | 415 | ### 范例:编辑文件 416 | 417 | 编辑文件实际上是操作文件的内容,对应普通文本文件的编辑,这里主要涉及到文件内容的读、写、追加、删除等。这些工作通常会通过专门的编辑器来做,这类编辑器有命令行下的 `vim` 、 `emacs` 和图形界面下的 `gedit,kedit` 等。如果是一些特定的文件,会有专门的编辑和处理工具,比如图像处理软件 `gimp`,文档编辑软件 `OpenOffice` 等。这些工具一般都会有专门的教程。 418 | 419 | 下面主要简单介绍 Linux 下通过重定向来实现文件的这些常规的编辑操作。 420 | 421 | 创建一个文件并写入 `abcde` 422 | 423 | $ echo "abcde" > new_regular_file 424 | 425 | 再往上面的文件中追加一行 `abcde` 426 | 427 | $ echo "abcde" >> new_regular_file 428 | 429 | 按行读一个文件 430 | 431 | $ while read LINE; do echo $LINE; done < test.sh 432 | 433 | 提示:如果要把包含重定向的字符串变量当作命令来执行,请使用 `eval` 命令,否则无法解释重定向。例如, 434 | 435 | $ redirect="echo \"abcde\" >test_redirect_file" 436 | $ $redirect #这里会把>当作字符 > 打印出来,而不会当作 重定向 解释 437 | "abcde" >test_redirect_file 438 | $ eval $redirect #这样才会把 > 解释成 重定向 439 | $ cat test_redirect_file 440 | abcde 441 | 442 | 443 | ### 范例:压缩/解压缩文件 444 | 445 | 压缩和解压缩文件在一定意义上来说是为了方便文件内容的传输,不过也可能有一些特定的用途,比如内核和文件系统的映像文件等(更多相关的知识请参考后续资料)。 446 | 447 | 这里仅介绍几种常见的压缩和解压缩方法: 448 | 449 | tar 450 | 451 | $ tar -cf file.tar file #压缩 452 | $ tar -xf file.tar #解压 453 | 454 | gz 455 | 456 | $ gzip -9 file 457 | $ gunzip file 458 | 459 | tar.gz 460 | 461 | $ tar -zcf file.tar.gz file 462 | $ tar -zxf file.tar.gz 463 | 464 | bz2 465 | 466 | $ bzip2 file 467 | $ bunzip2 file 468 | 469 | tar.bz2 470 | 471 | $ tar -jcf file.tar.bz2 file 472 | $ tar -jxf file.tar.bz2 473 | 474 | 通过上面的演示,应该已经非常清楚 `tar`,`bzip2,`bunzip2,`gzip,`gunzip` 命令的角色了吧?如果还不清楚,多操作和比较一些上面的命令,并查看它们的手册: `man tar`... 475 | 476 | 477 | ### 范例:文件搜索(文件定位) 478 | 479 | 文件搜索是指在某个目录层次中找出具有某些属性的文件在文件系统中的位置,这个位置如果扩展到整个网络,那么可以表示为一个 `URL` 地址,对于本地的地址,可以表示为 `file://+` 本地路径。本地路径在 Linux 系统下是以 `/` 开头,例如,每个用户的家目录可以表示为: `file:///home/` 。下面仅仅介绍本地文件搜索的一些办法。 480 | 481 | `find` 命令提供了一种“及时的”搜索办法,它根据用户的请求,在指定的目录层次中遍历所有文件直到找到需要的文件为止。而 `updatedb+locate` 提供了一种“快速的”的搜索策略,`updatedb` 更新并产生一个本地文件数据库,而 `locate` 通过文件名检索这个数据库以便快速找到相应的文件。前者支持通过各种文件属性进行搜索,并且提供了一个接口(`-exec` 选项)用于处理搜索后的文件。因此为“单条命令”脚本的爱好者提供了极大的方便,不过对于根据文件名的搜索而言,`updatedb+locate` 的方式在搜索效率上会有明显提高。下面简单介绍这两种方法: 482 | 483 | `find` 命令基本使用演示 484 | 485 | $ find ./ -name "*.c" -o -name "*.h" #找出所有的C语言文件,-o是或者 486 | $ find ./ \( -name "*.c" -o -name "*.h" \) -exec mv '{}' ./c_files/ \; 487 | # 把找到的文件移到c_files下,这种用法非常有趣 488 | 489 | 上面的用法可以用 `xargs` 命令替代 490 | 491 | $ find ./ -name "*.c" -o -name "*.h" | xargs -i mv '{}' ./c_files/ 492 | # 如果要对文件做更复杂的操作,可以考虑把mv改写为你自己的处理命令,例如,我需要修 493 | 494 | 改所有的文件名后缀为大写。 495 | 496 | $ find ./ -name "*.c" -o -name "*.h" | xargs -i ./toupper.sh '{}' ./c_files/ 497 | 498 | `toupper.sh` 就是我们需要实现的转换小写为大写的一个处理文件,具体实现如下: 499 | 500 | $ cat toupper.sh 501 | #!/bin/bash 502 | 503 | # the {} will be expended to the current line and becomen the first argument of this script 504 | FROM=$1 505 | BASENAME=${FROM##*/} 506 | 507 | BASE=${BASENAME%.*} 508 | SUFFIX=${BASENAME##*.} 509 | 510 | TOSUFFIX="$(echo $SUFFIX | tr '[a-z]' '[A-Z]')" 511 | TO=$2/$BASE.$TOSUFFIX 512 | COM="mv $FROM $TO" 513 | echo $COM 514 | eval $COM 515 | 516 | `updatedb+locate` 基本使用演示 517 | 518 | $ updatedb #更新库 519 | $ locate find*.gz #查找包含find字符串的所有gz压缩包 520 | 521 | 实际上,除了上面两种命令外,Linux 下还有命令查找工具:`which` 和 `whereis`,前者用于返回某个命令的全路径,而后者用于返回某个命令、源文件、`man 文件的路径。例如,查找 `find` 命令的绝对路径: 522 | 523 | $ which find 524 | /usr/bin/find 525 | $ whereis find 526 | find: /usr/bin/find /usr/X11R6/bin/find /usr/bin/X11/find /usr/X11/bin/find /usr/man/man1/find.1.gz /usr/share/man/man1/find.1.gz /usr/X11/man/man1/find.1.gz 527 | 528 | 需要提到的是,如果想根据文件的内容搜索文件,那么 `find` 和 `updatedb+locate` 以及 `which`,`whereis` 都无能为力啦,可选的方法是 `grep`,`sed` 等命令,前者在加上 `-r` 参数以后可以在指定目录下文件中搜索指定的文件内容,后者再使用 `-i` 参数后,可以对文件内容进行替换。它们的基本用法在前面的章节中已经详细介绍了,这里就不再赘述。 529 | 530 | 值得强调的是,这些命令对文件的操作非常有意义。它们在某个程度上把文件系统结构给抽象了,使得对整个文件系统的操作简化为对单个文件的操作,而单个文件如果仅仅考虑文本部分,那么最终却转化成了之前的字符串操作,即上一节讨论过的内容。为了更清楚地了解文件的组织结构,文件之间的关系,在下一节将深入探讨文件系统。 531 | 532 | 533 | ## 参考资料 534 | 535 | - [从文件 I/O 看 Linux 的虚拟文件系统](http://www.ibm.com/developerworks/cn/linux/l-cn-vfs/) 536 | - [Linux 文件系统剖析](http://www.ibm.com/developerworks/cn/linux/l-linux-filesystem/index.html?ca=drs-cn) 537 | - [《Linux 核心》第九章 文件系统](http://man.chinaunix.net/tech/lyceum/linuxK/fs/filesystem.html) 538 | - [Linux Device Drivers, 3rd Edition](http://lwn.net/Kernel/LDD3/) 539 | - [技巧:Linux I/O 重定向的一些小技巧](http://www.ibm.com/developerworks/cn/linux/l-iotips/index.html) 540 | - Intel 平台下 Linux 中 ELF 文件动态链接的加载、解析及实例分析: 541 | - [part1](http://www.ibm.com/developerworks/cn/linux/l-elf/part1/index.html), 542 | - [part2](http://www.ibm.com/developerworks/cn/linux/l-elf/part2/index.html) 543 | - [Shell 脚本调试技术](http://www.ibm.com/developerworks/cn/linux/l-cn-shell-debug/index.html) 544 | - [ELF 文件格式及程序加载执行过程总汇](http://www.linuxsir.org/bbs/thread206356.html) 545 | - [Linux下 C 语言编程——文件的操作](http://fanqiang.chinaunix.net/a4/b2/20010508/113315.html) 546 | - ["Linux下 C 语言编程" 的 文件操作部分](http://www.mwjx.com/aboutfish/private/book/linux_c.txt) 547 | - [Filesystem Hierarchy Standard](http://www.pathname.com/fhs/pub/fhs-2.3.html#INTRODUCTION) 548 | - [学会恢复 Linux系统里被删除的 Ext3 文件](http://tech.ccidnet.com/art/237/20070720/1150559_1.html) 549 | - [使用mc恢复被删除文件](http://bbs.tech.ccidnet.com/read.php?tid=48372) 550 | - [linux ext3 误删除及恢复原理](http://www.linuxdiyf.com/viewarticle.php?id=30866) 551 | - [Linux压缩/解压缩方式大全](http://www.cnblogs.com/eoiioe/archive/2008/09/20/1294681.html) 552 | - [Everything is a byte](http://www.reteam.org/papers/e56.pdf) 553 | 554 | 555 | ## 后记 556 | 557 | - 考虑到文件和文件系统的重要性,将把它分成三个小节来介绍:文件、文件系统、程序与进程。在“文件”这一部分,主要介绍文件的基本属性和常规操作,在“文件系统”那部分,将深入探讨 Linux 文件系统的各个部分(包括 Linux 文件系统的结构、具体某个文件系统的大体结构分析、底层驱动的工作原理),在“程序与进程”一节将专门讨论可执行文件的相关内容(包括不同的程序类型、加载执行过程、不同进程之间的交互[命令管道和无名管道、信号通信]、对进程的控制等) 558 | - 有必要讨论清楚 目录大小 的含义,另外,最好把一些常规的文件操作全部考虑到,包括文件的读、写、执行、删除、修改、复制、压缩/解压缩等 559 | - 下午刚从上海回来,比赛结果很“糟糕”,不过到现在已经不重要了,关键是通过决赛发现了很多不足,发现了设计在系统开发中的关键角色,并且发现了上海是个美丽的城市,上交也是个美丽的大学。回来就开始整理这个因为比赛落下了两周的 Blog 560 | - 12月15日,添加文件搜索部分内容 561 | -------------------------------------------------------------------------------- /zh/chapters/01-chapter6.markdown: -------------------------------------------------------------------------------- 1 | **关注作者公众号**: 2 |
3 | 4 |
5 | 6 | # 文件系统操作 7 | 8 | - [前言](#toc_23937_24032_1) 9 | - [文件系统在 Linux 操作系统中的位置](#toc_23937_24032_2) 10 | - [硬件管理和设备驱动](#toc_23937_24032_3) 11 | - [范例:查找设备所需的驱动文件](#toc_23937_24032_4) 12 | - [范例:查看已经加载的设备驱动](#toc_23937_24032_5) 13 | - [范例:卸载设备驱动](#toc_23937_24032_6) 14 | - [范例:挂载设备驱动](#toc_23937_24032_7) 15 | - [范例:查看设备驱动对应的设备文件](#toc_23937_24032_8) 16 | - [范例:访问设备文件](#toc_23937_24032_9) 17 | - [理解、查看磁盘分区](#toc_23937_24032_10) 18 | - [磁盘分区基本原理](#toc_23937_24032_11) 19 | - [通过分析 MBR 来理解分区原理](#toc_23937_24032_12) 20 | - [分区和文件系统的关系](#toc_23937_24032_13) 21 | - [常见分区类型](#toc_23937_24032_14) 22 | - [范例:格式化文件系统](#toc_23937_24032_15) 23 | - [分区、逻辑卷和文件系统的关系](#toc_23937_24032_16) 24 | - [文件系统的可视化结构](#toc_23937_24032_17) 25 | - [范例:挂载文件系统](#toc_23937_24032_18) 26 | - [范例:卸载某个分区](#toc_23937_24032_19) 27 | - [如何制作一个文件系统](#toc_23937_24032_20) 28 | - [范例:用 dd 创建一个固定大小的文件](#toc_23937_24032_21) 29 | - [范例:用 mkfs 格式化文件](#toc_23937_24032_22) 30 | - [范例:挂载刚创建的文件系统](#toc_23937_24032_23) 31 | - [范例:对文件系统进行读、写、删除等操作](#toc_23937_24032_24) 32 | - [如何开发自己的文件系统](#toc_23937_24032_25) 33 | - [后记](#toc_23937_24032_26) 34 | 35 | 36 | 37 | ## 前言 38 | 39 | 准备了很久,找了好多天资料,还不知道应该如何动笔写:因为担心拿捏不住,所以一方面继续查找资料,一方面思考如何来写。作为《Shell编程范例》的一部分,希望它能够很好地帮助 Shell 程序员理解如何用 Shell 命令来完成和 Linux 系统关系非常大的文件系统的各种操作,希望让 Shell 程序员中对文件系统"混沌"的状态从此消失,希望文件系统以一种更为清晰的样子呈现在眼前。 40 | 41 | 42 | ## 文件系统在 Linux 操作系统中的位置 43 | 44 | 如何来认识文件系统呢?从 Shell 程序员的角度来看,文件系统就是一个用来组织各种文件的方法。但是文件系统无法独立于硬件存储设备和操作系统而存在,因此还是有必要来弄清楚硬件存储设备、分区、操作系统、逻辑卷、文件系统等各种概念之间的联系,以便理解文件系统常规操作的一些“细节”。这个联系或许(也许会有一些问题)可以通过这样一种方式来呈现: 45 | 46 | ![Linux FileSystem Architecture](pic/Linux_FileSystem_Architecture.jpg) 47 | 48 | 从图中可以清晰地看到各个“概念”之间的关系,它们以不同层次分布,覆盖硬件设备、系统内核空间、系统用户空间。在用户空间,用户可以不管内核如何操作具体硬件设备,仅仅使用程序员设计的各种界面就可以,而普通程序员也仅仅需要利用内核提供的各种接口(System Call)或者一些C库来和内核进行交互,而无须关心具体的实现细节。不过对于操作系统开发人员,他们需要在内核空间设计特定的数据结构来管理和组织底层的硬件设备。 49 | 50 | 下面从下到上的方式(即从底层硬件开始),用工具来分析和理解图中几个重要概念。(如果有兴趣,可以先看看下面的几则资料) 51 | 52 | 参考资料: 53 | 54 | - [从文件 I/O 看 Linux 的虚拟文件系统](http://www.ibm.com/developerworks/cn/linux/l-cn-vfs/) 55 | - [Linux 文件系统剖析](http://www.ibm.com/developerworks/cn/linux/l-linux-filesystem/index.html?ca=drs-cn) 56 | - [第九章 文件系统](http://man.chinaunix.net/tech/lyceum/linuxK/fs/filesystem.html) 57 | - [Linux 逻辑盘卷管理 LVM 详解](http://unix-cd.com/vc/www/28/2007-06/1178.html) 58 | 59 | 60 | ## 硬件管理和设备驱动 61 | 62 | Linux 系统通过设备驱动管理硬件设备。如果添加了新的硬件设备,那么需要编写相应的硬件驱动来管理它。对于一些常见的硬件设备,系统已经自带了相应的驱动,编译内核时,选中它们,然后编译成内核的一部分或者以模块的方式编译。如果以模块的方式编译,那么可以在系统的 `/lib/modules/$(uname -r)`目录下找到对应的模块文件。 63 | 64 | 65 | ### 范例:查找设备所需的驱动文件 66 | 67 | 比如,可以这样找到相应的 scsi 驱动和 usb 驱动对应的模块文件: 68 | 69 | 更新系统中文件索引数据库(有点慢) 70 | 71 | $ updatedb 72 | 73 | 查找 scsi 相关的驱动 74 | 75 | $ locate scsi*.ko 76 | 77 | 查找 usb 相关的驱动 78 | 79 | $ locate usb*.ko 80 | 81 | 这些驱动以 `.ko` 为后缀,在安装系统时默认编译为了模块。实际上可以把它们编译为内核的一部分,仅仅需要在编译内核时选择为`[*]`即可。但是,很多情况下会以模块的方式编译它们,这样可以减少内核的大小,并根据需要灵活地加载和卸载它们。下面简单地演示如何卸载模块、加载模块以及查看已加载模块的状态。 82 | 83 | 可通过 `/proc` 文件系统的 `modules` 文件检查内核中已加载的各个模块的状态,也可以通过 `lsmod` 命令直接查看它们。 84 | 85 | $ cat /proc/modules 86 | 87 | 或者 88 | 89 | $ lsmod 90 | 91 | 92 | ### 范例:查看已经加载的设备驱动 93 | 94 | 查看 scsi 和 usb 相关驱动,结果各列为模块名、模块大小、被其他模块的引用情况(引用次数、引用它们的模块) 95 | 96 | $ lsmod | egrep "scsi|usb" 97 | usbhid 29536 0 98 | hid 28928 1 usbhid 99 | usbcore 138632 4 usbhid,ehci_hcd,ohci_hcd 100 | scsi_mod 147084 4 sg,sr_mod,sd_mod,libata 101 | 102 | 103 | ### 范例:卸载设备驱动 104 | 105 | 下面卸载 `usbhid` 模块看看(不要卸载scsi的驱动!因为你的系统可能就跑在上面,如果确实想玩玩,卸载前记得保存数据),通过 `rmmod` 命令就可以实现,先切换到 Root 用户: 106 | 107 | $ sudo -s 108 | # rmmod usbhid 109 | 110 | 再查看该模块的信息,已经看不到了吧 111 | 112 | $ lsmod | grep ^usbhid 113 | 114 | 115 | ### 范例:挂载设备驱动 116 | 117 | 如果有个 usb 鼠标,那么移动一下,是不是发现动不了啦?因为设备驱动都没有了,设备自然就没法用罗。不过不要紧张,既然知道原因,那么重新加载驱动就可以,下面用 `insmod` 把 `usbhid` 模块重新加载上。 118 | 119 | $ sudo -s 120 | # insmod `locate usbhid.ko` 121 | 122 | `locate usbhid.ko` 是为了找出 `usbhid.ko` 模块的路径,如果之前没有 `updatedb`,估计用它是找不到了,不过也可以直接到 `/lib/modules` 目录下用 `find` 把 `usbhid.ko` 文件找到。 123 | 124 | # insmod $(find /lib/modules -name "*usbhid.ko*" | grep `uname -r`) 125 | 126 | 现在鼠标又可以用啦,不信再动一下鼠标 :-) 127 | 128 | 到这里,硬件设备和设备驱动之间关系应该是比较清楚了。如果没有,那么继续下面的内容。 129 | 130 | 131 | ### 范例:查看设备驱动对应的设备文件 132 | 133 | Linux 设备驱动关联着相应的设备文件,而设备文件则和硬件设备一一对应。这些设备文件都统一存放在系统的 `/dev/` 目录下。 134 | 135 | 例如,scsi 设备对应`/dev/sda`,`/dev/sda1`,`/dev/sda2`... 下面查看这些设备信息。 136 | 137 | $ ls -l /dev/sda* 138 | brw-rw---- 1 root disk 8, 0 2007-12-28 22:49 /dev/sda 139 | brw-rw---- 1 root disk 8, 1 2007-12-28 22:50 /dev/sda1 140 | brw-rw---- 1 root disk 8, 3 2007-12-28 22:49 /dev/sda3 141 | brw-rw---- 1 root disk 8, 4 2007-12-28 22:49 /dev/sda4 142 | brw-rw---- 1 root disk 8, 5 2007-12-28 22:50 /dev/sda5 143 | brw-rw---- 1 root disk 8, 6 2007-12-28 22:50 /dev/sda6 144 | brw-rw---- 1 root disk 8, 7 2007-12-28 22:50 /dev/sda7 145 | brw-rw---- 1 root disk 8, 8 2007-12-28 22:50 /dev/sda8 146 | 147 | 可以看到第一列第一个字符都是 `b`,第五列都是数字 8 。 `b` 表示该文件是一个块设备文件,对应地,如果是 `c` 则表示字符设备(例如 `/dev/ttyS0),关于块设备和字符设备的区别,可以看这里: 148 | 149 | > - 字符设备:字符设备就是能够像字节流一样访问的设备,字符终端和串口就属于字符设备。 150 | 151 | 152 | > - 块设备:块设备上可以容纳文件系统。与字符设备不同,在读写时,块设备每次只能传输一个或多个完整的块。在 Linux 操作系统中,应用程序可以像访问字符设备一样读写块设备(一次读取或写入任意的字节数据)。因此,块设备和字符设备的区别仅仅是在内核中对于数据的管理不同。 153 | 154 | 数字 8 则是该硬件设备在内核中对应的设备编号,可以在内核的 `Documentation/devices.txt` 和 `/proc/devices` 文件中找到设备号分配情况。但是为什么同一个设备会对应不同的设备文件(`/dev/sda` 后面为什么还有不同的数字,而且 `ls` 结果中的第 6 列和它们对应起来)。这实际上是为了区分不同设备的不同部分。对于硬盘,这样可以处理硬盘内部的不同分区。就内核而言,它仅仅需要通过第 5 列的设备号就可以找到对应的硬件设备,但是对于驱动模块来说,它还需要知道如何处理不同的分区,于是就多了一个辅设备号,即第 6 列对应的内容。这样一个设备就有了主设备号(第 5 列)和辅设备号(第 6 列),从而方便地实现对各种硬件设备的管理。 155 | 156 | 因为设备文件和硬件是对应的,这样可以直接从 `/dev/sda` (如果是 `IDE` 的硬盘,那么对应的设备就是 `/dev/hda` 啦)设备中读出硬盘的信息,例如: 157 | 158 | 159 | ### 范例:访问设备文件 160 | 161 | 用 `dd` 命令复制出硬盘的前 512 个字节,要 Root 用户 162 | 163 | $ sudo dd if=/dev/sda of=mbr.bin bs=512 count=1 164 | 165 | 用 `file` 命令查看相应的信息 166 | 167 | $ file mbr.bin 168 | mbr.bin: x86 boot sector, LInux i386 boot LOader; partition 3: ID=0x82, starthead 254, startsector 19535040, 1959930 sectors; partition 4: ID=0x5, starthead 254, startsector 21494970, 56661255 sectors, code offset 0x48 169 | 170 | 也可以用 `od` 命令以 16 进制的形式读取并进行分析 171 | 172 | $ od -x mbr.bin 173 | 174 | `bs` 是块的大小(以字节 `bytes` 为单位),`count` 是块数 175 | 176 | 因为这些信息并不直观(而且下面会进一步深入分析),那么先来看看另外一个设备文件,将可以非常直观地演示设备文件和硬件的对应关系。还是以鼠标为例吧,下面来读取鼠标对应的设备文件的信息。 177 | 178 | $ sudo -s 179 | # cat /dev/input/mouse1 | od -x 180 | 181 | 你的鼠标驱动可能不太一样,所以设备文件可能是其他的,但是都会在 `/dev/input` 下。 182 | 183 | 移动鼠标看看,是不是发现有不同信息输出。基于这一原理,我们经常通过在一端读取设备文件 `/dev/ttyS0` 中的内容,而在另一端往设备文件 `/dev/ttyS0` 中写入内容来检查串口线是否被损坏。 184 | 185 | 到这里,对设备驱动、设备文件和硬件设备之间的关联应该是印象更深刻了。如果想深入了解设备驱动的工作原理和设备驱动的编写,那么看看下面列出的相关资料,开始设备驱动的编写历程吧。 186 | 187 | 参考资料: 188 | 189 | - [Compile linux kernel 2.6](http://www.cyberciti.biz/tips/compiling-linux-kernel-26.html) 190 | - [Linux 系统的硬件驱动程序编写原理](http://www.blue1000.com/bkhtml/2001-02/2409.htm) 191 | - [Linux 下 USB设备的原理、配置、常见问题](http://soft.zdnet.com.cn/software_zone/2007/1108/617545.shtml) 192 | - [The Linux Kernel Module Programming Guide](http://www.tldp.org/LDP/lkmpg/2.6/html/lkmpg.html) 193 | - [Linux 设备驱动开发](http://lwn.net/Kernel/LDD3/) 194 | 195 | 196 | ## 理解、查看磁盘分区 197 | 198 | 实际上内存、u 盘等都可以作为文件系统底层的“存储”设备,但是这里仅用硬盘作为实例来介绍磁盘和分区的关系。 199 | 200 | 目前 Linux 的分区依然采用第一台PC硬盘所使用的分区原理,下面逐步分析和演示这一分区原理。 201 | 202 | 203 | ### 磁盘分区基本原理 204 | 205 | 先来看看几个概念: 206 | 207 | - 设备管理和分区 208 | 209 | Linux 下,每一个存储设备对应一个系统的设备文件,对于硬盘等 `IDE` 和 `SCSI` 设备,在系统的 `/dev` 目录下可以找到对应的包含字符 `hd` 和 `sd` 的设备文件。而根据硬盘连接的主板设备接口和数据线接口的不同,在 `hd` 或者 `sd` 字符后面可以添加一个从 `a` 到 `z` 的字符,例如 `hda`,`hdb`,`hdc` 和 `sda`,`sdb`,`sdc` 等,另外为了区别同一个硬件设备的不同分区,在后面还可以添加了一个数字,例如 `hda1`,`hda2`,`hda3` 和 `sda1`,`sda2`,`sda3`,所以在 `/dev` 目录下,可以看到很多类似的设备文件。 210 | 211 | - 各分区的作用 212 | 213 | 在分区时常遇到主分区和逻辑分区的问题,这实际上是为了方便扩展分区,正如后面的逻辑卷的引入是为了更好地管理多个硬盘一样,引入主分区和逻辑分区可以方便地进行分区的管理。 214 | 215 | Linux 系统中每一个硬盘设备最多由 4 个主分区(包括扩展分区)构成。 216 | 217 | 主分区的作用是计算机用来进行启动操作系统的,因此每一个操作系统的启动程序或者称作是引导程序,都应该存放在主分区上。 Linux 规定主分区(或者扩展分区)占用分区编号中的前 4 个。所以会看到主分区对应的设备文件为 `/dev/hda1-4` 或者 `/dev/sda1-4`,而不会是 `hda5` 或者 `sda5` 。 218 | 219 | 扩展分区则是为了扩展更多的逻辑分区的,在 Linux 下,逻辑分区占用了 `hda5-16` 或者 `sda5-16` 等 12 个编号。 220 | 221 | - 分区类型 222 | 223 | 它规定了这个分区上的文件系统的类型。Linux支持诸如msdoc,vfat,ext2,ext3等诸多的文件系统类型,更多信息在下一小节进行进一步的介绍。 224 | 225 | 226 | ### 通过分析 MBR 来理解分区原理 227 | 228 | 下面通过分析硬盘的前 512 个字节(即 `MBR`)来分析和理解分区。 229 | 230 | 先来看看这张图: 231 | 232 | ![MBR Architecture](pic/MBR_Architecture.jpg) 233 | 234 | 它用来描述 `MBR` 的结构。 `MBR` 包括引导部分、分区表、以及结束标记 `(55AAH),分别占用了 512 字节中 446 字节、 64 字节和 2 字节。这里仅仅关注分区表部分,即中间的 64 字节以及图中左边的部分。 235 | 236 | 由于我用的是 `SCSI` 的硬盘,下面从 `/dev/sda` 设备中把硬盘的前 512 个字节拷贝到文件 `mbr.bin` 中。 237 | 238 | $ sudo -s 239 | # dd if=/dev/sda of=mbr.bin bs=512 count=1 240 | 241 | 下面用 `file`,`od`,`fdisk` 等命令来分析这段 `MBR` 的数据,并对照上图以便加深理解。 242 | 243 | $ file mbr.bin 244 | mbr.bin: x86 boot sector, LInux i386 boot LOader; partition 3: ID=0x82, starthead 254, startsector 19535040, 1959930 sectors; partition 4: ID=0x5, starthead 254, startsector 21494970, 56661255 sectors, code offset 0x48 245 | $ od -x mbr.bin | tail -6 #仅关注中间的64字节,所以截取了结果中后6行 246 | 0000660 0000 0000 0000 0000 a666 a666 0000 0180 247 | 0000700 0001 fe83 ffff 003f 0000 1481 012a 0000 248 | 0000720 0000 0000 0000 0000 0000 0000 0000 fe00 249 | 0000740 ffff fe82 ffff 14c0 012a e7fa 001d fe00 250 | 0000760 ffff fe05 ffff fcba 0147 9507 0360 aa55 251 | $ sudo -s 252 | # fdisk -l | grep ^/ #仅分析MBR相关的部分,不分析逻辑分区部分 253 | /dev/sda1 * 1 1216 9767488+ 83 Linux 254 | /dev/sda3 1217 1338 979965 82 Linux swap / Solaris 255 | /dev/sda4 1339 4865 28330627+ 5 Extended 256 | 257 | `file` 命令的结果显示,刚拷贝的 512 字节是启动扇区,用分号分开的几个部分分别是 `bootloader`,分区 3 和分区 4 。分区 3 的类型是 82,即 `swap` 分区(可以通过 `fdisk` 命令的 `l` 命令列出相关信息),它对应 `fdisk` 的结果中 `/dev/sda3` 所在行的第 5 列,分区 3 的扇区数是 1959930,转换成字节数是 `1959930\*512` (目前,硬盘的默认扇区大小是 512 字节),而 `swap` 分区的默认块大小是 1024 字节,这样块数就是 `:` 258 | 259 | $ echo 1959930*512/1024 | bc 260 | 979965 261 | 262 | 正好是 `fdisk` 结果中 `/dev/sda3` 所在行的第四列对应的块数,同样地,可以对照 `fdisk` 和 `file` 的结果分析分区 4 。 263 | 264 | 再来看看 `od` 命令以十六进制显示的结果,同样考虑分区 3,计算一下发现,分区 3 对应的 `od` 命令的结果为: 265 | 266 | fe00 ffff fe82 ffff 14c0 012a e7fa 001d 267 | 268 | 首先是分区标记,`00H`,从上图中,看出它就不是引导分区(`80H` 标记的才是引导分区),而分区类型呢?为 `82H`,和 `file` 显示结果一致,现在再来关注一下分区大小,即 `file` 结果中的扇区数。 269 | 270 | $ echo "ibase=10;obase=16;1959930" | bc 271 | 1DE7FA 272 | 273 | 刚好对应 `e7fa 001d`,同样地考虑引导分区的结果: 274 | 275 | > 0180 0001 fe83 ffff 003f 0000 1481 012a 276 | 277 | 分区标记: `80H`,正好反应了这个分区是引导分区,随后是引导分区所在的磁盘扇区情况,010100,即 1 面 0 道 1 扇区。其他内容可以对照分析。 278 | 279 | 考虑到时间关系,更多细节请参考下面的资料或者查看看系统的相关手册。 280 | 281 | 补充:安装系统时,可以用 `fdisk`,`cfdisk` 等命令进行分区。如果要想从某个分区启动,那么需要打上 `80H` 标记,例如可通过 `cfdisk` 把某个分区设置为 `bootable` 来实现。 282 | 283 | 参考资料: 284 | 285 | - [Inside the linux boot process](http://www-128.ibm.com/developerworks/linux/library/l-linuxboot/) 286 | - [Develop your own OS: booting](http://docs.huihoo.com/gnu_linux/own_os/booting.htm) 287 | - [Redhat9 磁盘分区简介](http://blog.csdn.net/fowse/article/details/7220021) 288 | - [Linux partition HOWTO](http://www.tldp.org/HOWTO/Partition/) 289 | 290 | 291 | ## 分区和文件系统的关系 292 | 293 | 在没有引入逻辑卷之前,分区类型和文件系统类型几乎可以同等对待,设置分区类型的过程就是格式化分区,建立相应的文件系统类型的过程。 294 | 295 | 下面主要介绍如何建立分区和文件系统类型的联系,即如何格式化分区为指定的文件系统类型。 296 | 297 | 298 | ### 常见分区类型 299 | 300 | 先来看看 Linux 下文件系统的常见类型(如果要查看所有 Linux 支持的文件类型,可以用 `fdisk` 命令的 `l` 命令查看,或者通过 `man fs` 查看,也可通过 `/proc/filesystems` 查看到当前内核支持的文件系统类型) 301 | 302 | - `ext2`,`ext3`,`ext4` :这三个是 Linux 根文件系统通常采用的类型 303 | - `swap` :这个是实现 Linux 虚拟内存时采用的一种文件系统,安装时一般需要建立一个专门的分区,并格式化为 `swap` 文件系统(如果想添加更多 `swap` 分区,可以参考本节的[参考资料](http://soft.zdnet.com.cn/software_zone/2007/1010/545261.shtml),熟悉 `dd`,`mkswap`,`swapon`,`swapoff` 等命令的用法) 304 | - `proc` :这是一种比较特别的文件系统,作为内核和用户之间的一个接口存在,建立在内存中(可以通过 `cat` 命令查看 `/proc` 系统下的文件,甚至可以通过修改 `/proc/sys` 下的文件实时调整内核配置,当前前提是需要把 `proc` 文件系统挂载上: `mount -t proc proc /proc` 305 | 306 | 除了上述文件系统类型外,Linux 支持包括 `vfat`,`iso`,`xfs`,`nfs` 在内各种常见的文件系统类型,在 Linux 下,可以自由地查看和操作 Windows 等其他操作系统使用的文件系统。 307 | 308 | 那么如何建立磁盘和这些文件系统类型的关联呢?格式化。 309 | 310 | 格式化的过程实际上就是重新组织分区的过程,可通过 `mkfs` 命令来实现,当然也可以通过 `fdisk` 等命令来实现。这里仅介绍 `mkfs`,`mkfs` 可用来对一个已有的分区进行格式化,不能实现分区操作(如果要对一个磁盘进行分区和格式化,那么可以用 `fdisk`)。格式化后,相应分区上的数据就会通过某种特别的文件系统类型进行组织。 311 | 312 | 313 | ### 范例:格式化文件系统 314 | 315 | 例如:把 `/dev/sda9` 分区格式化为 `ext3` 的文件系统。 316 | 317 | $ sudo -s 318 | # mkfs -t ext3 /dev/sda9 319 | 320 | 如果要列出各个分区的文件系统类型,那么可以用 `fdisk -l` 命令。 321 | 322 | 更多信息请参考下列资料。 323 | 324 | 参考资料: 325 | 326 | - [Linux 下加载 swap 分区的步骤](http://soft.zdnet.com.cn/software_zone/2007/1010/545261.shtml) 327 | - [Linux 下 ISO 镜像文件的制作与刻录](http://www.examda.com/linux/fudao/20071212/113445321.html) 328 | - RAM 磁盘分区解释: 329 | [\[1\]](http://oldlinux.org/oldlinux/viewthread.php?tid=2677), 330 | [\[2\]](http://www.ibm.com/developerworks/cn/linux/l-initrd.html) 331 | - [高级文件系统实现者指南](http://www.ibm.com/Search/?q=%E9%AB%98%E7%BA%A7%E6%96%87%E4%BB%B6%E7%B3%BB%E7%BB%9F%E5%AE%9E%E7%8E%B0%E8%80%85%E6%8C%87%E5%8D%97&v=17&en=utf&lang=en&cc=us) 332 | 333 | 334 | ## 分区、逻辑卷和文件系统的关系 335 | 336 | 上一节直接把分区格式化为某种文件系统类型,但是考虑到扩展新的存储设备的需要,开发人员在文件系统和分区之间引入了逻辑卷。考虑到时间关系,这里不再详述,请参考资料:[Linux 逻辑卷管理详解](http://unix-cd.com/vc/www/28/2007-06/1178.html) 337 | 338 | 339 | ## 文件系统的可视化结构 340 | 341 | 文件系统最终呈现出来的是一种可视化的结构,可用ls,find,tree等命令把它呈现出来。它就像一颗倒挂的“树”,在树的节点上还可以挂载新的“树”。 342 | 343 | 下面简单介绍文件系统的挂载。 344 | 345 | 一个文件系统可以通过一个设备挂载(`mount`)到某个目录下,这个目录被称为挂载点。有趣的是,在 Linux 下,一个目录本身还可以挂载到另外一个目录下,一个格式化了的文件也可以通过一个特殊的设备 `/dev/loop` 进行挂载(如 `iso` 文件)。另外,就文件系统而言,Linux 不仅支持本地文件系统,还支持远程文件系统(如 `nfs`)。 346 | 347 | 348 | ### 范例:挂载文件系统 349 | 350 | 下面简单介绍文件系统挂载的几个实例。 351 | 352 | - 根文件系统的挂载 353 | 354 | 挂载需要 Root 权限,例如,挂载系统根文件系统 `/dev/sda1` 到 `/mnt` 355 | 356 | $ sudo -s 357 | # mount -t ext3 /dev/sda1 /mnt/ 358 | 359 | 查看 `/dev/sda1` 的挂载情况,可以看到,一个设备可以多次挂载 360 | 361 | $ mount | grep sda1 362 | /dev/sda1 on / type ext3 (rw,errors=remount-ro) 363 | /dev/sda1 on /mnt type ext3 (rw) 364 | 365 | 对于一个已经挂载的文件系统,为支持不同属性可以重新挂载 366 | 367 | $ mount -n -o remount, rw / 368 | 369 | - 挂载一个新增设备 370 | 371 | 如果内核已经支持 USB 接口,那么插入 u 盘时,可以通过 `dmesg` 命令查看对应的设备号,并挂载它。 372 | 373 | 查看 `dmesg` 结果中的最后几行内容,找到类似 `/dev/sdN` 的信息,找出 u 盘对应的设备号 374 | 375 | $ dmesg 376 | 377 | 这里假设 u 盘是 `vfat` 格式,以便在一些打印店里的 Windows 上也可使用 378 | 379 | # mount -t vfat /dev/sdN /path/to/mountpoint_directory 380 | 381 | - 挂载一个 iso 文件或者是光盘 382 | 383 | 对于一些iso文件或者是 iso 格式的光盘,同样可以通过 `mount` 命令挂载。 384 | 385 | 对于 iso 文件: 386 | 387 | # mount -t iso9660 /path/to/isofile /path/to/mountpoint_directory 388 | 389 | 对于光盘: 390 | 391 | # mount -t iso9660 /dev/cdrom /path/to/mountpoint_directory 392 | 393 | - 挂载一个远程文件系统 394 | 395 | 396 | 397 | # mount -t nfs remote_ip:/path/to/share_directory /path/to/local_directory 398 | 399 | - 挂载一个 proc 文件系统 400 | 401 | 402 | 403 | # mount -t proc proc /proc 404 | 405 | `proc` 文件系统组织在内存中,但是可以把它挂载到某个目录下。通常把它挂载在 `/proc` 目录下,以便一些系统管理和配置工具使用它。例如 `top` 命令用它分析内存的使用情况(读取 `/proc/meminfo` 和 `/proc/stat` 等文件中的内容); `lsmod` 命令通过它获取内核模块的状态(读取 `/proc/modules`); `netstat` 命令通过它获取网络的状态(读取 `/proc/net/dev` 等文件)。当然,也可以编写相关工具。除此之外,通过调整 `/proc/sys` 目录下的文件,可以动态地调整系统配置,比如往 `/proc/sys/net/ipv4/ip_forward` 文件中写入数字 1 就可以让内核支持数据包转发。(更多信息请参考 `proc` 的帮助,`man` 406 | `proc`) 407 | 408 | - 挂载一个目录 409 | 410 | 411 | 412 | $ mount --bind /path/to/needtomount_directory /path/to/mountpoint_directory 413 | 414 | 这个非常有意思,比如可以把某个目录挂载到 ftp 服务的根目录下,而无须把内容复制过去,就可以把相应目录中的资源提供给别人共享。 415 | 416 | 417 | ### 范例:卸载某个分区 418 | 419 | 以上都只提到了挂载,那怎么卸载呢?用 `umount` 命令跟上挂载的源地址或者挂载点(设备,文件,远程目录等)就可以。例如: 420 | 421 | $ umount /path/to/mountpoint_directory 422 | 423 | 或者 424 | 425 | $ umount /path/to/mount_source 426 | 427 | 如果想管理大量的或者经常性的挂载服务,那么每次手动挂载是很糟糕的事情。这时就可利用 `mount` 的配置文件 `/etc/fstab`,把 `mount` 对应的参数写到 `/etc/fstab` 文件对应的列中即可实现批量挂载( `mount -a` )和卸载( `umount -a` )。 `/etc/fstab` 中各列分别为文件系统、挂载点、类型、相关选项。更多信息可参考 `fstab` 的帮助( `man fstab` )。 428 | 429 | 参考资料: 430 | 431 | - [Linux 硬盘分区以及其挂载原理](http://www.xxlinux.com/linux/article/accidence/technique/20070521/8493.html) 432 | - [从文件 I/O 看 Linux 的虚拟文件系统](http://www.ibm.com/developerworks/cn/linux/l-cn-vfs/) 433 | - [源码分析:静态分析 C 程序函数调用关系图](http://www.tinylab.org/callgraph-draw-the-calltree-of-c-functions/) 434 | 435 | 436 | ## 如何制作一个文件系统 437 | 438 | Linux 文件系统下有一些最基本的目录,不同的目录下存放着不同作用的各类文件。最基本的目录有 `/etc`,`/lib`,`/dev`,`/bin` 等,它们分别存放着系统配置文件,库文件,设备文件和可执行程序。这些目录一般情况下是必须的,在做嵌入式开发时,需要手动或者是用 `busybox` 等工具来创建这样一个基本的文件系统。这里仅制作一个非常简单的文件系统,并对该文件系统进行各种常规操作,以便加深对文件系统的理解。 439 | 440 | 441 | ### 范例:用 dd 创建一个固定大小的文件 442 | 443 | 还记得 `dd` 命令么?就用它来产生一个固定大小的文件,这个为 `1M(1024\*1024 bytes)` 的文件 444 | 445 | $ dd if=/dev/zero of=minifs bs=1024 count=1024 446 | 447 | 查看文件类型,这里的 `minifs` 是一个充满 `\\0` 的文件,没有任何特定的数据结构 448 | 449 | $ file minifs 450 | minifs: data 451 | 452 | 说明: `/dev/zero` 是一个非常特殊的设备,如果读取它,可以获取任意多个 `\\0` 。 453 | 454 | 接着把该文件格式化为某个指定文件类型的文件系统。(是不是觉得不可思议,文件也可以格式化?是的,不光是设备可以,文件也可以以某种文件系统类型进行组织,但是需要注意的是,某些文件系统(如 `ext3`)要求被格式化的目标最少有 `64M` 的空间)。 455 | 456 | 457 | ### 范例:用 mkfs 格式化文件 458 | 459 | $ mkfs.ext2 minifs 460 | 461 | 查看此时的文件类型,这时文件 `minifs` 就以 `ext2` 文件系统的格式组织了 462 | 463 | $ file minifs 464 | minifs: Linux rev 1.0 ext2 filesystem data 465 | 466 | 467 | ### 范例:挂载刚创建的文件系统 468 | 469 | 因为该文件以文件系统的类型组织了,那么可以用 `mount` 命令挂载并使用它。 470 | 471 | 请切换到 `root` 用户挂载它,并通过 `-o loop` 选项把它关联到一个特殊设备 `/dev/loop` 472 | 473 | $ sudo -s 474 | # mount minifs /mnt/ -o loop 475 | 476 | 查看该文件系统信息,仅可以看到一个目录文件 `lost+found` 477 | 478 | $ ls /mnt/ 479 | lost+found 480 | 481 | 482 | ### 范例:对文件系统进行读、写、删除等操作 483 | 484 | 在该文件系统下进行各种常规操作,包括读、写、删除等。(每次操作前先把 `minifs` 文件保存一份,以便比较,结合相关资料就可以深入地分析各种操作对文件系统的改变情况,从而深入理解文件系统作为一种组织数据的方式的实现原理等) 485 | 486 | $ cp minifs minifs.bak 487 | $ cd /mnt 488 | $ touch hello 489 | $ cd - 490 | $ cp minifs minifs-touch.bak 491 | $ od -x minifs.bak > orig.od 492 | $ od -x minifs-touch.bak > touch.od 493 | 494 | 创建一个文件后,比较此时文件系统和之前文件系统的异同 495 | 496 | $ diff orig.od touch.od 497 | diff orig.od touch.od 498 | 61,63c61,64 499 | < 0060020 000c 0202 2e2e 0000 000b 0000 03e8 020a 500 | < 0060040 6f6c 7473 662b 756f 646e 0000 0000 0000 501 | < 0060060 0000 0000 0000 0000 0000 0000 0000 0000 502 | --- 503 | > 0060020 000c 0202 2e2e 0000 000b 0000 0014 020a 504 | > 0060040 6f6c 7473 662b 756f 646e 0000 000c 0000 505 | > 0060060 03d4 0105 6568 6c6c 006f 0000 0000 0000 506 | > 0060100 0000 0000 0000 0000 0000 0000 0000 0000 507 | 508 | 通过比较发现:添加文件,文件系统的相应位置发生了明显的变化 509 | 510 | $ echo "hello, world" > /mnt/hello 511 | 512 | 执行 `sync` 命令,确保缓存中的数据已经写入磁盘(还记得本节图 1 的 `buffer cache` 吧,这里就是把 `cache` 中的数据写到磁盘中) 513 | 514 | $ sync 515 | $ cp minifs minifs-echo.bak 516 | $ od -x minifs-echo.bak > echo.od 517 | 518 | 写入文件内容后,比较文件系统和之前的异同 519 | 520 | $ diff touch.od echo.od 521 | 522 | 查看文件系统中的字符串 523 | 524 | $ strings minifs 525 | lost+found 526 | hello 527 | hello, world 528 | 529 | 删除 `hello` 文件,查看文件系统变化 530 | 531 | $ rm /mnt/hello 532 | $ cp minifs minifs-rm.bak 533 | $ od -x minifs-rm.bak > rm.od 534 | $ diff echo.od rm.od 535 | 536 | 通过查看文件系统的字符串发现:删除文件时并没有覆盖文件内容,所以从理论上说内容此时还是可恢复的 537 | 538 | $ strings minifs 539 | lost+found 540 | hello 541 | hello, world 542 | 543 | 上面仅仅演示了一些分析文件系统的常用工具,并分析了几个常规的操作,如果想非常深入地理解文件系统的实现原理,请熟悉使用上述工具并阅读相关资料。 544 | 545 | 参考资料: 546 | 547 | - [Build a mini filesystem in linux from scratch](http://202.201.1.130:8080/docs/summer_school_2007/team3/doc/build_a_mini_filesystem_from_scratch) 548 | - [Build a mini filesystem in linux with BusyBox](http://202.201.1.130:8080/docs/summer_school_2007/team3/doc/build_a_mini_filesystem_with_busybox) 549 | - [ext2 文件系统](http://man.chinaunix.net/tech/lyceum/linuxK/fs/filesystem.html) 550 | 551 | 552 | ## 如何开发自己的文件系统 553 | 554 | 随着 `fuse` 的出现,在用户空间开发文件系统成为可能,如果想开发自己的文件系统,那么推荐阅读:[使用 fuse 开发自己的文件系统](http://www.ibm.com/developerworks/cn/linux/l-fuse/)。 555 | 556 | 557 | ## 后记 558 | 559 | - 2007 年 12 月 22 日,收集了很多资料,写了整体的框架 560 | - 2007 年 12 月 28 日下午,完成初稿,考虑到时间关系,很多细节也没有进一步分析,另外有些部分可能存在理解上的问题,欢迎批评指正 561 | - 2007 年 12 月 28 日晚,修改部分资料,并正式公开该篇文档 562 | - 29 号,添加设备驱动和硬件设备一小节 563 | -------------------------------------------------------------------------------- /zh/chapters/01-chapter7.markdown: -------------------------------------------------------------------------------- 1 | **关注作者公众号**: 2 |
3 | 4 |
5 | 6 | # 进程操作 7 | 8 | - [前言](#toc_10579_14683_1) 9 | - [什么是程序,什么又是进程](#toc_10579_14683_2) 10 | - [进程的创建](#toc_10579_14683_3) 11 | - [范例:让程序在后台运行](#toc_10579_14683_4) 12 | - [范例:查看进程 ID](#toc_10579_14683_5) 13 | - [范例:查看进程的内存映像](#toc_10579_14683_6) 14 | - [查看进程的属性和状态](#toc_10579_14683_7) 15 | - [范例:通过 ps 命令查看进程属性](#toc_10579_14683_8) 16 | - [范例:通过 pstree 查看进程亲缘关系](#toc_10579_14683_9) 17 | - [范例:用 top 动态查看进程信息](#toc_10579_14683_10) 18 | - [范例:确保特定程序只有一个副本在运行](#toc_10579_14683_11) 19 | - [调整进程的优先级](#toc_10579_14683_12) 20 | - [范例:获取进程优先级](#toc_10579_14683_13) 21 | - [范例:调整进程的优先级](#toc_10579_14683_14) 22 | - [结束进程](#toc_10579_14683_15) 23 | - [范例:结束进程](#toc_10579_14683_16) 24 | - [范例:暂停某个进程](#toc_10579_14683_17) 25 | - [范例:查看进程退出状态](#toc_10579_14683_18) 26 | - [进程通信](#toc_10579_14683_19) 27 | - [范例:无名管道(pipe)](#toc_10579_14683_20) 28 | - [范例:有名管道(named pipe)](#toc_10579_14683_21) 29 | - [范例:信号(Signal)](#toc_10579_14683_22) 30 | - [作业和作业控制](#toc_10579_14683_23) 31 | - [范例:创建后台进程,获取进程的作业号和进程号](#toc_10579_14683_24) 32 | - [范例:把作业调到前台并暂停](#toc_10579_14683_25) 33 | - [范例:查看当前作业情况](#toc_10579_14683_26) 34 | - [范例:启动停止的进程并运行在后台](#toc_10579_14683_27) 35 | - [参考资料](#toc_10579_14683_28) 36 | 37 | 38 | 39 | ## 前言 40 | 41 | 进程作为程序真正发挥作用时的“形态”,我们有必要对它的一些相关操作非常熟悉,这一节主要描述进程相关的概念和操作,将介绍包括程序、进程、作业等基本概念以及进程状态查询、进程通信等相关的操作。 42 | 43 | 44 | ## 什么是程序,什么又是进程 45 | 46 | 程序是指令的集合,而进程则是程序执行的基本单元。为了让程序完成它的工作,必须让程序运行起来成为进程,进而利用处理器资源、内存资源,进行各种 `I/O` 操作,从而完成某项特定工作。 47 | 48 | 从这个意思上说,程序是静态的,而进程则是动态的。 49 | 50 | 进程有区别于程序的地方还有:进程除了包含程序文件中的指令数据以外,还需要在内核中有一个数据结构用以存放特定进程的相关属性,以便内核更好地管理和调度进程,从而完成多进程协作的任务。因此,从这个意义上可以说“高于”程序,超出了程序指令本身。 51 | 52 | 如果进行过多进程程序的开发,又会发现,一个程序可能创建多个进程,通过多个进程的交互完成任务。在 Linux 下,多进程的创建通常是通过 `fork` 系统调用来实现。从这个意义上来说程序则”包含”了进程。 53 | 54 | 另外一个需要明确的是,程序可以由多种不同程序语言描述,包括 C 语言程序、汇编语言程序和最后编译产生的机器指令等。 55 | 56 | 下面简单讨论 Linux 下面如何通过 Shell 进行进程的相关操作。 57 | 58 | 59 | ## 进程的创建 60 | 61 | 通常在命令行键入某个程序文件名以后,一个进程就被创建了。例如, 62 | 63 | 64 | ### 范例:让程序在后台运行 65 | 66 | $ sleep 100 & 67 | [1] 9298 68 | 69 | 70 | ### 范例:查看进程 ID 71 | 72 | 用`pidof`可以查看指定程序名的进程ID: 73 | 74 | $ pidof sleep 75 | 9298 76 | 77 | 78 | ### 范例:查看进程的内存映像 79 | 80 | $ cat /proc/9298/maps 81 | 08048000-0804b000 r-xp 00000000 08:01 977399 /bin/sleep 82 | 0804b000-0804c000 rw-p 00003000 08:01 977399 /bin/sleep 83 | 0804c000-0806d000 rw-p 0804c000 00:00 0 [heap] 84 | b7c8b000-b7cca000 r--p 00000000 08:01 443354 85 | ... 86 | bfbd8000-bfbed000 rw-p bfbd8000 00:00 0 [stack] 87 | ffffe000-fffff000 r-xp 00000000 00:00 0 [vdso] 88 | 89 | 程序被执行后,就被加载到内存中,成为了一个进程。上面显示了该进程的内存映像(虚拟内存),包括程序指令、数据,以及一些用于存放程序命令行参数、环境变量的栈空间,用于动态内存申请的堆空间都被分配好。 90 | 91 | 关于程序在命令行执行过程的细节,请参考[《Linux 命令行下程序执行的一刹那》][100]。 92 | 93 | [100]: 02-chapter3.markdown 94 | 95 | 实际上,创建一个进程,也就是说让程序运行,还有其他的办法,比如,通过一些配置让系统启动时自动启动程序(具体参考 `man init`),或者是通过配置 `crond` (或者 `at`)让它定时启动程序。除此之外,还有一个方式,那就是编写 Shell 脚本,把程序写入一个脚本文件,当执行脚本文件时,文件中的程序将被执行而成为进程。这些方式的细节就不介绍,下面了解如何查看进程的属性。 96 | 97 | 需要补充一点的是:在命令行下执行程序,可以通过 `ulimit` 内置命令来设置进程可以利用的资源,比如进程可以打开的最大文件描述符个数,最大的栈空间,虚拟内存空间等。具体用法见 `help ulimit` 。 98 | 99 | 100 | ## 查看进程的属性和状态 101 | 102 | 可以通过 `ps` 命令查看进程相关属性和状态,这些信息包括进程所属用户,进程对应的程序,进程对 `cpu` 和内存的使用情况等信息。熟悉如何查看它们有助于进行相关的统计分析等操作。 103 | 104 | 105 | ### 范例:通过 ps 命令查看进程属性 106 | 107 | 查看系统当前所有进程的属性: 108 | 109 | $ ps -ef 110 | 111 | 查看命令中包含某字符的程序对应的进程,进程 `ID` 是 1 。 `TTY` 为?表示和终端没有关联: 112 | 113 | $ ps -C init 114 | PID TTY TIME CMD 115 | 1 ? 00:00:01 init 116 | 117 | 选择某个特定用户启动的进程: 118 | 119 | $ ps -U falcon 120 | 121 | 按照指定格式输出指定内容,下面输出命令名和 `cpu` 使用率: 122 | 123 | $ ps -e -o "%C %c" 124 | 125 | 打印 `cpu` 使用率最高的前 4 个程序: 126 | 127 | $ ps -e -o "%C %c" | sort -u -k1 -r | head -5 128 | 7.5 firefox-bin 129 | 1.1 Xorg 130 | 0.8 scim-panel-gtk 131 | 0.2 scim-bridge 132 | 133 | 获取使用虚拟内存最大的 5 个进程: 134 | 135 | $ ps -e -o "%z %c" | sort -n -k1 -r | head -5 136 | 349588 firefox-bin 137 | 96612 xfce4-terminal 138 | 88840 xfdesktop 139 | 76332 gedit 140 | 58920 scim-panel-gtk 141 | 142 | 143 | ### 范例:通过 pstree 查看进程亲缘关系 144 | 145 | 系统所有进程之间都有“亲缘”关系,可以通过 `pstree` 查看这种关系: 146 | 147 | $ pstree 148 | 149 | 上面会打印系统进程调用树,可以非常清楚地看到当前系统中所有活动进程之间的调用关系。 150 | 151 | 152 | ### 范例:用top动态查看进程信息 153 | 154 | $ top 155 | 156 | 该命令最大特点是可以动态地查看进程信息,当然,它还提供了一些其他的参数,比如 `-S` 可以按照累计执行时间的大小排序查看,也可以通过 `-u` 查看指定用户启动的进程等。 157 | 158 | 补充: `top` 命令支持交互式,比如它支持 `u` 命令显示用户的所有进程,支持通过 `k` 命令杀掉某个进程;如果使用 `-n 1` 选项可以启用批处理模式,具体用法为: 159 | 160 | $ top -n 1 -b 161 | 162 | 163 | ### 范例:确保特定程序只有一个副本在运行 164 | 165 | 下面来讨论一个有趣的问题:如何让一个程序在同一时间只有一个在运行。 166 | 167 | 这意味着当一个程序正在被执行时,它将不能再被启动。那该怎么做呢? 168 | 169 | 假如一份相同的程序被复制成了很多份,并且具有不同的文件名被放在不同的位置,这个将比较糟糕,所以考虑最简单的情况,那就是这份程序在整个系统上是唯一的,而且名字也是唯一的。这样的话,有哪些办法来回答上面的问题呢? 170 | 171 | 总的机理是:在程序开头检查自己有没有执行,如果执行了则停止否则继续执行后续代码。 172 | 173 | 策略则是多样的,由于前面的假设已经保证程序文件名和代码的唯一性,所以通过 `ps` 命令找出当前所有进程对应的程序名,逐个与自己的程序名比较,如果已经有,那么说明自己已经运行了。 174 | 175 | ps -e -o "%c" | tr -d " " | grep -q ^init$   #查看当前程序是否执行 176 | [ $? -eq 0 ] && exit  #如果在,那么退出, $?表示上一条指令是否执行成功 177 | 178 | 每次运行时先在指定位置检查是否存在一个保存自己进程 `ID` 的文件,如果不存在,那么继续执行,如果存在,那么查看该进程 `ID` 是否正在运行,如果在,那么退出,否则往该文件重新写入新的进程 `ID`,并继续。 179 | 180 | pidfile=/tmp/$0".pid" 181 | if [ -f $pidfile ]; then 182 | OLDPID=$(cat $pidfile) 183 | ps -e -o "%p" | tr -d " " | grep -q "^$OLDPID$" 184 | [ $? -eq 0 ] && exit 185 | fi 186 | 187 | echo $$ > $pidfile 188 | 189 | #... 代码主体 190 | 191 | #设置信号0的动作,当程序退出时触发该信号从而删除掉临时文件 192 | trap "rm $pidfile"      0 193 | 194 | 更多实现策略自己尽情发挥吧! 195 | 196 | 197 | ## 调整进程的优先级 198 | 199 | 在保证每个进程都能够顺利执行外,为了让某些任务优先完成,那么系统在进行进程调度时就会采用一定的调度办法,比如常见的有按照优先级的时间片轮转的调度算法。这种情况下,可以通过 `renice` 调整正在运行的程序的优先级,例如:` 200 | 201 | 202 | ### 范例:获取进程优先级 203 | 204 | $ ps -e -o "%p %c %n" | grep xfs 205 | 5089 xfs 0 206 | 207 | 208 | ### 范例:调整进程的优先级 209 | 210 | $ renice 1 -p 5089 211 | renice: 5089: setpriority: Operation not permitted 212 | $ sudo renice 1 -p 5089 #需要权限才行 213 | [sudo] password for falcon: 214 | 5089: old priority 0, new priority 1 215 | $ ps -e -o "%p %c %n" | grep xfs #再看看,优先级已经被调整过来了 216 | 5089 xfs 1 217 | 218 | 219 | ## 结束进程 220 | 221 | 既然可以通过命令行执行程序,创建进程,那么也有办法结束它。可以通过 `kill` 命令给用户自己启动的进程发送某个信号让进程终止,当然“万能”的 `root` 几乎可以 `kill` 所有进程(除了 `init` 之外)。例如, 222 | 223 | 224 | ### 范例:结束进程 225 | 226 | $ sleep 50 & #启动一个进程 227 | [1] 11347 228 | $ kill 11347 229 | 230 | `kill` 命令默认会发送终止信号( `SIGTERM` )给程序,让程序退出,但是 `kill` 还可以发送其他信号,这些信号的定义可以通过 `man 7 signal` 查看到,也可以通过 `kill -l` 列出来。 231 | 232 | $ man 7 signal 233 | $ kill -l 234 | 1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 235 | 5) SIGTRAP 6) SIGABRT 7) SIGBUS 8) SIGFPE 236 | 9) SIGKILL 10) SIGUSR1 11) SIGSEGV 12) SIGUSR2 237 | 13) SIGPIPE 14) SIGALRM 15) SIGTERM 16) SIGSTKFLT 238 | 17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP 239 | 21) SIGTTIN 22) SIGTTOU 23) SIGURG 24) SIGXCPU 240 | 25) SIGXFSZ 26) SIGVTALRM 27) SIGPROF 28) SIGWINCH 241 | 29) SIGIO 30) SIGPWR 31) SIGSYS 34) SIGRTMIN 242 | 35) SIGRTMIN+1 36) SIGRTMIN+2 37) SIGRTMIN+3 38) SIGRTMIN+4 243 | 39) SIGRTMIN+5 40) SIGRTMIN+6 41) SIGRTMIN+7 42) SIGRTMIN+8 244 | 43) SIGRTMIN+9 44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 245 | 47) SIGRTMIN+13 48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 246 | 51) SIGRTMAX-13 52) SIGRTMAX-12 53) SIGRTMAX-11 54) SIGRTMAX-10 247 | 55) SIGRTMAX-9 56) SIGRTMAX-8 57) SIGRTMAX-7 58) SIGRTMAX-6 248 | 59) SIGRTMAX-5 60) SIGRTMAX-4 61) SIGRTMAX-3 62) SIGRTMAX-2 249 | 63) SIGRTMAX-1 64) SIGRTMAX 250 | 251 | 252 | ### 范例:暂停某个进程 253 | 254 | 例如,用 `kill` 命令发送 `SIGSTOP` 信号给某个程序,让它暂停,然后发送 `SIGCONT` 信号让它继续运行。 255 | 256 | $ sleep 50 & 257 | [1] 11441 258 | $ jobs 259 | [1]+ Running sleep 50 & 260 | $ kill -s SIGSTOP 11441 #这个等同于我们对一个前台进程执行CTRL+Z操作 261 | $ jobs 262 | [1]+ Stopped sleep 50 263 | $ kill -s SIGCONT 11441 #这个等同于之前我们使用bg %1操作让一个后台进程运行起来 264 | $ jobs 265 | [1]+ Running sleep 50 & 266 | $ kill %1 #在当前会话(session)下,也可以通过作业号控制进程 267 | $ jobs 268 | [1]+ Terminated sleep 50 269 | 270 | 可见 `kill` 命令提供了非常好的功能,不过它只能根据进程的 `ID` 或者作业来控制进程,而 `pkill` 和 `killall` 提供了更多选择,它们扩展了通过程序名甚至是进程的用户名来控制进程的方法。更多用法请参考它们的手册。 271 | 272 | 273 | ### 范例:查看进程退出状态 274 | 275 | 当程序退出后,如何判断这个程序是正常退出还是异常退出呢?还记得 Linux 下,那个经典 `hello world` 程序吗?在代码的最后总是有条 `return 0` 语句。这个 `return 0` 实际上是让程序员来检查进程是否正常退出的。如果进程返回了一个其他的数值,那么可以肯定地说这个进程异常退出了,因为它都没有执行到 `return 0` 这条语句就退出了。 276 | 277 | 那怎么检查进程退出的状态,即那个返回的数值呢? 278 | 279 | 在 `Shell` 中,可以检查这个特殊的变量 `$?`,它存放了上一条命令执行后的退出状态。 280 | 281 | $ test1 282 | bash: test1: command not found 283 | $ echo $? 284 | 127 285 | $ cat ./test.c | grep hello 286 | $ echo $? 287 | 1 288 | $ cat ./test.c | grep hi 289 | printf("hi, myself!\n"); 290 | $ echo $? 291 | 0 292 | 293 | 貌似返回 0 成为了一个潜规则,虽然没有标准明确规定,不过当程序正常返回时,总是可以从 `$?` 中检测到 0,但是异常时,总是检测到一个非 0 值。这就告诉我们在程序的最后最好是跟上一个 `exit 0` 以便任何人都可以通过检测 `$?` 确定程序是否正常结束。如果有一天,有人偶尔用到你的程序,试图检查它的退出状态,而你却在程序的末尾莫名地返回了一个 `-1` 或者 1,那么他将会很苦恼,会怀疑他自己编写的程序到底哪个地方出了问题,检查半天却不知所措,因为他太信任你了,竟然从头至尾都没有怀疑你的编程习惯可能会与众不同! 294 | 295 | 296 | ## 进程通信 297 | 298 | 为便于设计和实现,通常一个大型的任务都被划分成较小的模块。不同模块之间启动后成为进程,它们之间如何通信以便交互数据,协同工作呢?在《UNIX 环境高级编程》一书中提到很多方法,诸如管道(无名管道和有名管道)、信号(`signal`)、报文(`Message`)队列(消息队列)、共享内存(`mmap/munmap`)、信号量(`semaphore`,主要是同步用,进程之间,进程的不同线程之间)、套接口(`Socket`,支持不同机器之间的进程通信)等,而在 Shell 中,通常直接用到的就有管道和信号等。下面主要介绍管道和信号机制在 Shell 编程时的一些用法。 299 | 300 | 301 | ### 范例:无名管道(pipe) 302 | 303 | 在 Linux 下,可以通过 `|` 连接两个程序,这样就可以用它来连接后一个程序的输入和前一个程序的输出,因此被形象地叫做个管道。在 C 语言中,创建无名管道非常简单方便,用 `pipe` 函数,传入一个具有两个元素的 `int` 型的数组就可以。这个数组实际上保存的是两个文件描述符,父进程往第一个文件描述符里头写入东西后,子进程可以从第一个文件描述符中读出来。 304 | 305 | 如果用多了命令行,这个管子 `|` 应该会经常用。比如上面有个演示把 `ps` 命令的输出作为 `grep` 命令的输入: 306 | 307 | $ ps -ef | grep init 308 | 309 | 也许会觉得这个“管子”好有魔法,竟然真地能够链接两个程序的输入和输出,它们到底是怎么实现的呢?实际上当输入这样一组命令时,当前 Shell 会进行适当的解析,把前面一个进程的输出关联到管道的输出文件描述符,把后面一个进程的输入关联到管道的输入文件描述符,这个关联过程通过输入输出重定向函数 `dup` (或者 `fcntl` )来实现。 310 | 311 | 312 | ### 范例:有名管道(named pipe) 313 | 314 | 有名管道实际上是一个文件(无名管道也像一个文件,虽然关系到两个文件描述符,不过只能一边读另外一边写),不过这个文件比较特别,操作时要满足先进先出,而且,如果试图读一个没有内容的有名管道,那么就会被阻塞,同样地,如果试图往一个有名管道里写东西,而当前没有程序试图读它,也会被阻塞。下面看看效果。 315 | 316 | $ mkfifo fifo_test #通过mkfifo命令创建一个有名管道 317 | $ echo "fewfefe" > fifo_test 318 | #试图往fifo_test文件中写入内容,但是被阻塞,要另开一个终端继续下面的操作 319 | $ cat fifo_test #另开一个终端,记得,另开一个。试图读出fifo_test的内容 320 | fewfefe 321 | 322 | 这里的 `echo` 和 `cat` 是两个不同的程序,在这种情况下,通过 `echo` 和 `cat` 启动的两个进程之间并没有父子关系。不过它们依然可以通过有名管道通信。 323 | 324 | 这样一种通信方式非常适合某些特定情况:例如有这样一个架构,这个架构由两个应用程序构成,其中一个通过循环不断读取 `fifo_test` 中的内容,以便判断,它下一步要做什么。如果这个管道没有内容,那么它就会被阻塞在那里,而不会因死循环而耗费资源,另外一个则作为一个控制程序不断地往 `fifo_test` 中写入一些控制信息,以便告诉之前的那个程序该做什么。下面写一个非常简单的例子。可以设计一些控制码,然后控制程序不断地往 `fifo_test` 里头写入,然后应用程序根据这些控制码完成不同的动作。当然,也可以往 `fifo_test` 传入除控制码外的其他数据。 325 | 326 | - 应用程序的代码 327 | 328 | $ cat app.sh 329 | #!/bin/bash 330 | 331 | FIFO=fifo_test 332 | while :; 333 | do 334 | CI=`cat $FIFO` #CI --> Control Info 335 | case $CI in 336 | 0) echo "The CONTROL number is ZERO, do something ..." 337 | ;; 338 | 1) echo "The CONTROL number is ONE, do something ..." 339 | ;; 340 | *) echo "The CONTROL number not recognized, do something else..." 341 | ;; 342 | esac 343 | done 344 | 345 | - 控制程序的代码 346 | 347 | $ cat control.sh 348 | #!/bin/bash 349 | 350 | FIFO=fifo_test 351 | CI=$1 352 | 353 | [ -z "$CI" ] && echo "the control info should not be empty" && exit 354 | 355 | echo $CI > $FIFO 356 | 357 | - 一个程序通过管道控制另外一个程序的工作 358 | 359 | $ chmod +x app.sh control.sh #修改这两个程序的可执行权限,以便用户可以执行它们 360 | $ ./app.sh #在一个终端启动这个应用程序,在通过./control.sh发送控制码以后查看输出 361 | The CONTROL number is ONE, do something ... #发送1以后 362 | The CONTROL number is ZERO, do something ... #发送0以后 363 | The CONTROL number not recognized, do something else... #发送一个未知的控制码以后 364 | $ ./control.sh 1 #在另外一个终端,发送控制信息,控制应用程序的工作 365 | $ ./control.sh 0 366 | $ ./control.sh 4343 367 | 368 | 这样一种应用架构非常适合本地的多程序任务设计,如果结合 `web cgi`,那么也将适合远程控制的要求。引入 `web cgi` 的唯一改变是,要把控制程序 `./control.sh` 放到 `web` 的 `cgi` 目录下,并对它作一些修改,以使它符合 `CGI` 的规范,这些规范包括文档输出格式的表示(在文件开头需要输出 `content-tpye: text/html` 以及一个空白行)和输入参数的获取 `(web` 输入参数都存放在 `QUERY_STRING` 环境变量里头)。因此一个非常简单的 `CGI` 控制程序可以写成这样: 369 | 370 | #!/bin/bash 371 | 372 | FIFO=./fifo_test 373 | CI=$QUERY_STRING 374 | 375 | [ -z "$CI" ] && echo "the control info should not be empty" && exit 376 | 377 | echo -e "content-type: text/html\n\n" 378 | echo $CI > $FIFO 379 | 380 | 在实际使用时,请确保 `control.sh` 能够访问到 `fifo_test` 管道,并且有写权限,以便通过浏览器控制 `app.sh` : 381 | 382 | http://ipaddress\_or\_dns/cgi-bin/control.sh?0 383 | 384 | 问号 `?` 后面的内容即 `QUERY_STRING`,类似之前的 `$1` 。 385 | 386 | 这样一种应用对于远程控制,特别是嵌入式系统的远程控制很有实际意义。在去年的暑期课程上,我们就通过这样一种方式来实现马达的远程控制。首先,实现了一个简单的应用程序以便控制马达的转动,包括转速,方向等的控制。为了实现远程控制,我们设计了一些控制码,以便控制马达转动相关的不同属性。 387 | 388 | 在 C 语言中,如果要使用有名管道,和 Shell 类似,只不过在读写数据时用 `read`,`write` 调用,在创建 `fifo` 时用 `mkfifo` 函数调用。 389 | 390 | 391 | ### 范例:信号(Signal) 392 | 393 | 信号是软件中断,Linux 用户可以通过 `kill` 命令给某个进程发送一个特定的信号,也可以通过键盘发送一些信号,比如 `CTRL+C` 可能触发 `SGIINT` 信号,而 `CTRL+\` 可能触发 `SGIQUIT` 信号等,除此之外,内核在某些情况下也会给进程发送信号,比如在访问内存越界时产生 `SGISEGV` 信号,当然,进程本身也可以通过 `kill`,`raise` 等函数给自己发送信号。对于 Linux 下支持的信号类型,大家可以通过 `man 7 signal` 或者 `kill -l` 查看到相关列表和说明。 394 | 395 | 对于有些信号,进程会有默认的响应动作,而有些信号,进程可能直接会忽略,当然,用户还可以对某些信号设定专门的处理函数。在 Shell 中,可以通过 `trap` 命令(Shell 内置命令)来设定响应某个信号的动作(某个命令或者定义的某个函数),而在 C 语言中可以通过 `signal` 调用注册某个信号的处理函数。这里仅仅演示 `trap` 命令的用法。 396 | 397 | $ function signal_handler { echo "hello, world."; } #定义signal_handler函数 398 | $ trap signal_handler SIGINT #执行该命令设定:收到SIGINT信号时打印hello, world 399 | $ hello, world #按下CTRL+C,可以看到屏幕上输出了hello, world字符串 400 | 401 | 类似地,如果设定信号 0 的响应动作,那么就可以用 `trap` 来模拟 C 语言程序中的 `atexit` 程序终止函数的登记,即通过 `trap signal_handler SIGQUIT` 设定的 `signal_handler` 函数将在程序退出时执行。信号 0 是一个特别的信号,在 `POSIX.1` 中把信号编号 0 定义为空信号,这常被用来确定一个特定进程是否仍旧存在。当一个程序退出时会触发该信号。 402 | 403 | $ cat sigexit.sh 404 | #!/bin/bash 405 | 406 | function signal_handler { 407 | echo "hello, world" 408 | } 409 | trap signal_handler 0 410 | $ chmod +x sigexit.sh 411 | $ ./sigexit.sh #实际Shell编程会用该方式在程序退出时来做一些清理临时文件的收尾工作 412 | hello, world 413 | 414 | 415 | ## 作业和作业控制 416 | 417 | 当我们为完成一些复杂的任务而将多个命令通过 `|,\>,<, ;, (,)` 等组合在一起时,通常这个命令序列会启动多个进程,它们间通过管道等进行通信。而有时在执行一个任务的同时,还有其他的任务需要处理,那么就经常会在命令序列的最后加上一个&,或者在执行命令后,按下 `CTRL+Z` 让前一个命令暂停。以便做其他的任务。等做完其他一些任务以后,再通过 `fg` 命令把后台任务切换到前台。这样一种控制过程通常被成为作业控制,而那些命令序列则被成为作业,这个作业可能涉及一个或者多个程序,一个或者多个进程。下面演示一下几个常用的作业控制操作。 418 | 419 | 420 | ### 范例:创建后台进程,获取进程的作业号和进程号 421 | 422 | $ sleep 50 & 423 | [1] 11137 424 | 425 | 426 | ### 范例:把作业调到前台并暂停 427 | 428 | 使用 Shell 内置命令 `fg` 把作业 1 调到前台运行,然后按下 `CTRL+Z` 让该进程暂停 429 | 430 | $ fg %1 431 | sleep 50 432 | ^Z 433 | [1]+ Stopped sleep 50 434 | 435 | 436 | ### 范例:查看当前作业情况 437 | 438 | $ jobs #查看当前作业情况,有一个作业停止 439 | [1]+ Stopped sleep 50 440 | $ sleep 100 & #让另外一个作业在后台运行 441 | [2] 11138 442 | $ jobs #查看当前作业情况,一个正在运行,一个停止 443 | [1]+ Stopped sleep 50 444 | [2]- Running sleep 100 & 445 | 446 | 447 | ### 范例:启动停止的进程并运行在后台 448 | 449 | $ bg %1 450 | [2]+ sleep 50 & 451 | 452 | 不过,要在命令行下使用作业控制,需要当前 Shell,内核终端驱动等对作业控制支持才行。 453 | 454 | 455 | ## 参考资料 456 | 457 | - 《UNIX 环境高级编程》 458 | -------------------------------------------------------------------------------- /zh/chapters/01-chapter8.markdown: -------------------------------------------------------------------------------- 1 | **关注作者公众号**: 2 |
3 | 4 |
5 | 6 | # 网络操作 7 | 8 | - [前言](#toc_5782_1744_1) 9 | - [网络原理介绍](#toc_5782_1744_2) 10 | - [我们的网络世界](#toc_5782_1744_3) 11 | - [网络体系结构和网络协议介绍](#toc_5782_1744_4) 12 | - [Linux 下网络“实战”](#toc_5782_1744_5) 13 | - [如何把我们的 Linux 主机接入网络](#toc_5782_1744_6) 14 | - [范例:通过dhclient获取IP地址](#toc_5782_1744_7) 15 | - [范例:静态配置IP地址](#toc_5782_1744_8) 16 | - [用 Linux 搭建网桥](#toc_5782_1744_9) 17 | - [用 Linux 做路由](#toc_5782_1744_10) 18 | - [用 Linux 搭建各种常规的网络服务](#toc_5782_1744_11) 19 | - [Linux 下网络问题诊断与维护](#toc_5782_1744_12) 20 | - [Linux 下网络编程与开发](#toc_5782_1744_13) 21 | - [后记](#toc_5782_1744_14) 22 | - [参考资料](#toc_5782_1744_15) 23 | 24 | 25 | 26 | ## 前言 27 | 28 | 前面章节已经介绍了Shell编程范例之数值、布尔值、字符串、文件、文件系统、进程等的操作。这些内容基本覆盖了网络中某个独立机器正常工作的“方方面面”,现在需要把视角从单一的机器延伸到这些机器通过各种网络设备和协议连接起来的网络世界,分析网络拓扑结构、网络工作原理、了解各种常见网络协议、各种常见硬件工作原理、网络通信与安全相关软件以及工作原理分析等。 29 | 30 | 不过,因为网络相关的问题确实太复杂了,这里不可能介绍具体,因此如果想了解更多细节,还是建议参考相关资料。但Linux是一个网络原理学习和实践的好平台,不仅因为它本身对网络体系结构的实现是开放源代码的,而且各种相关的分析工具和函数库数不胜数,因此,如果你是学生,千万不要错过通过它来做相关的实践工作。 31 | 32 | 33 | ## 网络原理介绍 34 | 35 | 36 | ### 我们的网络世界 37 | 38 | 在进行所有介绍之前,来直观地感受一下那个真真实实存在的网络世界吧。当我在 Linux 下通过 `Web` 编辑器写这篇 Blog 时,一边用 `mplayer` 听着远程音乐,累了时则打开兰大的网络 `TV` 频道开始看看凤凰卫视……这些“现代化”的生活,我想,如果没有网络,将变得无法想象。 39 | 40 | 下面来构想一下这样一个网络世界的优美图画: 41 | 42 | > 一边盯着显示器,一边敲击着键盘,一边挂着耳机。 43 | 44 | > 主机电源灯灿烂得很,发着绿光,这时很容易想象主机背后的那个网卡位置肯定有两个不同颜色的灯光在闪烁,它显示着主机正在与计算机网络世界打着交道。 45 | 46 | > 就在实验室的某个角落,有一个交换机上的一个网口的网线连到主机上,这个交换机接到了一个局域网的网关上,然后这个网关再接到了信息楼的某个路由器上,再转接到学校网络中心的另外一个路由器上…… 47 | 48 | > 期间,有一个路由器连接到了这个 Blog 服务器上,而另外一个则可能连到了那个网络 `TV` 服务器上,还有呢,另外一些则连接到了电信网络里头的某个音乐服务器上…… 49 | 50 | 下面用 `dia` 绘制一个简单的“网络地图”: 51 | 52 | ![Network Architecture](pic/Network_Architecture.jpg) 53 | 54 | 该图把一些最常见的网络设备和网络服务基本都呈现出来了,包括本地主机、路由、交换机、网桥,域名服务器,万维网服务,视频服务,防火墙服务,动态 `IP` 地址服务等。其中各种设备构成了整个物理网络,而网络服务则是构建在这些设备上的各种网络应用。 55 | 56 | 现在的网络应用越来越丰富多样,比如即时聊天(`IM`)、 `p2p` 资源共享、网络搜索等,它们是如何实现的,它们如何构建在各种各样的网络设备之上,并且能够安全有效的工作呢?这取决于这背后逐步完善的网络体系结构和各种相关网络协议的开发、实现和应用。 57 | 58 | 59 | ### 网络体系结构和网络协议介绍 60 | 61 | 那么网络体系结构是怎么样的呢?涉及到哪些相关的网络协议呢?什么又是网络协议呢? 62 | 63 | 在《计算机网络——自顶向下的方法》一书中非常巧妙地给出了网络体系结构分层的比喻,把网络中各层跟交通运输体系中的各个环节对照起来,让人通俗易懂。在交通运输体系中,运输的是人和物品,在计算机网络体系中,运输的是电子数据。考虑到交通运输网络和计算机网络中最终都可以划归为点对点的信息传输。这里考虑两点之间的信息传递过程,得到这样一个对照关系,见下图: 64 | 65 | ![Network Layer与交通运输体系比较](pic/Network_Layer_Compare.jpg) 66 | 67 | 对照上图,更容易理解右侧网络体系结构的分层原理(如果比照一封信发出到收到的这一中间过程可能更容易理解),上图右侧是 `TCP/IP` 网络体系结构的一个网络分层示意图,在把数据发送到网络之前,在各层中需要进行各种“打包”的操作,而从网络接收到数据后,就需要进行“解包”操作,最终把纯粹的数据信息给提取出来。这种分层的方式是为了传输数据的需要,也是两个主机之间如何建立连接以及如何保证数据传输的完整性和可靠性的需要。通过把各种需要分散在不同的层次,使得整个体系结构更加清晰和明了。这些“需求”具体通过各种对应的协议来规范,这些规范统成为网络协议。 68 | 69 | 关于 `OSI` 模型(7 层)比照 `TCP/IP` 模型(4 层)的协议栈可以从下图(来自网络)看个明了: 70 | 71 | ![OSI Network Layer](pic/Network_Layer_OSI.jpg) 72 | 73 | 而下图(来自网络)则更清晰地体现了 `TCP/IP` 分层模型。 74 | 75 | ![TCP IP Network Layer](pic/Network_Layer_TCP_IP.jpg) 76 | 77 | 上面介绍了网络原理方面的基本内容,如果想了解更多网络原理和操作系统对网络支持的实现,可以考虑阅读后面的参考资料。下面将做一些实践,即在 Linux 下如何联网,如何用 Linux 搭建各种网络服务,并进行网络安全方面的考量以及基本的网络编程和开发的介绍。 78 | 79 | 80 | ## Linux 下网络“实战” 81 | 82 | 83 | ### 如何把我们的 Linux 主机接入网络 84 | 85 | 如果要让一个系统能够联网,首先当然是搭建好物理网络了。接入网络的物理方式还是蛮多的,比如直接用网线接入以太网,用无线网卡上网,用 `ADSL` 拨号上网…… 86 | 87 | 对于用以太网网卡接入网络的常见方式,在搭建好物理网络并确保连接正常后,可以通过配置 `IP` 地址和默认网关来接入网络,这个可以通过手工配置和动态获取两种方式。 88 | 89 | 90 | #### 范例:通过dhclient获取IP地址 91 | 92 | 如果所在的局域网有 `DHCP` 服务,那么可以这么获取,`N` 是设备名称,如果只有一块网卡,一般是 0 或者 1 。 93 | 94 | $ dhclient ethN 95 | 96 | 97 | #### 范例:静态配置IP地址 98 | 99 | 当然,也可以考虑采用静态配置的方式,`ip_address` 是本地主机的 `IP` 地址,`gw_ip_address` 是接入网络的网关的 `IP` 地址。 100 | 101 | $ ifconfig eth0 ip_address on 102 | $ route add deafult gw gw_ip_address 103 | 104 | 如果上面不工作,记得通过 `ifconfig/mii-tool/ethtool` 等工具检查网卡是否有被驱动起来,然后通过 `lspci/dmesg` 等检查网卡类型(或者通过主板手册和独立网卡自带的手册查看),接着安装或者编译相关驱动,最后把驱动通过 `insmod/modprobe` 等工具加载到内核中。 105 | 106 | 107 | ### 用 Linux 搭建网桥 108 | 109 | 网桥工作在 `OSI` 模型的第二层,即数据链路层,它只需要知道目标主机的 `MAC` 地址就可以工作。 Linux 内核在 `2.2` 开始就已经支持了这个功能,具体怎么配置看看后续[参考资料](http://www.ibm.com/developerworks/cn/linux/kernel/l-netbr/index.html)吧。如果要把 Linux 主机配置成一个网桥,至少需要两个网卡。 110 | 111 | 网桥的作用相当于一根网线,用户无须关心里头有什么东西,把它的两个网口连接到两个主机上就可以让这两个主机支持相互通信。不过它比网线更厉害,如果配上防火墙,就可以隔离连接在它两端的网段(注意这里是网络,因为它不识别 `IP`),另外,如果这个网桥有多个网口,那么可以实现一个功能复杂的交换机,而如果有效组合多个网桥,则有可能实现一个复杂的可实现流量控制和负载平衡的防火墙系统。 112 | 113 | 114 | ### 用 Linux 做路由 115 | 116 | 路由工作在 `OSI` 模型的第三层,即网络层,通过 `router` 可以配置 Linux 的路由,当然,Linux 下也有很多工具支持动态路由的。相关的资料在网路中铺天盖地,由于时间关系,这里不做介绍。 117 | 118 | 119 | ### 用 Linux 搭建各种常规的网络服务 120 | 121 | 需要什么网络服务呢? 122 | 123 | - 给局域网弄个 `DHCP` 服务器,那就弄个 `dhcpd`,看看[参考资料](http://tldp.org/HOWTO/DHCP/); 124 | - 如果想弄个邮件发送服务器,那就安装个 `sendmail` 或者 `exim4` ; 125 | - 如果再想弄个邮件列表服务器呢,那就装个 `mailman` ; 126 | - 如果想弄个接收邮件的服务器呢,那就安装个 `pop3` 服务器; 127 | - 如果想弄个 `web` 站点,那就弄个 `apache` 或者 `nginx` 服务器; 128 | - 如果想弄上防火墙服务,那么通过 `iptables` 工具配置 `netfilter` 就可以 129 | 130 | What's more?如果你能想到,Linux上基本都有相应的实现。 131 | 132 | 133 | ### Linux 下网络问题诊断与维护 134 | 135 | 如果出现网络问题,不要惊慌,逐步检查网络的各个层次:物理链接、链路层、网络层直到应用层,熟悉使用各种如下的工具,包括 `ethereal/tcpdump`,`hping`,`nmap`,`netstat`,`netpipe`,`netperf`,`vnstat`,`ntop` 等。 136 | 137 | 关于这些工具的详细用法和网络问题诊断和维护的相关知识,请看后续相关资料。 138 | 139 | 140 | ## Linux 下网络编程与开发 141 | 142 | 如果想做网络编程开发,比如: 143 | 144 | - 要实现一个客户端 `/` 服务器架构的应用,可以采用 Linux 下的 `socket` 编程了; 145 | - 如果想写一个数据包抓获和协议分析的程序,可以采用 `libpap` 等函数库; 146 | - 如果想实现某个协议呢,那就可以参考相关的 `RFC` 文档,并通过 `socket` 编程来实现。 147 | 148 | 这个可以参考相关的 `Linux socket` 编程等资料。 149 | 150 | 151 | ## 后记 152 | 153 | 本来介绍网络相关的一些基本内容,但因时间关系,没有详述,更多细节请参考相关资料。 154 | 155 | 到这里,整个《Shell编程范例》算是很粗略地完成了,不过“范例”却缺少实例,特别是这一节。因此,如果时间允许,会逐步补充一些实例。 156 | 157 | 158 | ## 参考资料 159 | 160 | - 计算机网络——自上而下的分析方法 161 | - Linux 网络体系结构(清华大学出版社出版) 162 | - Linux 系统故障诊断与排除 第13章 网络问题(人民邮电出版社) 163 | - 在 Linux 下用 ADSL 拨号上网 164 | - Linux 下无线网络相关资料收集 165 | - [Linux网桥的实现分析与使用](http://www.ibm.com/developerworks/cn/linux/kernel/l-netbr/index.html) 166 | - [DHCP mini howto](http://tldp.org/HOWTO/DHCP/) 167 | - 最佳的 75 个安全工具 168 | - 网络管理员必须掌握的知识 169 | - Linux 上检测 rootkit 的两种工具: Rootkit Hunter 和 Chkrootkit 170 | - 数据包抓获与 ip 协议的简单分析(基于 pcap 库) 171 | - [RFC](http://www.ietf.org/rfc) 172 | - [HTTP 协议的 C 语言编程实现实例](http://zhoulifa.bokee.com/4640913.html) 173 | -------------------------------------------------------------------------------- /zh/chapters/01-chapter9.markdown: -------------------------------------------------------------------------------- 1 | **关注作者公众号**: 2 |
3 | 4 |
5 | 6 | # 用户管理 7 | 8 | - [用户帐号](#toc_13359_11834_1) 9 | - [添加](#toc_13359_11834_2) 10 | - [删除](#toc_13359_11834_3) 11 | - [修改](#toc_13359_11834_4) 12 | - [禁用](#toc_13359_11834_5) 13 | - [用户口令](#toc_13359_11834_6) 14 | - [设置](#toc_13359_11834_7) 15 | - [删除](#toc_13359_11834_8) 16 | - [修改](#toc_13359_11834_9) 17 | - [禁用](#toc_13359_11834_10) 18 | - [用户组别](#toc_13359_11834_11) 19 | - [添加](#toc_13359_11834_12) 20 | - [删除](#toc_13359_11834_13) 21 | - [修改](#toc_13359_11834_14) 22 | - [用户和组](#toc_13359_11834_15) 23 | - [增加](#toc_13359_11834_16) 24 | - [删除](#toc_13359_11834_17) 25 | - [用户切换](#toc_13359_11834_18) 26 | - [切换帐号](#toc_13359_11834_19) 27 | - [免密码切到 Root](#toc_13359_11834_20) 28 | 29 | 30 | 在初次撰写本书时,都只讨论到了“物”,而没有关注“人”。而在实际使用中,Linux 系统首先是面向用户的系统,所有之前介绍的内容全部是提供给不同的用户使用的。实际使用中常常碰到各类用户操作,所以这里添加一个独立的章节来介绍。 31 | 32 | Linux 支持多用户,也就是说允许不同的人使用同一个系统,每个人有一个属于自己的帐号。而且允许大家设置不同的认证密码,确保大家的私有信息得到保护。另外,为了确保整个系统的安全,用户权限又做了进一步划分,包括普通用户和系统管理员。普通用户只允许访问自己账户授权下的信息,而系统管理员才能访问所有资源。普通用户如果想行使管理员的职能,必须获得系统管理员的许可。 33 | 34 | 为避免分散注意力,咱们不去介绍背后的那些数据文件: 35 | `/etc/passwd`,`/etc/shadow`,`/etc/group`,`/etc/gshadow` 36 | 37 | 如果确实有需要,大家可通过如下命令查看帮助: 38 | `man 5 passwd`,`man shadow`, `man group` 和 `man gshadow` 39 | 40 | 下面我们分如下几个部分来介绍: 41 | 42 | * 用户帐号 43 | * 用户口令 44 | * 用户组别 45 | * 用户和组 46 | * 用户切换 47 | 48 | 49 | ## 用户帐号 50 | 51 | 帐号操作主要是增、删、改、禁。Linux 系统提供了底层的 `useradd`, `userdel` 和 `usermod` 来完成相关操作,也提供了进一步的简化封装:`adduser`, `deluser`。为了避免混淆,咱们这里只介绍最底层的指令,这些指令设计上已经够简洁明了方便。 52 | 53 | 由于只有系统管理员才能创建新用户,请确保以 root 帐号登录或者可以通过 sudo 切换为管理员帐号。 54 | 55 | 56 | ### 添加 57 | 58 | 创建家目录并指定登录 Shell: 59 | 60 | # useradd -s /bin/bash -m test 61 | # groups test 62 | test : test 63 | 64 | 并加入所属组: 65 | 66 | # useradd -s /bin/bash -m -G docker test 67 | # groups test 68 | test : test docker 69 | 70 | 71 | 72 | ### 删除 73 | 74 | 删除用户以及家目录等: 75 | 76 | # userdel -r test 77 | 78 | 79 | ### 修改 80 | 81 | 常常用来修改默认的 Shell: 82 | 83 | # usermod -s /bin/bash test 84 | 85 | 或者把用户加入某个新安装软件所属的组: 86 | 87 | # usermod -a -G docker test 88 | 89 | 修改登录用户名并搬到新家: 90 | 91 | # usermod -d /home/new_test -m -l new_test test 92 | 93 | 94 | ### 禁用 95 | 96 | 如果想禁用某个帐号: 97 | 98 | # usermod -L test 99 | # usermod --expiredate 1 test 100 | 101 | 102 | ## 用户口令 103 | 104 | 口令操作主要是设置、删除、修改和禁用。Linux 系统提供了 `passwd` 命令来管理用户口令。 105 | 106 | 107 | ### 设置 108 | 109 | 设置用户 test 的初始密码: 110 | 111 | $ passwd test 112 | Enter new UNIX password: 113 | Retype new UNIX password: 114 | passwd: password updated successfully 115 | 116 | 117 | 118 | ### 删除 119 | 120 | 让用户 test 无须密码登录(密码为空): 121 | 122 | $ passwd -d test 123 | 124 | 这个很方便某些安全无关紧要的条件下(比如已登录主机中的虚拟机),可避免每次频繁输入密码。 125 | 126 | 127 | ### 修改 128 | 129 | $ passwd test 130 | Changing password for test. 131 | (current) UNIX password: 132 | Enter new UNIX password: 133 | Retype new UNIX password: 134 | passwd: password updated successfully 135 | 136 | 137 | ### 禁用 138 | 139 | 禁止用户通过密码登录: 140 | 141 | $ passwd -l user 142 | 143 | 为了安全起见或者为了避免暴力破解,我们通常可以禁用密码登录,而只允许通过 SSH Key 登录。 144 | 145 | 如果要真地禁用整个帐号的使用,需要用上一节提到的 `usermod --expiredate 1`。 146 | 147 | 148 | ## 用户组别 149 | 150 | 类似帐号,主要操作也是增、删、改。 151 | 152 | Linux 系统提供了底层的 `groupadd`, `groupdel` 和 `groupmod` 来完成相关操作,也提供了进一步的简化封装:`addgroup`, `delgroup`。 153 | 154 | 用户组别通常用来管理不同的资源,确保只有某个组别的用户才可以访问某类资源。当然,实际案例中,有些软件也为自己定义一个组别,只有该组别的用户才能访问该软件的一些功能。 155 | 156 | 157 | ### 添加 158 | 159 | 添加一个新组别: 160 | 161 | # groupadd test 162 | 163 | 164 | ### 删除 165 | 166 | # groupdel test 167 | 168 | 169 | ### 修改 170 | 171 | 修改组别名: 172 | 173 | # groupmod -n new_test test 174 | 175 | 176 | ## 用户和组 177 | 178 | 用户和组别不能独立存在,`gpasswd` 可以用来处理两者的关系。 179 | 180 | 181 | ### 增加 182 | 183 | 从 docker 组中增加用户 test(等同于把 test 增加到 docker 组中): 184 | 185 | # gpasswd -a test docker 186 | 187 | 或 188 | 189 | # usermod -a -G docker test 190 | 191 | 192 | ### 删除 193 | 194 | 从 test 组中删除用户 test: 195 | 196 | # gpasswd -d test test 197 | 198 | 199 | ## 用户切换 200 | 201 | 由于支持多用户,那么在登录一个帐号后,可能需要切换到另外一个帐号下,可以通过 `su` 命令完成,而 `sudo` 则可以用来作为另外一个用户来执行命令。 202 | 203 | 204 | ### 切换帐号 205 | 206 | 切换到 Root 并启用 Bash: 207 | 208 | $ su -s /bin/bash - 209 | root@falcon-desktop:~# 210 | 211 | 或者 212 | 213 | $ sudo -s 214 | 215 | 切换到普通用户: 216 | 217 | $ su -s /bin/bash - test 218 | test@falcon-desktop:~$ 219 | 220 | 或者 221 | 222 | $ sudo -i -u test 223 | test@falcon-desktop:~$ 224 | 225 | 226 | ### 免密码切到 Root 227 | 228 | 首先得把用户加入到 sudo 用户组: 229 | 230 | # usermod -a -G sudo falcon 231 | 232 | 否则,会看到如下信息: 233 | 234 | $ sudo -s 235 | [sudo] password for test: 236 | test is not in the sudoers file. This incident will be reported. 237 | 238 | 加入 sudo 用户组以后: 239 | 240 | $ sudo -s 241 | [sudo] password for test: 242 | 243 | 要实现免密切换,需要先修改 `/etc/sudoers`,加入如下一行: 244 | 245 | test ALL=(ALL) NOPASSWD: ALL 246 | 247 | 或者在 `/etc/sudoers.d/` 下创建一个文件并加入上述内容。 248 | 249 | # echo "test ALL=(ALL) NOPASSWD: ALL" > /etc/sudoers.d/test 250 | # chmod 440 /etc/sudoers.d/test 251 | -------------------------------------------------------------------------------- /zh/chapters/pic/Linux_FileSystem_Architecture-150x150.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tinyclub/open-shell-book/752ffde2badb9b2291af79208fa2f85d958ca9b1/zh/chapters/pic/Linux_FileSystem_Architecture-150x150.jpg -------------------------------------------------------------------------------- /zh/chapters/pic/Linux_FileSystem_Architecture-197x300.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tinyclub/open-shell-book/752ffde2badb9b2291af79208fa2f85d958ca9b1/zh/chapters/pic/Linux_FileSystem_Architecture-197x300.jpg -------------------------------------------------------------------------------- /zh/chapters/pic/Linux_FileSystem_Architecture.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tinyclub/open-shell-book/752ffde2badb9b2291af79208fa2f85d958ca9b1/zh/chapters/pic/Linux_FileSystem_Architecture.jpg -------------------------------------------------------------------------------- /zh/chapters/pic/MBR_Architecture-150x150.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tinyclub/open-shell-book/752ffde2badb9b2291af79208fa2f85d958ca9b1/zh/chapters/pic/MBR_Architecture-150x150.jpg -------------------------------------------------------------------------------- /zh/chapters/pic/MBR_Architecture-300x139.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tinyclub/open-shell-book/752ffde2badb9b2291af79208fa2f85d958ca9b1/zh/chapters/pic/MBR_Architecture-300x139.jpg -------------------------------------------------------------------------------- /zh/chapters/pic/MBR_Architecture.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tinyclub/open-shell-book/752ffde2badb9b2291af79208fa2f85d958ca9b1/zh/chapters/pic/MBR_Architecture.jpg -------------------------------------------------------------------------------- /zh/chapters/pic/Network_Architecture-150x150.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tinyclub/open-shell-book/752ffde2badb9b2291af79208fa2f85d958ca9b1/zh/chapters/pic/Network_Architecture-150x150.jpg -------------------------------------------------------------------------------- /zh/chapters/pic/Network_Architecture-300x176.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tinyclub/open-shell-book/752ffde2badb9b2291af79208fa2f85d958ca9b1/zh/chapters/pic/Network_Architecture-300x176.jpg -------------------------------------------------------------------------------- /zh/chapters/pic/Network_Architecture.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tinyclub/open-shell-book/752ffde2badb9b2291af79208fa2f85d958ca9b1/zh/chapters/pic/Network_Architecture.jpg -------------------------------------------------------------------------------- /zh/chapters/pic/Network_Layer_Compare-150x150.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tinyclub/open-shell-book/752ffde2badb9b2291af79208fa2f85d958ca9b1/zh/chapters/pic/Network_Layer_Compare-150x150.jpg -------------------------------------------------------------------------------- /zh/chapters/pic/Network_Layer_Compare-300x118.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tinyclub/open-shell-book/752ffde2badb9b2291af79208fa2f85d958ca9b1/zh/chapters/pic/Network_Layer_Compare-300x118.jpg -------------------------------------------------------------------------------- /zh/chapters/pic/Network_Layer_Compare.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tinyclub/open-shell-book/752ffde2badb9b2291af79208fa2f85d958ca9b1/zh/chapters/pic/Network_Layer_Compare.jpg -------------------------------------------------------------------------------- /zh/chapters/pic/Network_Layer_OSI-150x150.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tinyclub/open-shell-book/752ffde2badb9b2291af79208fa2f85d958ca9b1/zh/chapters/pic/Network_Layer_OSI-150x150.jpg -------------------------------------------------------------------------------- /zh/chapters/pic/Network_Layer_OSI-300x221.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tinyclub/open-shell-book/752ffde2badb9b2291af79208fa2f85d958ca9b1/zh/chapters/pic/Network_Layer_OSI-300x221.jpg -------------------------------------------------------------------------------- /zh/chapters/pic/Network_Layer_OSI.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tinyclub/open-shell-book/752ffde2badb9b2291af79208fa2f85d958ca9b1/zh/chapters/pic/Network_Layer_OSI.jpg -------------------------------------------------------------------------------- /zh/chapters/pic/Network_Layer_TCP_IP-118x300.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tinyclub/open-shell-book/752ffde2badb9b2291af79208fa2f85d958ca9b1/zh/chapters/pic/Network_Layer_TCP_IP-118x300.jpg -------------------------------------------------------------------------------- /zh/chapters/pic/Network_Layer_TCP_IP-150x150.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tinyclub/open-shell-book/752ffde2badb9b2291af79208fa2f85d958ca9b1/zh/chapters/pic/Network_Layer_TCP_IP-150x150.jpg -------------------------------------------------------------------------------- /zh/chapters/pic/Network_Layer_TCP_IP.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tinyclub/open-shell-book/752ffde2badb9b2291af79208fa2f85d958ca9b1/zh/chapters/pic/Network_Layer_TCP_IP.jpg -------------------------------------------------------------------------------- /zh/chapters/pic/UI_Shell_and_GUI-150x150.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tinyclub/open-shell-book/752ffde2badb9b2291af79208fa2f85d958ca9b1/zh/chapters/pic/UI_Shell_and_GUI-150x150.jpg -------------------------------------------------------------------------------- /zh/chapters/pic/UI_Shell_and_GUI-300x190.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tinyclub/open-shell-book/752ffde2badb9b2291af79208fa2f85d958ca9b1/zh/chapters/pic/UI_Shell_and_GUI-300x190.jpg -------------------------------------------------------------------------------- /zh/chapters/pic/UI_Shell_and_GUI.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tinyclub/open-shell-book/752ffde2badb9b2291af79208fa2f85d958ca9b1/zh/chapters/pic/UI_Shell_and_GUI.jpg -------------------------------------------------------------------------------- /zh/chapters/pic/cover-tinylab.org.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tinyclub/open-shell-book/752ffde2badb9b2291af79208fa2f85d958ca9b1/zh/chapters/pic/cover-tinylab.org.png -------------------------------------------------------------------------------- /zh/chapters/pic/cover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tinyclub/open-shell-book/752ffde2badb9b2291af79208fa2f85d958ca9b1/zh/chapters/pic/cover.png -------------------------------------------------------------------------------- /zh/chapters/pic/mommy-tea.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tinyclub/open-shell-book/752ffde2badb9b2291af79208fa2f85d958ca9b1/zh/chapters/pic/mommy-tea.png -------------------------------------------------------------------------------- /zh/chapters/pic/tinylab-targets-150x110.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tinyclub/open-shell-book/752ffde2badb9b2291af79208fa2f85d958ca9b1/zh/chapters/pic/tinylab-targets-150x110.gif -------------------------------------------------------------------------------- /zh/chapters/pic/tinylab-targets-150x110.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tinyclub/open-shell-book/752ffde2badb9b2291af79208fa2f85d958ca9b1/zh/chapters/pic/tinylab-targets-150x110.jpg -------------------------------------------------------------------------------- /zh/chapters/pic/tinylab-targets-300x54.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tinyclub/open-shell-book/752ffde2badb9b2291af79208fa2f85d958ca9b1/zh/chapters/pic/tinylab-targets-300x54.gif -------------------------------------------------------------------------------- /zh/chapters/pic/tinylab-targets-300x54.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tinyclub/open-shell-book/752ffde2badb9b2291af79208fa2f85d958ca9b1/zh/chapters/pic/tinylab-targets-300x54.jpg -------------------------------------------------------------------------------- /zh/chapters/pic/tinylab-targets.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tinyclub/open-shell-book/752ffde2badb9b2291af79208fa2f85d958ca9b1/zh/chapters/pic/tinylab-targets.jpg -------------------------------------------------------------------------------- /zh/chapters/pic/tinylab.org-sina-weibo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tinyclub/open-shell-book/752ffde2badb9b2291af79208fa2f85d958ca9b1/zh/chapters/pic/tinylab.org-sina-weibo.jpg -------------------------------------------------------------------------------- /zh/chapters/pic/tinylab.org-weixin-logo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tinyclub/open-shell-book/752ffde2badb9b2291af79208fa2f85d958ca9b1/zh/chapters/pic/tinylab.org-weixin-logo.jpg -------------------------------------------------------------------------------- /zh/preface/01-chapter0.markdown: -------------------------------------------------------------------------------- 1 | # 版本修订历史 2 | 3 | |Revision | Author | From | Date | Description | 4 | |---------|------------------|--------------------|------------|---------------------| 5 | | 0.3 |[@吴章金falcon][1]|[@泰晓科技][2] | 2016/08/30 | 增加一章: 用户管理 | 6 | | 0.2 |[@吴章金falcon][1]|[@泰晓科技][2] | 2015/07/23 | 调整格式,修复链接 | 7 | | 0.1 |[@吴章金falcon][1]|[@泰晓科技][2] | 2014/01/07 | 初稿 | 8 | 9 | [1]: https://www.zhihu.com/people/wuzhangjin 10 | [2]: http://tinylaborg 11 | -------------------------------------------------------------------------------- /zh/preface/01-chapter1.markdown: -------------------------------------------------------------------------------- 1 | **关注作者公众号**: 2 |
3 | 4 |
5 | 6 | # 前言 7 | 8 | - [背景](#toc_18682_17841_1) 9 | - [现状](#toc_18682_17841_2) 10 | - [计划](#toc_18682_17841_3) 11 | 12 | 13 | 14 | ## 背景 15 | 16 | 早在 2007 年 11 月,为了系统地学习和总结 Shell 编程,作者专门制定了一个 Shell 编程范例的总结计划,当时的计划是: 17 | 18 | > 这个系列将以面向“对象”(即我们操作的对象)来展开,并引入大量的实例,这样有助于让我们真正去学以致用,并在用的过程中提高兴趣。所以这个系列将不会专门介绍 Shell 的语法, 而是假设读者对 Shell 编程有了一定的基础。 19 | > 20 | > 另外,该系列到最后可能会涵盖:数值、逻辑值、字符串、文件、进程、文件系统等所有我们可以操作的“对象”,这个操作对象也将从低级到高级,进而上升到网络层面,整个通过各种方式连接起来的计算机的集合。实际上这也未尝不是在摸索 UNIX 的哲学,那"K.I.S.S"(Keep It Simple, Stupid)蕴藏的巨大能量。 21 | 22 | > —— 摘自《兰大开源社区 >> 脚本编程 >> Shell 编程范例》 23 | 24 | 2008 年 4 月底,整个系列大部分内容和框架基本完成,后来由于实习和工作原因,并没有持续完善。不过相关章节却获得了较好的反响,很多热心网友有大量评论和转载,例如,在百度文库转载的一份《Shell编程范例之字符串操作》的访问量已接近 3000。说明整个系列还是有比较大的阅读群体。 25 | 26 | 27 | ## 现状 28 | 29 | 考虑到整个 Linux 世界的蓬勃发展,Shell 的使用环境越来越多,相关使用群体会不断增加,所以最近已经将该系列重新整理,并以自由书籍的方式发布,以便惠及更多的读者。 30 | 31 | 整个系列已经用 [Markdown](http://www.tinylab.org/start-posting-with-markdown/) 重新组织,并发布到了 [泰晓科技|TinyLab.org](http://tinylab.org)。 32 | 33 | 整理到[TinyLab.org](http://tinylab.org)的索引篇是:[《Shell编程范例之索引篇》](http://www.tinylab.org/shell-programming-paradigm-series-index-review/),其内容结构如下: 34 | 35 | - [Shell编程范例之开篇](http://www.tinylab.org/shell-programming-paradigm-begins-with/) (更新时间:2007-07-21) 36 | - [Shell编程范例之数值运算](http://www.tinylab.org/shell-numeric-calculation/) (更新时间:2007-11-9) 37 | - [Shell编程范例之布尔运算](http://www.tinylab.org/shell-programming-paradigm-of-boolean-operations/) (更新时间:2007-10-30) 38 | - [Shell编程范例之字符串操作](http://www.tinylab.org/shell-programming-paradigm-of-string-manipulation/) (更新时间:2007-11-21) 39 | - [Shell编程范例之文件操作](http://www.tinylab.org/shell-programming-paradigms-of-file-operations/) (更新时间:2007-12-5) 40 | - [Shell编程范例之文件系统操作](http://www.tinylab.org/shell-programming-paradigm-in-file-system-operations/) (更新时间:2007-12-29) 41 | - [Shell编程范例之进程操作](http://www.tinylab.org/shell-programming-paradigm-of-process-operations/) (更新时间:2008-02-22) 42 | - [Shell编程范例之网络操作](http://www.tinylab.org/shell-programming-paradigm-of-network-operations/) (更新时间:2008-04-19) 43 | - [Shell编程范例之总结篇](http://www.tinylab.org/summary-of-shell-programming-paradigm-article/) (更新时间:2008-07-21)
44 | 45 | 最近,基于一个 Markdown 的[开源书籍模版](http://tinylab.org/docker-quick-start-docker-gitbook-writing-a-book/):Gitbook,已经把该系列整理成了自由书籍,并维护在 TinyLab 的[项目仓库](https://github.com/tinyclub/open-shell-book)中。项目相关信息如下: 46 | 47 | - 项目首页: 48 | - 代码仓库:[https://github.com/tinyclub/open-shell-book.git](https://github.com/tinyclub/open-shell-book) 49 | 50 | 51 | ## 计划 52 | 53 | 后续除了继续在 [泰晓科技|TinyLab.org](http://tinylab.org) 以 Blog 形式持续更新外,还打算重新规划、增补整个系列,并以自由书籍的方式持续维护,并通过 [TinLab.org](http://tinylab.org) 平台接受读者的反馈,直到正式发行出版。 54 | 55 | 欢迎大家指出本书初稿中的不足,甚至参与到相关章节的写作、校订和完善中来。 56 | 57 | 如果有时间和兴趣,欢迎参与。可以通过 [泰晓科技](http://www.tinylab.org/about/) 联系我们,或者直接联系微信号 tinylab。 58 | -------------------------------------------------------------------------------- /zh/preface/pic/Linux_FileSystem_Architecture-150x150.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tinyclub/open-shell-book/752ffde2badb9b2291af79208fa2f85d958ca9b1/zh/preface/pic/Linux_FileSystem_Architecture-150x150.jpg -------------------------------------------------------------------------------- /zh/preface/pic/Linux_FileSystem_Architecture-197x300.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tinyclub/open-shell-book/752ffde2badb9b2291af79208fa2f85d958ca9b1/zh/preface/pic/Linux_FileSystem_Architecture-197x300.jpg -------------------------------------------------------------------------------- /zh/preface/pic/Linux_FileSystem_Architecture.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tinyclub/open-shell-book/752ffde2badb9b2291af79208fa2f85d958ca9b1/zh/preface/pic/Linux_FileSystem_Architecture.jpg -------------------------------------------------------------------------------- /zh/preface/pic/MBR_Architecture-150x150.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tinyclub/open-shell-book/752ffde2badb9b2291af79208fa2f85d958ca9b1/zh/preface/pic/MBR_Architecture-150x150.jpg -------------------------------------------------------------------------------- /zh/preface/pic/MBR_Architecture-300x139.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tinyclub/open-shell-book/752ffde2badb9b2291af79208fa2f85d958ca9b1/zh/preface/pic/MBR_Architecture-300x139.jpg -------------------------------------------------------------------------------- /zh/preface/pic/MBR_Architecture.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tinyclub/open-shell-book/752ffde2badb9b2291af79208fa2f85d958ca9b1/zh/preface/pic/MBR_Architecture.jpg -------------------------------------------------------------------------------- /zh/preface/pic/Network_Architecture-150x150.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tinyclub/open-shell-book/752ffde2badb9b2291af79208fa2f85d958ca9b1/zh/preface/pic/Network_Architecture-150x150.jpg -------------------------------------------------------------------------------- /zh/preface/pic/Network_Architecture-300x176.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tinyclub/open-shell-book/752ffde2badb9b2291af79208fa2f85d958ca9b1/zh/preface/pic/Network_Architecture-300x176.jpg -------------------------------------------------------------------------------- /zh/preface/pic/Network_Architecture.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tinyclub/open-shell-book/752ffde2badb9b2291af79208fa2f85d958ca9b1/zh/preface/pic/Network_Architecture.jpg -------------------------------------------------------------------------------- /zh/preface/pic/Network_Layer_Compare-150x150.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tinyclub/open-shell-book/752ffde2badb9b2291af79208fa2f85d958ca9b1/zh/preface/pic/Network_Layer_Compare-150x150.jpg -------------------------------------------------------------------------------- /zh/preface/pic/Network_Layer_Compare-300x118.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tinyclub/open-shell-book/752ffde2badb9b2291af79208fa2f85d958ca9b1/zh/preface/pic/Network_Layer_Compare-300x118.jpg -------------------------------------------------------------------------------- /zh/preface/pic/Network_Layer_Compare.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tinyclub/open-shell-book/752ffde2badb9b2291af79208fa2f85d958ca9b1/zh/preface/pic/Network_Layer_Compare.jpg -------------------------------------------------------------------------------- /zh/preface/pic/Network_Layer_OSI-150x150.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tinyclub/open-shell-book/752ffde2badb9b2291af79208fa2f85d958ca9b1/zh/preface/pic/Network_Layer_OSI-150x150.jpg -------------------------------------------------------------------------------- /zh/preface/pic/Network_Layer_OSI-300x221.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tinyclub/open-shell-book/752ffde2badb9b2291af79208fa2f85d958ca9b1/zh/preface/pic/Network_Layer_OSI-300x221.jpg -------------------------------------------------------------------------------- /zh/preface/pic/Network_Layer_OSI.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tinyclub/open-shell-book/752ffde2badb9b2291af79208fa2f85d958ca9b1/zh/preface/pic/Network_Layer_OSI.jpg -------------------------------------------------------------------------------- /zh/preface/pic/Network_Layer_TCP_IP-118x300.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tinyclub/open-shell-book/752ffde2badb9b2291af79208fa2f85d958ca9b1/zh/preface/pic/Network_Layer_TCP_IP-118x300.jpg -------------------------------------------------------------------------------- /zh/preface/pic/Network_Layer_TCP_IP-150x150.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tinyclub/open-shell-book/752ffde2badb9b2291af79208fa2f85d958ca9b1/zh/preface/pic/Network_Layer_TCP_IP-150x150.jpg -------------------------------------------------------------------------------- /zh/preface/pic/Network_Layer_TCP_IP.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tinyclub/open-shell-book/752ffde2badb9b2291af79208fa2f85d958ca9b1/zh/preface/pic/Network_Layer_TCP_IP.jpg -------------------------------------------------------------------------------- /zh/preface/pic/UI_Shell_and_GUI-150x150.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tinyclub/open-shell-book/752ffde2badb9b2291af79208fa2f85d958ca9b1/zh/preface/pic/UI_Shell_and_GUI-150x150.jpg -------------------------------------------------------------------------------- /zh/preface/pic/UI_Shell_and_GUI-300x190.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tinyclub/open-shell-book/752ffde2badb9b2291af79208fa2f85d958ca9b1/zh/preface/pic/UI_Shell_and_GUI-300x190.jpg -------------------------------------------------------------------------------- /zh/preface/pic/UI_Shell_and_GUI.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tinyclub/open-shell-book/752ffde2badb9b2291af79208fa2f85d958ca9b1/zh/preface/pic/UI_Shell_and_GUI.jpg -------------------------------------------------------------------------------- /zh/preface/pic/cover-tinylab.org.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tinyclub/open-shell-book/752ffde2badb9b2291af79208fa2f85d958ca9b1/zh/preface/pic/cover-tinylab.org.png -------------------------------------------------------------------------------- /zh/preface/pic/cover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tinyclub/open-shell-book/752ffde2badb9b2291af79208fa2f85d958ca9b1/zh/preface/pic/cover.png -------------------------------------------------------------------------------- /zh/preface/pic/mommy-tea.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tinyclub/open-shell-book/752ffde2badb9b2291af79208fa2f85d958ca9b1/zh/preface/pic/mommy-tea.png -------------------------------------------------------------------------------- /zh/preface/pic/tinylab-targets-150x110.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tinyclub/open-shell-book/752ffde2badb9b2291af79208fa2f85d958ca9b1/zh/preface/pic/tinylab-targets-150x110.gif -------------------------------------------------------------------------------- /zh/preface/pic/tinylab-targets-150x110.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tinyclub/open-shell-book/752ffde2badb9b2291af79208fa2f85d958ca9b1/zh/preface/pic/tinylab-targets-150x110.jpg -------------------------------------------------------------------------------- /zh/preface/pic/tinylab-targets-300x54.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tinyclub/open-shell-book/752ffde2badb9b2291af79208fa2f85d958ca9b1/zh/preface/pic/tinylab-targets-300x54.gif -------------------------------------------------------------------------------- /zh/preface/pic/tinylab-targets-300x54.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tinyclub/open-shell-book/752ffde2badb9b2291af79208fa2f85d958ca9b1/zh/preface/pic/tinylab-targets-300x54.jpg -------------------------------------------------------------------------------- /zh/preface/pic/tinylab-targets.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tinyclub/open-shell-book/752ffde2badb9b2291af79208fa2f85d958ca9b1/zh/preface/pic/tinylab-targets.jpg -------------------------------------------------------------------------------- /zh/preface/pic/tinylab.org-sina-weibo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tinyclub/open-shell-book/752ffde2badb9b2291af79208fa2f85d958ca9b1/zh/preface/pic/tinylab.org-sina-weibo.jpg -------------------------------------------------------------------------------- /zh/preface/pic/tinylab.org-weixin-logo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tinyclub/open-shell-book/752ffde2badb9b2291af79208fa2f85d958ca9b1/zh/preface/pic/tinylab.org-weixin-logo.jpg --------------------------------------------------------------------------------