├── .github ├── FUNDING.yml └── workflows │ └── ci.yml ├── .gitignore ├── LICENSE ├── README.md ├── docs └── shell-regex.md ├── example ├── demo1 ├── demo10 ├── demo11 ├── demo12 ├── demo13 ├── demo14 ├── demo15 ├── demo16 ├── demo17 ├── demo18 ├── demo19 ├── demo2 ├── demo20 ├── demo21 ├── demo22 ├── demo23 ├── demo24 ├── demo25 ├── demo26 ├── demo27 ├── demo28 ├── demo29 ├── demo3 ├── demo30 ├── demo31 ├── demo32 ├── demo33 ├── demo34 ├── demo4 ├── demo5 ├── demo6 ├── demo7 ├── demo8 └── demo9 ├── idoc.yml ├── logo.svg └── package.json /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | ko_fi: jaywcjlove 2 | buy_me_a_coffee: jaywcjlove 3 | custom: ["https://www.paypal.me/kennyiseeyou", "https://jaywcjlove.github.io/#/sponsor"] 4 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: 3 | push: 4 | branches: 5 | - master 6 | 7 | jobs: 8 | build-deploy: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v4 12 | - uses: actions/setup-node@v4 13 | with: 14 | node-version: 20 15 | registry-url: 'https://registry.npmjs.org' 16 | 17 | - run: npm install 18 | - run: npm run build 19 | 20 | - name: Generate Contributors Images 21 | uses: jaywcjlove/github-action-contributors@main 22 | with: 23 | filter-author: (renovate\[bot\]|renovate-bot|dependabot\[bot\]) 24 | output: dist/CONTRIBUTORS.svg 25 | avatarSize: 42 26 | 27 | - name: Create Tag 28 | id: create_tag 29 | uses: jaywcjlove/create-tag-action@main 30 | with: 31 | package-path: ./package.json 32 | 33 | - name: Deploy Website 34 | uses: peaceiris/actions-gh-pages@v4 35 | with: 36 | user_name: 'github-actions[bot]' 37 | user_email: 'github-actions[bot]@users.noreply.github.com' 38 | commit_message: ${{ github.event.head_commit.message }} 39 | github_token: ${{ secrets.GITHUB_TOKEN }} 40 | publish_dir: ./dist 41 | 42 | - name: Generate Changelog 43 | id: changelog 44 | uses: jaywcjlove/changelog-generator@main 45 | with: 46 | token: ${{ secrets.GITHUB_TOKEN }} 47 | filter-author: (jaywcjlove|小弟调调™|dependabot\[bot\]|Renovate Bot) 48 | filter: (^[\s]+?[R|r]elease)|(^[R|r]elease) 49 | 50 | - name: Create Release 51 | uses: ncipollo/release-action@v1 52 | if: steps.create_tag.outputs.successful 53 | with: 54 | allowUpdates: true 55 | token: ${{ secrets.GITHUB_TOKEN }} 56 | name: ${{ steps.create_tag.outputs.version }} 57 | tag: ${{ steps.create_tag.outputs.version }} 58 | body: | 59 | [![Buy me a coffee](https://img.shields.io/badge/Buy%20me%20a%20coffee-048754?logo=buymeacoffee)](https://jaywcjlove.github.io/#/sponsor) 60 | 61 | Documentation ${{ steps.changelog.outputs.tag }}: https://raw.githack.com/jaywcjlove/vim-web/${{ steps.changelog.outputs.gh-pages-short-hash }}/index.html 62 | Comparing Changes: ${{ steps.changelog.outputs.compareurl }} 63 | 64 | ${{ steps.changelog.outputs.changelog }} -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | dist 2 | node_modules 3 | test/out 4 | 5 | npm-debug.log* 6 | lerna-debug.log 7 | yarn-error.log 8 | package-lock.json 9 | 10 | .DS_Store 11 | .cache 12 | .vscode 13 | .idea 14 | 15 | *.bak 16 | *.tem 17 | *.temp 18 | #.swp 19 | *.*~ 20 | ~*.* 21 | 22 | # IDEA 23 | *.iml 24 | *.ipr 25 | *.iws 26 | .idea/ -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 小弟调调™ 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | Shell Tutorial 3 | === 4 | 5 | 6 | [![Buy me a coffee](https://img.shields.io/badge/Buy%20me%20a%20coffee-048754?logo=buymeacoffee)](https://jaywcjlove.github.io/#/sponsor) 7 | [![CI](https://github.com/jaywcjlove/shell-tutorial/actions/workflows/ci.yml/badge.svg)](https://github.com/jaywcjlove/shell-tutorial/actions/workflows/ci.yml) 8 | 9 | 最近需要写一个脚本管理服务,花了两天时间学习了一下shell脚本,记录我的学习笔记。 10 | 11 | 12 | 13 | ## 目录 14 | 15 | - [脚本运行](#脚本运行) 16 | - [特殊字符](#特殊字符) 17 | - [注释](#注释) 18 | - [命令分割符](#命令分割符) 19 | - [结束符](#结束符) 20 | - [句号/圆点](#句号圆点) 21 | - [引号](#引号) 22 | - [命令替换](#命令替换) 23 | - [操作符](#操作符) 24 | - [赋值](#赋值) 25 | - [计算操作符](#计算操作符) 26 | - [位操作符](#位操作符) 27 | - [逗号操作符](#逗号操作符) 28 | - [逻辑操作符](#逻辑操作符) 29 | - [变量](#变量) 30 | - [变量值](#变量值) 31 | - [定义变量](#定义变量) 32 | - [只读变量](#只读变量) 33 | - [使用变量](#使用变量) 34 | - [删除变量unset](#删除变量unset) 35 | - [变量类型](#变量类型) 36 | - [内部变量](#内部变量) 37 | - [位置参数](#位置参数) 38 | - [参数替换](#参数替换) 39 | - [declare/typeset](#declaretypeset) 40 | - [变量间接引用](#变量间接引用) 41 | - [$RANDOM](#$RANDOM) 42 | - [双括号结构](#双括号结构) 43 | - [转义字符](#转义字符) 44 | - [测试](#测试) 45 | - [测试结构](#测试结构) 46 | - [文件测试操作符](#文件测试操作符) 47 | - [比较操作符](#比较操作符) 48 | - [操作字符串](#操作字符串) 49 | - [for/while](#forwhile) 50 | - [for](#for) 51 | - [while](#while) 52 | - [until](#until) 53 | - [嵌套循环](#嵌套循环) 54 | - [循环控制](#循环控制) 55 | - [case/select](#caseselect) 56 | - [case](#case) 57 | - [select](#select) 58 | - [函数](#函数) 59 | - [局部变量](#局部变量) 60 | - [函数参数](#函数参数) 61 | - [函数返回值](#函数返回值) 62 | 63 | 64 | 65 | ## 脚本运行 66 | 67 | 建立一个文本文件[demo1](./example/demo1),以`.sh`结尾的或者不需要后缀都可以,下面是demo1的内容: 68 | 69 | ```shell 70 | #!/bin/bash 71 | NUM=10 72 | printf "输出数字$NUM\n" 73 | echo $NUM 74 | ``` 75 | 76 | 直接运行是会报错误的,因为没有权限执行(Permission denied),需要给权限。下面方式 77 | 78 | ```bash 79 | chmod +x ./demo1 # 使每个人都有执行的权限 80 | chmod +rx ./demo1 # 使每个人都有读和执行的权限 81 | chmod u+rx ./demo1 # 仅仅使脚本文件拥有者有读和执行的权限 82 | chmod u+x ./demo1 # 只有自己可以执行,其它人不能执行 83 | chmod ug+x ./demo1 # 只有自己以及同一群可以执行,其它人不能执行 84 | chmod 555 ./demo1 # 使每个人都有读和执行的权限 85 | chmod 777 ./demo1 86 | ``` 87 | 88 | 当前目录下运行 [demo1](./example/demo1) 脚本 89 | 90 | ```bash 91 | ./demo1 92 | # 输出结果===> 93 | 输出数字10 94 | 10 95 | ``` 96 | 97 | 直接运行demo1脚本,这个方法是把脚本给系统中所有其他的用户使用,这个时候需要你将demo1脚本移动到目录`/usr/local/bin` 中(必须要有root的权限),移动到 bin 目录之后,你只需使用 `demo1` 加回车就能执行脚本了。 98 | 99 | ```bash 100 | demo1 101 | 102 | # 输出结果===> 103 | 输出数字10 104 | 10 105 | ``` 106 | 107 | 通过sh或者bash命令运行脚本,`sh scriptname` 运行一个Bash脚本将会禁止所有Bash的扩展特性。 108 | 109 | ```bash 110 | # 你能够运行它用命令 111 | sh demo1 112 | 113 | # 另外也也可以用bash来执行 114 | bash demo1 115 | ``` 116 | 117 | 脚本以"#!"行开头,行将会命令解释器(sh或是bash)。`#!/bin/rm` 当你运行这个脚本时,除了这个脚本消失了之外,你不会发现更多其他的东西。 118 | 119 | ## 特殊字符 120 | 121 | ### 注释 122 | 123 | 以`#`开头的行就是注释,会被解释器忽略。注释行前面也可以有空白字符。 124 | 125 | ```shell 126 | #-------------------------------------------- 127 | # 这是一个注释 128 | # author:作者 129 | # site:https://github.com/jaywcjlove 130 | #-------------------------------------------- 131 | 132 | echo "A comment will follow." # 这里可以添加注释. 133 | # ^ 注意在#前面可以有空白符 134 | ``` 135 | 136 | echo命令给出的一个转义的#字符并不会开始一个注释。同样地,出现在一些参数代换结构和在数值常量表达式中的#字符也同样不会开始一个注释。 137 | 138 | ```shell 139 | echo "这里的 # 不会被注释" 140 | echo '这里的 # 不会被注释' 141 | echo 这里的 \# 不会被注释 142 | echo 这里的 # 会被注释 143 | 144 | echo ${PATH#*:} # 前面的#是参数代换,不是注释. 145 | echo $(( 2#101011 )) # 基本转换,不是注释. 146 | ``` 147 | 148 | ### 命令分割符 149 | 150 | 分号`;`命令分割符,分割符允许在同一行里有两个或更多的命令。执行[demo2](./example/demo2),会将 demo2 拷贝输出 demo2.bak 。 151 | 152 | ```shell 153 | echo hello; echo there # 输出 hello 和 there 154 | filename='demo2' # 变量 155 | if [ -x "$filename" ]; then # 注意:"if" and "then"需要分隔符 156 | # 思考一下这是为什么? 157 | echo "File $filename exists."; cp $filename $filename.bak 158 | else 159 | echo "File $filename not found."; touch $filename 160 | fi; echo "File test complete." 161 | ``` 162 | 163 | ### 结束符 164 | 165 | 双分号`;;`,case语句分支的结束符。[demo3](./example/demo3) 166 | 167 | ```shell 168 | read Keypress 169 | case "$Keypress" in 170 | [[:lower:]] ) echo "Lowercase letter";; 171 | [[:upper:]] ) echo "Uppercase letter";; 172 | [0-9] ) echo "Digit";; 173 | * ) echo "Punctuation, whitespace, or other";; 174 | esac # 允许字符串的范围出现在[]中, 175 | #+ 或者POSIX风格的[[中. 176 | exit 0 177 | ``` 178 | 179 | ### 句号/圆点 180 | 181 | 作为一个文件名的组成部分`.`,当点`.`以一个文件名为前缀时,起作用使该文件变成了隐藏文件。这种隐藏文件ls一般是不会显示出来的。 182 | 183 | 作为目录名时,单个点(.)表示当前目录,两个点(..)表示上一级目录(或称为父目录)。 184 | 185 | 点(.)字符匹配。作为正则表达式的一部分,匹配字符时,单点(.)表示匹配任意一个字符。 186 | 187 | ### 引号 188 | 189 | 引号一个很重要的作用是保护命令行上的一个参数不被shell解释,而把此参数传递给要执行的程序来处理它。 190 | 191 | ```bash 192 | bash$ grep '[Ff]ile' demo* 193 | demo2:filename='demo2' 194 | demo2:if [ -x "$filename" ]; then # 注意:"if" and "then"需要分隔符 195 | demo2: echo "File $filename exists."; cp $filename $filename.bak 196 | ``` 197 | 198 | 引号能改掉echo's不换行的“习惯”。 199 | 200 | ```bash 201 | bash$ echo $(ls -al) 202 | total 24 drwxr-xr-x 5 kacperwang staff 170 1 22 16:47 . drwxr-xr-x 5 kacperwang staff 170 1 22 13:29 .. -rwxr-xr-x 1 kacperwang staff 58 1 22 16:20 demo1 -rwxr-xr-x 1 kacperwang staff 325 1 22 16:39 demo2 -rwxr-xr-x 1 kacperwang staff 899 1 22 17:16 demo3 203 | 204 | bash$ echo "$(ls -al)" 205 | total 24 206 | drwxr-xr-x 5 kacperwang staff 170 1 22 16:47 . 207 | drwxr-xr-x 5 kacperwang staff 170 1 22 13:29 .. 208 | -rwxr-xr-x 1 kacperwang staff 58 1 22 16:20 demo1 209 | -rwxr-xr-x 1 kacperwang staff 325 1 22 16:39 demo2 210 | -rwxr-xr-x 1 kacperwang staff 899 1 22 17:16 demo3 211 | ``` 212 | 213 | **单引号** 214 | 215 | ```shell 216 | str='this is a string' 217 | ``` 218 | 219 | 单引号里的任何字符都会原样输出,单引号字符串中的变量是无效的 220 | 单引号字串中不能出现单引号(对单引号使用转义符后也不行) 221 | 222 | **双引号** 223 | 224 | ```shell 225 | your_name='qinjx' 226 | str="Hello, I know your are \"$your_name\"! \n" 227 | ``` 228 | 229 | 双引号里可以有变量 230 | 双引号里可以出现转义字符 231 | 232 | ### 命令替换 233 | 234 | 命令替换"`",将会重新分配一个命令甚至是多个命令的输出;它会将命令的输出如实地添加到另一个上下文中。[demo4](./example/demo4) 235 | 236 | ```shell 237 | script_name=`basename $0` 238 | echo "The name of this script is $script_name." 239 | 240 | textfile_listing=`ls *` 241 | # 变量中包含了当前工作目录下所有的*文件 242 | echo $textfile_listing 243 | ``` 244 | 245 | 通过这个符号,批量删除文件 246 | 247 | ```shell 248 | rm `cat filename` # "filename" 包含了需要被删除的文件列表 249 | # 可能会产生"参数列表太长"的错误 250 | # 更好的方法是 xargs rm -- < filename 251 | # ( -- 同时覆盖了那些以"-"开头的文件所产生的特殊情况 ) 252 | ``` 253 | 254 | ## 操作符 255 | 256 | ### 赋值 257 | 258 | 变量赋值,初始化或改变一个变量的值,通用的变量赋值操作符,可以用于数值和字符串的赋值 259 | 260 | ```shell 261 | var=27 262 | category=minerals # "="字符后面不能加空白字符. 263 | ``` 264 | 265 | 不要把"="赋值操作符和`=`测试操作符搞混了。 266 | 267 | ```shell 268 | # = 用于测试操作符 269 | if [ "$string1" = "$string2" ] 270 | # if [ "X$string1" = "X$string2" ] 会更安全, 271 | # 它为了防止其中有一个字符串为空时产生错误信息. 272 | # (增加的"X"字符可以互相抵消.) 273 | then 274 | command 275 | fi 276 | ``` 277 | 278 | ### 计算操作符 279 | 280 | | 操作符 | 描述 | 操作符 | 描述 | 操作符 | 描述 | 281 | | ---- | ---- | ---- | ---- |---- | ---- | 282 | | `+` | 加 | `/` | 除 | `**` | 求幂 | 283 | | `-` | 减 | `*` | 乘 | `%` | 求模[demo6](./example/demo6) | 284 | 285 | ```shell 286 | # Bash在版本2.02引入了"**"求幂操作符. 287 | let "z=5**3" 288 | echo "z = $z" # z = 125 289 | 290 | # 求模(它返回整数整除一个数后的余数) 291 | let "y=5 % 3" 292 | echo "y = $y" # y = 2 293 | ``` 294 | 295 | | 操作符 | 描述 | 296 | | ---- | ---- | 297 | | `+=` | 加等(plus-equal) 把原变量值增加一个常量并重新赋值给变量 | 298 | | `-=` | 减等(minus-equal) 把原变量值减少一个常量并重新赋值给变量 | 299 | | `*=` | 乘等(times-equal) 把原变量值乘上一个常量并重新赋值给变量 | 300 | | `/=` | 除等(slash-equal) 把原变量值除以一个常量并重新赋值给变量 | 301 | | `%=` | 模等(mod-equal) 把原变量值除以一个常量整除(即取模)并重新赋余数的值给变量 | 302 | 303 | ```shell 304 | let "var += 5" # 会使变量var值加了5并把值赋给var. 305 | let "var *= 4" # 使变量var的值乘上4并把值赋给var. 306 | ``` 307 | 308 | 例子: 309 | - [用10种不同的方法计数到11](./example/demo8) 310 | - [最大公约数](./example/demo8) 311 | - [整数变量符号的长整数测试](./example/demo9),到2.05b版本为止,Bash支持64位的整数,以前的版本会有长度溢出错误。 312 | - [Bash不能处理浮点计算,它会把含有小数点的数当成字符串。](./example/demo10) 313 | 314 | 315 | ### 位操作符 316 | 317 | 位操作符很少在脚本中使用。他们主要用于操作和测试从端口或sockets中读到的数据。“位运算”更多地用于编译型的语言,比如说C和C++,它们运行起来快地像飞。 318 | 319 | | 操作符 | 描述 | 操作符 | 描述 | 320 | | ---- | ---- | ---- | ---- | 321 | | `<<` | 位左移(每移一位相当乘以2) | `|` | 位或 | 322 | | `<<=` | 位左移赋值 | `|=` | 位或赋值 | 323 | | `>>` | 位右移(每移一位相当除以2) | `~` | 位反 | 324 | | `>>=` | "位右移赋值"(和<<=相反) | `!` | 位非 | 325 | | `&` | 位与 | `^` | 位或 | 326 | | `&=` | 位于赋值 | `^=` | 位或赋值 | 327 | 328 | 329 | ```shell 330 | # <<= 331 | # "位左移赋值" 332 | let "var <<= 2" 结果使var的二进制值左移了二位(相当于乘以4) 333 | ``` 334 | 335 | ### 逻辑操作符 336 | 337 | 逻辑与`&&` 338 | 339 | ```shell 340 | if [ $condition1 ] && [ $condition2 ] 341 | # 等同于: if [ $condition1 -a $condition2 ] 342 | # 如果condition1和condition2都为真则返回真... 343 | fi; 344 | 345 | if [[ $condition1 && $condition2 ]] # Also works. 346 | # 注意&&操作不能在[ ... ]结构中使用. 347 | fi; 348 | ``` 349 | 350 | 逻辑或`||` 351 | 352 | ```shell 353 | if [ $condition1 ] || [ $condition2 ] 354 | # 等同于: if [ $condition1 -o $condition2 ] 355 | # 如果condition1和condition2有一个为真则返回真... 356 | fi; 357 | if [[ $condition1 || $condition2 ]] # Also works. 358 | # 注意||操作不能在[ ... ]结构中使用. 359 | fi; 360 | ``` 361 | 362 | 使用&&和||进行混合条件测试[demo11](./example/demo11)。在算术计算的环境中,&&和||操作符也可以使用。 363 | 364 | ``` 365 | bash$ echo $(( 1 && 2 )) $((3 && 0)) $((4 || 0)) $((0 || 0)) 366 | 1 0 1 0 367 | ``` 368 | 369 | ### 逗号操作符 370 | 371 | 逗号`,`操作符连接两个或更多的算术操作。所有的操作都被求值(可能会有副作用),但只返回最后一个操作的结构。[demo5](./example/demo5) 372 | 373 | ```shell 374 | let "t1 = ((5 + 3, 7 - 1, 15 - 4))" 375 | echo "t1 = $t1" # t1 = 11 376 | 377 | let "t2 = ((a = 9, 15 / 3))" # 初始化"a"并求"t2"的值. 378 | echo "t2 = $t2 a = $a" # t2 = 5 a = 9 379 | ``` 380 | 381 | ## 变量 382 | 383 | 变量,是脚本编程中的如何进行数据表现的办法。它们可以在算术计算中作为操作数,在一个字符串表达式中作为符号表达抽象的意义或是其他的其它意义。变量是表示计算机内存中保存一种数据需要占的一个位置或一组的位置的标识。 384 | 385 | ### 变量值 386 | 387 | 如果variable1是一个变量的名字,那么$variable1就是引用这个变量的值――即这个变量它包含的数据。[变量赋值与替换例子](./example/demo12) 388 | 389 | ```shell 390 | t1=12 391 | echo $t1 392 | ``` 393 | 394 | 一个未初始化的变量有一个”null”值――表示从没有被赋值过(注意null值不等于零)。在一个变量从未赋值之前就使用它通常会引起问题。然而,仍然有可能在执行算术计算时使用一个未初始化的变量。 395 | 396 | ```shell 397 | echo "$uninitialized" # (blank line) 398 | let "uninitialized += 5" # Add 5 to it. 399 | echo "$uninitialized" # 5 400 | 401 | # 结论: 402 | # 一个未初始化的变量没有值, 403 | #+ 但是似乎它在算术计算中的值是零。 404 | # 这个无法证实(也可能是不可移植)的行为。 405 | ``` 406 | 407 | ### 定义变量 408 | 409 | ```shell 410 | # 变量名不加美元符号 411 | your_var="elaine" 412 | # 重新定义 413 | your_var="newname" 414 | ``` 415 | 416 | **注意⚠️** 417 | 418 | 1. 首个字符必须为字母(a-z,A-Z)。 419 | 1. 中间不能有空格,可以使用下划线(_),等号左右也不能有空格。 420 | 1. 不能使用标点符号。 421 | 1. 不能使用bash里的关键字(可用help命令查看保留关键字)。 422 | 423 | ### 只读变量 424 | 425 | ```shell 426 | #!/bin/bash 427 | github="https://jaywcjlove.github.io" 428 | readonly github 429 | github="https://www.github.com" 430 | 431 | # 运行脚本,结果如下: 432 | /bin/sh: NAME: This variable is read only. 433 | ``` 434 | 435 | ### 使用变量 436 | 437 | ```shell 438 | your_var="github" 439 | echo $your_var 440 | echo ${your_var} 441 | echo "your name is ${your_var}-l" 442 | ``` 443 | 444 | ### 删除变量unset 445 | 446 | 变量被删除后不能再次使用。unset 命令不能删除只读变量。 447 | 448 | ```shell 449 | myUrl="https://jaywcjlove.github.io" 450 | unset myUrl 451 | echo $myUrl 452 | ``` 453 | 454 | ### 变量类型 455 | 456 | 不同与许多其他的编程语言,Bash不以"类型"来区分变量。本质上来说,Bash变量是字符串,但是根据环境的不同,Bash允许变量有整数计算和比较。其中的决定因素是变量的值是不是只含有数字。 457 | 458 | 1. **局部变量** 局部变量在脚本或命令中定义,仅在当前shell实例中有效,其他shell启动的程序不能访问局部变量。 459 | 2. **环境变量** 所有的程序,包括shell启动的程序,都能访问环境变量,有些程序需要环境变量来保证其正常运行。必要的时候shell脚本也可以定义环境变量。 460 | 3. **shell变量** shell变量是由shell程序设置的特殊变量。shell变量中有一部分是环境变量,有一部分是局部变量,这些变量保证了shell的正常运行 461 | 462 | ### 内部变量 463 | 464 | | 内部变量 | 说明 | 465 | | ---- | ---- | 466 | | $BASH | Bash二进制程序文件的路径 | 467 | | $BASH_ENV | 该环境变量保存一个Bash启动文件路径,当启动一个脚本程序时会去读该环境变量指定的文件。 | 468 | | $BASH_SUBSHELL | 一个指示子shell(subshell)等级的变量。它是Bash版本3新加入的。 | 469 | | $BASH_VERSINFO[n] | 这个数组含有6个元素,指示了安装的Bash版本的信息。它和$BASH_VERSION相似,但它们还是有一些小小的不同。| 470 | | $BASH_VERSION | 安装在系统里的Bash版本。| 471 | | $DIRSTACK | 在目录堆栈里面最顶端的值(它受pushd和popd的控制) | 472 | | $EDITOR | 由脚本调用的默认的编辑器,一般是vi或是emacs。 | 473 | | $EUID | 有效用户ID | 474 | | $FUNCNAME | 当前函数的名字 | 475 | | $GLOBIGNORE | 由通配符(globbing)扩展的一列文件名模式。| 476 | | $GROUPS | 目前用户所属的组| 477 | | $HOME | 用户的家目录,通常是/home/username | 478 | | $HOSTNAME | 在系统启动时由一个初始化脚本中用hostname命令给系统指派一个名字。然而,gethostname()函数能设置Bash内部变量E$HOSTNAME。| 479 | | $HOSTTYPE | 机器类型,像$MACHTYPE一样标识系统硬件。| 480 | | $IFS | 内部字段分隔符 | 481 | | $IGNOREEOF | 忽略EOF:在退出控制台前有多少文件结尾标识(end-of-files,control-D)会被shell忽略。| 482 | | $LC_COLLATE | 它常常在.bashrc或/etc/profile文件里被设置,它控制文件名扩展和模式匹配的展开顺序。| 483 | | $LINENO | 这个变量表示在本shell脚本中该变量出现时所在的行数。它只在脚本中它出现时有意义,它一般可用于调试。| 484 | | $MACHTYPE | 机器类型,识别系统的硬件类型。| 485 | | $OLDPWD | 上一次工作的目录("OLD-print-working-directory",你上一次进入工作的目录)| 486 | | $TZ | 时区 | 487 | | $MAILCHECK | 每隔多少秒检查是否有新的信件 | 488 | | $OSTYPE | 操作系统类型 | 489 | | $MANPATH man | 指令的搜寻路径 | 490 | | $PATH | 可执行程序文件的搜索路径。一般有/usr/bin/, /usr/X11R6/bin/, /usr/local/bin,等等。| 491 | | $PIPESTATUS | 此数组变量保存了最后执行的前台管道的退出状态。相当有趣的是,它不一定和最后执行的命令的退出状态一样。| 492 | | $PPID | 一个进程的$PPID变量保存它的父进程的进程ID(pid)。用这个变量和pidof命令比较。| 493 | | $PROMPT_COMMAND | 这个变量在主提示符前($PS1显示之前)执行它的值里保存的命令。| 494 | | $PS1 | 这是主提示符(第一提示符),它能在命令行上看见。| 495 | | $PS2 | 副提示符(第二提示符),它在期望有附加的输入时能看见。它显示像">"的提示。| 496 | | $PS3 | 第三提示符。它在一个select循环里显示 (参考例子 10-29)。| 497 | | $PS4 | 第四提示符,它在用-x选项调用一个脚本时的输出的每一行开头显示。它通常显示像"+"的提示。| 498 | | $PWD | 工作目录(即你现在所处的目录) ,它类似于内建命令pwd。| 499 | | $REPLY | 没有变量提供给read命令时的默认变量.这也适用于select命令的目录,但只是提供被选择的变量项目编号而不是变量本身的值。 | 500 | | $SECONDS | 脚本已运行的秒数。| 501 | | $SHELLOPTS | 已经激活的shell选项列表,它是一个只读变量。| 502 | | $SHLVL | SHELL的嵌套级别.指示了Bash被嵌套了多深.在命令行里,$SHLVL是1,因此在一个脚本里,它是2 | 503 | | $TMOUT | 如果$TMOUT环境变量被设为非零值时间值time,那么经过time这么长的时间后,shell提示符会超时.这将使此shell退出登录 | 504 | | $UID | 用户ID号,这是当前用户的用户标识号,它在/etc/passwd文件中记录。| 505 | 506 | ### 位置参数 507 | 508 | | 参数处理 | 说明 | 509 | | ---- | ---- | 510 | | `$#` | 传递到脚本的参数个数 | 511 | | `$*` | 以一个单字符串显示所有向脚本传递的参数。如"∗"用「"」括起来的情况、以"1 2…n"的形式输出所有参数。 | 512 | | `$$` | 脚本运行的当前进程ID号 | 513 | | `$!` | 后台运行的最后一个进程的ID号 | 514 | | `$@` | 与∗相同,但是使用时加引号,并在引号中返回每个参数。如"@"用「"」括起来的情况、以"1""2" … "$n" 的形式输出所有参数。 | 515 | | `$-` | 显示Shell使用的当前选项,与set命令功能相同。 | 516 | | `$?` | 显示最后命令的退出状态。0表示没有错误,其他任何值表明有错误。 | 517 | 518 | ### 参数替换 519 | 520 | **${parameter}**:和$parameter是相同的,都是表示变量parameter的值,可以把变量和字符串连接。 521 | 522 | ```shell 523 | your_id=${USER}-on-${HOSTNAME} 524 | echo "$your_id" 525 | # 526 | echo "Old \$PATH = $PATH" 527 | PATH=${PATH}:/opt/bin #在脚本的生存期内,能额外增加路径/opt/bin到环境变量$PATH中去. 528 | echo "New \$PATH = $PATH" 529 | ``` 530 | 531 | **${parameter-default}, ${parameter:-default}**:如果变量没有被设置,使用默认值。`${parameter-default}`和`${parameter:-default}`几乎是相等的。它们之间的差别是:当一个参数已被声明,但是值是NULL的时候两者不同。 532 | 533 | ```shell 534 | echo ${username-`whoami`} 535 | # 如果变量$username还没有被设置,则把命令`whoami`的结果赋给该变量 536 | ``` 537 | 538 | **${parameter=default}, ${parameter:=default}**:如果变量parameter没有设置,把它设置成默认值。除了引起的当变量被声明且值是空值时有些不同外,两种形式几乎相等。 539 | 540 | ```shell 541 | echo "===== \${parameter+alt_value} =====" 542 | echo 543 | 544 | a=${param1+xyz}; echo "a = $a" # a = 545 | 546 | param2= 547 | a=${param2+xyz} 548 | echo "a = $a" # a = xyz 549 | 550 | param3=123 551 | a=${param3+xyz} 552 | echo "a = $a" # a = xyz 553 | 554 | echo 555 | echo "====== \${parameter:+alt_value} ======" 556 | a=${param4:+xyz} 557 | echo "a = $a" # a = 558 | 559 | param5= 560 | a=${param5:+xyz} 561 | echo "a = $a" # a = 562 | # 产生与a=${param5+xyz}不同。 563 | 564 | param6=123 565 | a=${param6+xyz} 566 | echo "a = $a" # a = xyz 567 | ``` 568 | 569 | **${parameter?err_msg}, ${parameter:?err_msg}**:如果变量parameter已经设置,则使用该值,否则打印err_msg错误信息。[demo20](./example/demo20) 570 | 571 | ```shell 572 | #!/bin/bash 573 | # 变量替换和"usage"信息 574 | 575 | : ${1?"Usage: $0 ARGUMENT"} 576 | # 如果没有提供命令行参数则脚本在这儿就退出了, 577 | #+ 并打印了错误信息. 578 | # usage-message.sh: 1: Usage: usage-message.sh ARGUMENT 579 | 580 | echo "These two lines echo only if command-line parameter given." 581 | echo "command line parameter = \"$1\"" 582 | 583 | exit 0 # 仅在命令行参数提供时,才会在这儿退出. 584 | 585 | # 分别检查有命令行参数和没有命令行参数时的退出状态。 586 | # 如果有命令行参数,则"$?"为0. 587 | # 否则, "$?"为1. 588 | ``` 589 | 590 | **${#var}**:字符串长度(即变量$var的字符个数)。对于数组来说,${#array}是数组的第一个元素的升序。`${#*}和${#@}` 表示位置参数的个数。对于一个数组来说,`${#array[*]}`和`${#array[@]}`表示数组中元素的个数。 591 | 592 | ```shell 593 | E_NO_ARGS=65 594 | 595 | if [ $# -eq 0 ] # 必须要有命令行参数给这个演示程序. 596 | then 597 | echo "Please invoke this script with one or more command-line arguments." 598 | exit $E_NO_ARGS 599 | fi 600 | 601 | var01=abcdEFGH28ij 602 | echo "var01 = ${var01}" 603 | echo "Length of var01 = ${#var01}" 604 | # 现在,让我们在变量值内嵌入一个空格. 605 | var02="abcd EFGH28ij" 606 | echo "var02 = ${var02}" 607 | echo "Length of var02 = ${#var02}" 608 | 609 | echo "Number of command-line arguments passed to script = ${#@}" 610 | echo "Number of command-line arguments passed to script = ${#*}" 611 | 612 | exit 0 613 | ``` 614 | 615 | **${var#Pattern}, ${var##Pattern}**:删除从$var前端开始的最短或最长匹配$Pattern的字符串。[demo22](./example/demo22) 616 | 617 | ```shell 618 | echo `basename $PWD` # 当前工作目录的基本名字. 619 | echo "${PWD##*/}" # 当前工作目录的基本名字. 620 | echo 621 | echo `basename $0` # 脚本名. 622 | echo $0 # 脚本名. 623 | echo "${0##*/}" # 脚本名. 624 | echo 625 | filename=test.data 626 | echo "${filename##*.}" # data 627 | # 文件的扩展名. 628 | ``` 629 | 630 | ### declare/typeset 631 | 632 | declare或typeset内建命令(它们是完全相同的)可以用来限定变量的属性。这是在某些编程语言中使用的定义类型不严格的方式。 633 | 634 | - 命令declare是bash版本2之后才有的。 635 | - 命令typeset也可以在ksh脚本中运行。 636 | 637 | **选项** 638 | 639 | [用declare来声明变量类型](./example/demo23) 640 | 641 | | 参数 | 说明 | 例子 | 642 | | ---- | ---- | ---- | 643 | | `-r` | 只读 | `declare -r var1` | 644 | | `-i` | 整数 | `declare -i number;number=3;` | 645 | | `-a` | 数组 | `declare -a indices` | 646 | | `-f` | 函数 | `declare -f` 会列出所有在此脚本前面已定义的函数| 647 | | `-x export` | 函这样将声明一个变量作为脚本的环境变量而被导出。 | `declare -x var3` | 648 | | `-x var=$value` | declare命令允许在声明变量类型的时候同时给变量赋值。| `declare -x var3=373` | 649 | 650 | ### 变量间接引用 651 | 652 | 假设一个变量的值是第二个变量的名字。这样要如何才能从第一个变量处重新获得第二个变量的值?例如,`a=letter_of_alphabet`和`letter_of_alphabet=z`,是否能由a引用得到z ? 这确实可以办到,这种技术被称为间接引用。 653 | 654 | ```shell 655 | a=letter_of_alphabet # 变量"a"保存着另外一个变量的名字. 656 | letter_of_alphabet=z 657 | # 直接引用. 658 | echo "a = $a" # a = letter_of_alphabet 659 | 660 | # 间接引用. 661 | eval a=\$$a 662 | echo "Now a = $a" # 现在 a = z 663 | exit 0 664 | ``` 665 | 666 | ### $RANDOM 667 | 668 | $RANDOM是Bash的一个返回伪随机整数(范围为0 - 32767)的内部函数(而不是一个常量或变量),它不应该用于产生加密的密钥。[demo25](./example/demo25) 669 | 670 | ```shell 671 | # 模拟掷骰子. 672 | SPOTS=6 # 模除 6 会产生 0 - 5 之间的值. 673 | # 结果增1会产生 1 - 6 之间的值. 674 | # 多谢Paulo Marcel Coelho Aragao的简化. 675 | die1=0 676 | die2=0 677 | # 这会比仅设置SPOTS=7且不增1好?为什么会好?为什么会不好? 678 | 679 | # 单独地掷每个骰子,然后计算出正确的机率. 680 | 681 | let "die1 = $RANDOM % $SPOTS +1" # 掷第一个. 682 | let "die2 = $RANDOM % $SPOTS +1" # 掷第二个. 683 | # 上面的算术式中,哪个操作符优先计算 -- 684 | #+ 取模 (%) 还是 加法 (+)? 685 | 686 | let "throw = $die1 + $die2" 687 | echo "Throw of the dice = $throw" 688 | 689 | exit 0 690 | ``` 691 | 692 | ### 双括号结构 693 | 694 | 用`((...))`结构来使用C风格操作符来处理变量。[demo26](./example/demo26) 695 | 696 | ```shell 697 | (( a = 23 )) # 以C风格来设置一个值,在"="两边可以有空格. 698 | echo "a (initial value) = $a" 699 | 700 | (( a++ )) # C风格的计算后自增. 701 | echo "a (after a++) = $a" 702 | 703 | (( a-- )) # C风格的计算后自减. 704 | echo "a (after a--) = $a" 705 | 706 | 707 | (( ++a )) # C风格的计算前自增. 708 | echo "a (after ++a) = $a" 709 | 710 | (( --a )) # C风格的计算前自减. 711 | echo "a (after --a) = $a" 712 | ``` 713 | 714 | ## 转义字符 715 | 716 | 在单个字符前面的转义符`\`告诉shell不必特殊解释这个字符,只把它当成字面上的意思。但在一些命令和软件包里,比如说echo和sed,转义一个字符可能会引起一个相反的效果--因为它们可能触发那个字符的特殊意思。[demo13](./example/demo13) 717 | 718 | `\r` 回车 719 | `\n` 换行 720 | `\c` 不换行 721 | `\t` 水平制表符 722 | `\v` 垂直制表符 723 | `\a` 表示“警告”(蜂鸣或是闪动) 724 | `\\` 反斜杠字符 725 | `\0ddd` 将自负表示成1到3的八进制数值 726 | 727 | 728 | ## 退出/退出状态 729 | 730 | `$?` 变量用于测试脚本中的命令执行结果非常的有用。[demo14](./example/demo14) 731 | 732 | ```shell 733 | echo hello 734 | echo $? # 因为上一条命令执行成功,打印0。 735 | 736 | lskdf # 无效命令。 737 | echo $? # 因为上面的无效命令执行失败,打印一个非零的值。 738 | 739 | exit 113 # 返回113状态码给shell。 740 | # 可以运行脚本结束后立即执行命令"echo $?" 检验。 741 | 742 | # 依照惯例,命令'exit 0'表示执行成功, 743 | #+ 当产生一个非零退出值时表示一个错误或是反常的条件。 744 | ``` 745 | 746 | 下面这些退出状态码,用于保留(reserved meanings) 的含义,不应该在用户脚本使用。 747 | 748 | | Exit Code Number | Meaning | Example | Comments | 749 | | ---- | ---- | ---- | ---- | 750 | | 1 | Catchall for general errors | `let "var1 = 1/0"` | Miscellaneous errors, such as "divide by zero" | 751 | | 2 | Misuse of shell builtins (according to Bash documentation) | - | Seldom seen, usually defaults to exit code 1 | 752 | | 126 | Command invoked cannot execute | - | Permission problem or command is not an executable | 753 | | 127 | "command not found" | - | Possible problem with $PATH or a typo | 754 | | 128 | Invalid argument to exit | `exit 3.14159` | exit takes only integer args in the range 0 - 255 (see footnote) | 755 | | 128+n | Fatal error signal "n" | `kill -9 $PPID` | of script $? returns 137 (128 + 9) | 756 | | 130 | Script terminated by Control-C | - | Control-C is fatal error signal 2, (130 = 128 + 2, see above) | 757 | | 255* | Exit status out of range | `exit -1` | exit takes only integer args in the range 0 - 255 | 758 | 759 | 760 | ## 测试 761 | 762 | ### 测试结构 763 | 764 | 一个if/then结构能包含嵌套的比较和测试。 765 | 766 | ```shell 767 | echo "Testing \"false\"" 768 | if [ "false" ] # "false"是一个字符串. 769 | then 770 | echo "\"false\" is true." #+ 它被测试为真. 771 | else 772 | echo "\"false\" is false." 773 | fi # "false"为真. 774 | ``` 775 | 776 | Else if 和 elif 777 | 778 | elif是else if的缩写。作用是在一个if/then里嵌入一个内部的if/then结构。 779 | 780 | `[[]]`结构比Bash版本的`[]`更通用。用`[[ ... ]]`测试结构比用`[ ... ]`更能防止脚本里的许多逻辑错误。比如说,`&&`,`||`,`<`和`>`操作符能在一个`[[]]`测试里通过,但在`[]`结构会发生错误。 781 | 782 | `(( ))`结构扩展并计算一个算术表达式的值。如果表达式值为0,会返回1或假作为退出状态码。一个非零值的表达式返回一个0或真作为退出状态码。这个结构和先前test命令及`[]`结构的讨论刚好相反。 783 | 784 | ### 文件测试操作符 785 | 786 | 如果下面的条件成立返回真。[demo15](./example/demo15) 787 | 788 | | 操作符 | 描述 | 789 | | ---- | ---- | 790 | | -e | 文件存在 | 791 | | -a | 文件存在,这个和-e的作用一样. 它是不赞成使用的,所以它的用处不大。 | 792 | | -f | 文件是一个普通文件(不是一个目录或是一个设备文件) | 793 | | -s | 文件大小不为零 | 794 | | -d | 文件是一个目录 | 795 | | -b | 文件是一个块设备(软盘,光驱,等等。) | 796 | | -c | 文件是一个字符设备(键盘,调制解调器,声卡,等等。) | 797 | | -p | 文件是一个管道 | 798 | | -h | 文件是一个符号链接 | 799 | | -L | 文件是一个符号链接 | 800 | | -S | 文件是一个socket | 801 | | -t | 文件(描述符)与一个终端设备相关。| 802 | | -r | 文件是否可读 (指运行这个测试命令的用户的读权限) | 803 | | -w | 文件是否可写 (指运行这个测试命令的用户的读权限) | 804 | | -x | 文件是否可执行 (指运行这个测试命令的用户的读权限) | 805 | | -g | 文件或目录的设置-组-ID(sgid)标记被设置。 | 806 | | -u | 文件的设置-用户-ID(suid)标志被设置 | 807 | | -k | 粘住位设置 | 808 | | -N | 文件最后一次读后被修改 | 809 | | f1 -nt f2 | 文件f1比f2新 | 810 | | f1 -ot f2 | 文件f1比f2旧 | 811 | | f1 -ef f2 | 文件f1和f2 是相同文件的硬链接 | 812 | | ! | "非" -- 反转上面所有测试的结果(如果没有给出条件则返回真)。| 813 | 814 | **注意⚠️** 815 | 816 | 1. `-t` 这个测试选项可以用于检查脚本中是否标准输入 ([ -t 0 ])或标准输出([ -t 1 ])是一个终端。 817 | 1. `-g` 如果一个目录的sgid标志被设置,在这个目录下创建的文件都属于拥有此目录的用户组,而不必是创建文件的用户所属的组。这个特性对在一个工作组里的同享目录很有用处。 818 | 819 | ### 比较操作符 820 | 821 | 二元比较操作符比较两个变量或是数值。注意整数和字符串比较的分别。 822 | 823 | **整数比较** 824 | 825 | [demo16](./example/demo16) 826 | 827 | | 比较操作符 | 描述 | 例子 | 828 | | ---- | ---- | ---- | 829 | | `-eq` | 等于 | `if [ "$a" -eq "$b" ]` | 830 | | `-ne` | 不等于 | `if [ "$a" -ne "$b" ]` | 831 | | `-gt` | 大于 | `if [ "$a" -gt "$b" ]` | 832 | | `-ge` | 大于等于 | `if [ "$a" -ge "$b" ]` | 833 | | `-lt` | 小于 | `if [ "$a" -lt "$b" ]` | 834 | | `-le` | 小于等于 | `if [ "$a" -le "$b" ]` | 835 | | `<` | 小于(在双括号里使用) | `(("$a" < "$b"))` | 836 | | `<=` | 小于等于 (在双括号里使用) | `(("$a" <= "$b"))` | 837 | | `>` | 大于 (在双括号里使用) | `(("$a" > "$b"))` | 838 | | `>=` | 大于等于(在双括号里使用) | `(("$a" >= "$b"))` | 839 | 840 | **字符串比较** 841 | 842 | | 比较操作符 | 描述 | 例子 | 843 | | ---- | ---- | ---- | 844 | | = | 等于 | `if [ "$a" = "$b" ]` | 845 | | == | 等于,它和=是同义词。 | `if [ "$a" == "$b" ]` | 846 | | != | 不相等,操作符在[[ ... ]]结构里使用模式匹配. | `if [ "$a" != "$b" ]` | 847 | | < | 小于,依照ASCII字符排列顺序,注意"<"字符在[ ] 结构里需要转义 | `if [[ "$a" < "$b" ]]` `if [ "$a" \< "$b" ]` | 848 | | > | 大于,依照ASCII字符排列顺序,注意">"字符在[ ] 结构里需要转义. | `if [[ "$a" > "$b" ]]` `if [ "$a" \> "$b" ]`| 849 | | -z | 字符串为"null",即是指字符串长度为零。 | - | 850 | | -n | 字符串不为"null",即长度不为零。 | - | 851 | 852 | **混合比较** 853 | 854 | | 比较操作符 | 描述 | 例子 | 855 | | ---- | ---- | ---- | 856 | | -a | 逻辑与,如果exp1和exp2都为真,则exp1 -a exp2返回真。 | `if [ "$exp1" -a "$exp2" ]` | 857 | | -o | 逻辑或,只要exp1和exp2任何一个为真,则exp1 -o exp2 返回真。 | `if [ "$exp1" -o "$exp2" ]` | 858 | 859 | 在一个混合测试中,把一个字符串变量引号引起来可能还不够。如果$string变量是空的话,表达式`[ -n "$string" -o "$a" = "$b" ]`在一些Bash版本中可能会引起错误。安全的办法是附加一个外部的字符串给可能有空字符串变量比较的所有变量,`[ "x$string" != x -o "x$a" = "x$b" ]` (x字符可以互相抵消) 860 | 861 | ## 操作字符串 862 | 863 | Bash已经支持了令人惊讶的字符串操作的数量。不一致的命令语法和冗余的功能,导致真的学起来有困难。 864 | 865 | ### 字符串长度 866 | 867 | ```shell 868 | String=abcABC123ABCabc 869 | 870 | echo ${#String} # 15 871 | echo `expr length $String` # 15 872 | echo `expr "$String" : '.*'` # 15 873 | ``` 874 | 875 | 匹配字符串开头的字串的长度,下面两种方法的 $substring 是一个正则表达式。 876 | 877 | `expr match "$string" '$substring'` 878 | `expr "$string" : '$substring'` 879 | 880 | ```shell 881 | String=abcABC123ABCabc 882 | # └------┘ 883 | # 884 | echo `expr match "$String" 'abc[A-Z]*.2'` # 8 885 | echo `expr "$String" : 'abc[A-Z]*.2'` # 8 886 | ``` 887 | 888 | ### 索引 889 | 890 | `expr index $string $substring` 在字符串$string中$substring第一次出现的数字位置 891 | 892 | ```shell 893 | String=abcABC123ABCabc 894 | echo `expr index "$String" C12` # 6 895 | # C 字符的位置. 896 | 897 | echo `expr index "$String" 1c` # 3 898 | # 'c' (in #3 position) matches before '1'. 899 | ``` 900 | 901 | ### 字串提取 902 | 903 | `${string:position}` 把$string中从第$postion个字符开始字符串提取出来。如果$string是"*"或"@",则表示从位置参数中提取第$postion后面的字符串。 904 | `${string:position:length}` 把$string中$postion个字符后面的长度为$length的字符串提取出来。[demo18](./example/demo18) 905 | 906 | ```shell 907 | # 字串提取 908 | String=abcABC123ABCabc 909 | # 0123456789..... 910 | # 以0开始计算. 911 | 912 | echo ${String:0} # abcABC123ABCabc 913 | echo ${String:1} # bcABC123ABCabc 914 | echo ${String:7} # 23ABCabc 915 | echo ${String:7:3} # 23A 916 | # 提取的字串长为3 917 | 918 | # 有没有可能从字符串的右边结尾处提取? 919 | 920 | echo ${String:-4} # abcABC123ABCabc 921 | # 默认是整个字符串,就相当于${parameter:-default}. 922 | # 然而. . . 923 | 924 | echo ${String:(-4)} # Cabc 925 | echo ${String: -4} # Cabc 926 | # 这样,它可以工作了. 927 | # 圆括号或附加的空白字符可以转义$position参数. 928 | ``` 929 | 930 | ### 字串移动 931 | 932 | `${string#substring}`从$string左边开始,剥去最短匹配$substring子串。 933 | `${string##substring}`从$string左边开始,剥去最长匹配$substring子串。 934 | `${string%substring}` 从$string结尾开始,剥去最短匹配$substring子串。 935 | `${string%%substring}`从$string结尾开始,剥去最长匹配$substring子串。 936 | 937 | ```shell 938 | String=abcABC123ABCabc 939 | # ├----┘ ┆ 940 | # └----------┘ 941 | 942 | echo ${String#a*C} # 123ABCabc 943 | # 剥去匹配'a'到'C'之间最短的字符串. 944 | 945 | echo ${String##a*C} # abc 946 | # 剥去匹配'a'到'C'之间最长的字符串. 947 | 948 | 949 | String=abcABC123ABCabc 950 | # ┆ || 951 | # └------------┘ 952 | 953 | echo ${String%b*c} # abcABC123ABCa 954 | # 从$String后面尾部开始,剥去匹配'a'到'C'之间最短的字符串. 955 | 956 | echo ${String%%b*c} # a 957 | # 从$String后面尾部开始,剥去匹配'a'到'C'之间最长的字符串. 958 | ``` 959 | 960 | ### 用awk处理字符串 961 | 962 | Bash脚本可以调用awk的字符串操作功能来代替它自己内建的字符串操作符 963 | 964 | ```shell 965 | String=23skidoo1 966 | # 012345678 Bash 967 | # 123456789 awk 968 | # 注意上面两个程序对索引的不同处理: 969 | # Bash把字符串的第一个字符的标号称为'0'。 970 | # Awk把字符串的第一个字符的标号称为'1'。 971 | 972 | echo ${String:2:4} # position 3 (0-1-2), 4 characters long 973 | # skid 974 | 975 | # 在awk中与Bash的${string:pos:length}等同的是substr(string,pos,length)。 976 | echo | awk '{ 977 | print substr("'"${String}"'",3,4) # skid 978 | }' 979 | # 用一个空的"echo"由管道传一个空的输入给awk, 980 | #+ 这样就不必提供一个文件名给awk。 981 | exit 0 982 | ``` 983 | 984 | ## for/while 985 | 986 | 重复一些命令的代码块,如果条件不满足就退出循环。 987 | 988 | ### for 989 | 990 | 在循环的每次执行中,arg将顺序的存取list中列出的变量,下面是一个基本的循环结构。[demo27](./example/demo27) 991 | 992 | > for arg in [list] 993 | > do 994 | > command(s)... 995 | > done 996 | 997 | 每个`[list]`中的元素都可能包含多个参数,在处理参数组时,这是非常有用的,使用set命令来强制解析每个`[list]`中的元素。并且分配每个解析出来的部分到一个位置参数中。 998 | 999 | 循环的一个简单例子 1000 | 1001 | ```shell 1002 | for planet in Mercury Venus Earth Mars Jupiter Saturn Uranus Neptune Pluto 1003 | do 1004 | echo $planet # 每个行星被单独打印在一行上. 1005 | done 1006 | ``` 1007 | 1008 | ### while 1009 | 1010 | 一个while循环可以有多个判断条件,但是只有最后一个才能决定是否退出循环。然而这需要一种有点不同的循环语法。 1011 | 1012 | > while [condition] 1013 | > do 1014 | > command... 1015 | > done 1016 | 1017 | ```shell 1018 | # -------------------------- 1019 | # 简单的while循环 1020 | # -------------------------- 1021 | var0=0 1022 | LIMIT=10 1023 | 1024 | while [ "$var0" -lt "$LIMIT" ] 1025 | do 1026 | echo -n "$var0 " # -n 将会阻止产生新行。 1027 | # ^ 空格,数字之间的分隔。 1028 | var0=`expr $var0 + 1` # var0=$(($var0+1)) 也可以。 1029 | # var0=$((var0 + 1)) 也可以。 1030 | # let "var0 += 1" 也可以。 1031 | done # 使用其他的方法也行。 1032 | # -------------------------- 1033 | # 多条件的while循环 1034 | # -------------------------- 1035 | var1=unset 1036 | previous=$var1 1037 | 1038 | while echo "previous-variable = $previous" 1039 | echo 1040 | previous=$var1 1041 | [ "$var1" != end ] # 记录之前的$var1. 1042 | # 这个"while"循环中有4个条件, 但是只有最后一个能控制循环. 1043 | # 退出状态由第4个条件决定. 1044 | do 1045 | echo "Input variable #1 (end to exit) " 1046 | read var1 1047 | echo "variable #1 = $var1" 1048 | done 1049 | exit 0 1050 | ``` 1051 | 1052 | ### until 1053 | 1054 | 这个结构在循环的顶部判断条件,并且如果条件一直为false那就一直循环下去。(与while相反)。 1055 | 1056 | > until [condition-is-true] 1057 | > do 1058 | > command... 1059 | > done 1060 | 1061 | **注意⚠️** 1062 | 1063 | 1. until循环的判断在循环的顶部,这与某些编程语言是不同的。 1064 | 2. 与for循环一样,如果想把do和条件放在一行里,就使用";"。 1065 | 1066 | > until [condition-is-true] ; do 1067 | 1068 | ```shell 1069 | END_CONDITION=end 1070 | until [ "$var1" = "$END_CONDITION" ] 1071 | # 在循环的顶部判断条件. 1072 | do 1073 | echo "Input variable #1 " 1074 | echo "($END_CONDITION to exit)" 1075 | read var1 1076 | echo "variable #1 = $var1" 1077 | done 1078 | exit 0 1079 | ``` 1080 | 1081 | ### 嵌套循环 1082 | 1083 | 嵌套循环就是在一个循环中还有一个循环,内部循环在外部循环体中。[demo28](./example/demo28) 1084 | 1085 | ```shell 1086 | outer=1 # 设置外部循环计数. 1087 | # 开始外部循环. 1088 | for a in 1 2 3 4 5 1089 | do 1090 | echo "Pass $outer in outer loop." 1091 | echo "---------------------" 1092 | inner=1 # 重设内部循环的计数. 1093 | 1094 | # =============================================== 1095 | # 开始内部循环. 1096 | for b in 1 2 3 4 5 1097 | do 1098 | echo "Pass $inner in inner loop." 1099 | let "inner+=1" # 增加内部循环计数. 1100 | done 1101 | # 内部循环结束. 1102 | # =============================================== 1103 | 1104 | let "outer+=1" # 增加外部循环的计数. 1105 | echo # 每次外部循环之间的间隔. 1106 | done 1107 | # 外部循环结束. 1108 | 1109 | exit 0 1110 | ``` 1111 | 1112 | ### 循环控制 1113 | 1114 | 影响循环行为的命令 `break`, `continue`, break命令将会跳出循环,continue命令将会跳过本次循环下边的语句,直接进入下次循环。[demo29](./example/demo29) 1115 | 1116 | **continue:** continue命令与break命令类似,只有一点差别,它不会跳出所有循环,仅仅跳出当前循环。 1117 | 1118 | ```shell 1119 | LIMIT=19 # 上限 1120 | 1121 | echo "Printing Numbers 1 through 20 (but not 3 and 11)." 1122 | 1123 | a=0 1124 | 1125 | while [ $a -le "$LIMIT" ] 1126 | do 1127 | a=$(($a+1)) 1128 | if [ "$a" -eq 3 ] || [ "$a" -eq 11 ] # Excludes 3 and 11. 1129 | then 1130 | continue # 跳过本次循环剩下的语句. 1131 | fi 1132 | echo -n "$a " # 在$a等于3和11的时候,这句将不会执行. 1133 | done 1134 | ``` 1135 | 1136 | **break:** break命令允许跳出所有循环(终止执行后面的所有循环)。 1137 | 1138 | 下面的例子中,脚本进入死循环直至用户输入数字大于5。要跳出这个循环,返回到shell提示符下,就要使用break命令。 1139 | 1140 | ```shell 1141 | while : 1142 | do 1143 | echo -n "Input a number between 1 to 5: " 1144 | read aNum 1145 | case $aNum in 1146 | 1|2|3|4|5) echo "Your number is $aNum!" 1147 | ;; 1148 | *) echo "You do not select a number between 1 to 5, game is over!" 1149 | break 1150 | ;; 1151 | esac 1152 | done 1153 | ``` 1154 | 1155 | ⚠️ 在嵌套循环中,break 命令后面还可以跟一个整数,表示跳出第几层循环。例如: 1156 | 1157 | ```shell 1158 | break n #表示跳出第 n 层循环。 1159 | ``` 1160 | 1161 | ## case/select 1162 | 1163 | case/select依靠在代码块的顶部或底部的条件判断来决定程序的分支。 1164 | 1165 | ### case 1166 | 1167 | case它允许通过判断来选择代码块中多条路径中的一条。它的作用和多个if/then/else语句相同,是它们的简化结构,特别适用于创建目录。[demo30](./example/demo30) 1168 | 1169 | > ```shell 1170 | > case "$variable" in 1171 | > ?"$condition1" ) 1172 | > ?command... 1173 | > ?;; 1174 | > ?"$condition2" ) 1175 | > ?command... 1176 | > ?;; 1177 | > esac 1178 | > ``` 1179 | 1180 | - 对变量使用`""`并不是强制的,因为不会发生单词分离。 1181 | - 每句测试行,都以右小括号`)`结尾。 1182 | - 每个条件块都以两个分号结尾`;;`。 1183 | - case块的结束以esac(case的反向拼写)结尾。 1184 | 1185 | ```shell 1186 | clear # 清屏. 1187 | 1188 | echo " 我的简历" 1189 | echo " ------- " 1190 | echo "下面通过shell脚本输出我的简历" 1191 | echo 1192 | echo "[B]asicinfo, 基本信息" 1193 | echo "[E]ducation, 教育经历" 1194 | echo "[I]tskill, IT 技能" 1195 | echo 1196 | read person 1197 | case "$person" in 1198 | # 注意,变量是被引用的. 1199 | "B" | "b" ) 1200 | # 接受大写或小写输入. 1201 | echo 1202 | echo "小弟调调" 1203 | echo "手 机 : 136*****13" 1204 | echo "E-mail :wowohoo@qq.com" 1205 | echo "首 页 : http://JSLite.io" 1206 | ;; 1207 | # 注意,在每个选项后边都需要以;;结尾. 1208 | 1209 | "E" | "e" ) 1210 | # 接受大写或小写输入. 1211 | echo "■ 2003年9月 到 2006年8月" 1212 | echo "----------------------------" 1213 | echo "› 学校 : 野鸟高中" 1214 | echo "› 专业 : 艺术类" 1215 | echo "› 学历 : 高中" 1216 | ;; 1217 | # 后边的[I]tskill的信息在这里就省略了. 1218 | * ) 1219 | # 默认选项. 1220 | # 空输入(敲RETURN). 1221 | echo 1222 | echo "没有数据!" 1223 | ;; 1224 | esac 1225 | exit 0 1226 | ``` 1227 | 1228 | 1229 | ### select 1230 | 1231 | select结构是建立菜单的另一种工具,这种结构是从ksh中引入的。 1232 | 1233 | > select variable [in list] 1234 | > do 1235 | > ?command... 1236 | > ?break 1237 | > done 1238 | 1239 | 用select来创建菜单 1240 | 1241 | ```shell 1242 | PS3='选择你喜欢的蔬菜: ' # 设置提示符字串. 1243 | 1244 | echo 1245 | select vegetable in "豆" "胡萝卜" "土豆" "洋葱" "芜菁甘蓝" 1246 | do 1247 | echo 1248 | echo "你最喜欢的蔬菜是 $vegetable 。" 1249 | echo "讨厌!" 1250 | echo 1251 | break # 如果这里没有'break'会发生什么? 1252 | done 1253 | exit 0 1254 | ``` 1255 | 1256 | 如果忽略了in list列表,那么select命令将使用传递到脚本的命令行参数($@),或者是函数参数(当select是在函数中时)与忽略in list时的for语句相比较:**for variable [in list]** 1257 | 1258 | ```shell 1259 | PS3='选择你喜欢的蔬菜: ' 1260 | echo 1261 | choice_of(){ 1262 | select vegetable 1263 | # [in list] 被忽略, 所以'select'用传递给函数的参数. 1264 | do 1265 | echo 1266 | echo "你最喜欢的蔬菜是 $vegetable。" 1267 | echo "讨厌!" 1268 | echo 1269 | break 1270 | done 1271 | } 1272 | 1273 | choice_of "豆" "米饭" "胡萝卜" "土豆" "洋葱" "芜菁甘蓝" 1274 | # $1 $2 $3 $4 $5 $6 1275 | # 传递给choice_of() 函数的参数 1276 | 1277 | exit 0 1278 | ``` 1279 | 1280 | ## 函数 1281 | 1282 | 和"真正的"编程语言一样,Bash也有函数,虽然在某些实现方面稍有些限制。 一个函数是一个子程序,用于实现一串操作的代码块(code block),它是完成特定任务的"黑盒子"。 当有重复代码,当一个任务只需要很少的修改就被重复几次执行时, 这时你应考虑使用函数。 [demo33](./example/demo33) 1283 | 1284 | ```shell 1285 | function function_name { 1286 | command... 1287 | } 1288 | # 或 1289 | function_name () { 1290 | command... 1291 | } 1292 | ``` 1293 | 1294 | 在一个函数内嵌套另一个函数也是可以的,但是不常用。 1295 | 1296 | ```shell 1297 | f1 (){ 1298 | f2 (){ # nested 1299 | echo "Function \"f2\", inside \"f1\"." 1300 | } 1301 | } 1302 | f2 # 引起错误. 1303 | # 就是你先"declare -f f2"了也没用. 1304 | 1305 | f1 # 什么也不做,因为调用"f1"不会自动调用"f2". 1306 | f2 # 现在,可以正确的调用"f2"了, 1307 | #+ 因为之前调用"f1"使"f2"在脚本中变得可见了. 1308 | ``` 1309 | 1310 | ### 局部变量 1311 | 1312 | 如果变量用local来声明,那么它只能在该变量声明的代码块(block of code)中可见,这个代码块就是局部"范围"。 1313 | 1314 | ```shell 1315 | # 在函数内部的全局和局部变量. 1316 | func () 1317 | { 1318 | local loc_var=23 # 声明为局部变量. 1319 | echo # 使用内建的'local'关键字. 1320 | echo "\"loc_var\" in function = $loc_var" 1321 | global_var=999 # 没有声明为局部变量. 1322 | # 默认为全局变量. 1323 | echo "\"global_var\" in function = $global_var" 1324 | } 1325 | 1326 | func 1327 | # 现在,来看看是否局部变量"loc_var"能否在函数外面可见. 1328 | echo "\"loc_var\" outside function = $loc_var" 1329 | # $loc_var outside function = 1330 | # 不, $loc_var不是全局可访问的. 1331 | echo "\"global_var\" outside function = $global_var" 1332 | # $global_var outside function = 999 1333 | # $global_var 是全局可访问的. 1334 | exit 0 1335 | # 与In contrast to C相比, 在函数内声明的Bash变量只有在 1336 | #+ 它被明确声明成局部的变量时才是局部的 1337 | ``` 1338 | 1339 | ⚠️ :在函数调用之前,所有在函数内声明且没有明确声明为local的变量都可在函数体外可见 1340 | 1341 | ```shell 1342 | func (){ 1343 | global_var=37 # 在函数还没有被调用前 1344 | #+ 变量只在函数内可见. 1345 | } # 函数结束 1346 | echo "global_var = $global_var" # global_var = 1347 | # 函数"func"还没有被调用, 1348 | #+ 所以变量$global_var还不能被访问. 1349 | func 1350 | echo "global_var = $global_var" # global_var = 37 1351 | # 已经在函数调用时设置了值. 1352 | ``` 1353 | 1354 | ### 函数参数 1355 | 1356 | 在Shell中,调用函数时可以向其传递参数。在函数体内部,通过 `$n` 的形式来获取参数的值,例如,$1表示第一个参数,$2表示第二个参数 1357 | 1358 | ```shell 1359 | funWithParam(){ 1360 | echo "第一个参数为 $1 !" 1361 | echo "第二个参数为 $2 !" 1362 | echo "第十个参数为 $10 !" 1363 | echo "第十个参数为 ${10} !" 1364 | echo "第十一个参数为 ${11} !" 1365 | echo "参数总数有 $# 个!" 1366 | echo "作为一个字符串输出所有参数 $* !" 1367 | } 1368 | funWithParam 1 2 3 4 5 6 7 8 9 34 73 1369 | ``` 1370 | 1371 | ### 函数返回值 1372 | 1373 | 定义一个带有return语句的函数。函数返回值在调用该函数后通过 `$?` 来获得。 1374 | 1375 | ```shell 1376 | funWithReturn(){ 1377 | echo "这个函数会对输入的两个数字进行相加运算..." 1378 | echo "输入第一个数字: " 1379 | read aNum 1380 | echo "输入第二个数字: " 1381 | read anotherNum 1382 | echo "两个数字分别为 $aNum 和 $anotherNum !" 1383 | return $(($aNum+$anotherNum)) 1384 | } 1385 | funWithReturn 1386 | echo "输入的两个数字之和为 $? !" 1387 | 1388 | # 这个函数会对输入的两个数字进行相加运算... 1389 | # 输入第一个数字: 1390 | # 1 1391 | # 输入第二个数字: 1392 | # 2 1393 | # 两个数字分别为 1 和 2 ! 1394 | # 输入的两个数字之和为 3 ! 1395 | ``` 1396 | 1397 | ⚠️ `$10` 不能获取第十个参数,获取第十个参数需要${10}。当n>=10时,需要使用${n}来获取参数。 1398 | 1399 | **特殊字符用来处理参数:** 1400 | 1401 | | 参数处理 | 说明 | 1402 | | ---- | ---- | 1403 | | $# | 传递到脚本的参数个数 | 1404 | | $* | 以一个单字符串显示所有向脚本传递的参数 | 1405 | | $$ | 脚本运行的当前进程ID号 | 1406 | | $! | 后台运行的最后一个进程的ID号 | 1407 | | $@ | 与$*相同,但是使用时加引号,并在引号中返回每个参数。 | 1408 | | $- | 显示Shell使用的当前选项,与set命令功能相同。 | 1409 | | $? | 显示最后命令的退出状态。0表示没有错误,其他任何值表明有错误。 | 1410 | 1411 | ## Contributors 1412 | 1413 | As always, thanks to our amazing contributors! 1414 | 1415 | 1416 | 1417 | 1418 | 1419 | Made with [action-contributors](https://github.com/jaywcjlove/github-action-contributors). 1420 | 1421 | ## License 1422 | 1423 | Licensed under the MIT License. 1424 | -------------------------------------------------------------------------------- /docs/shell-regex.md: -------------------------------------------------------------------------------- 1 | Shell RegEx 2 | === 3 | 4 | ⚠️ 创建了一个[新仓库](https://github.com/jaywcjlove/regexp-example),方便专门搜集讨论正则相关内容。顺便将下面内容整理到了[新仓库](https://github.com/jaywcjlove/regexp-example)。=> [`@jaywcjlove/regexp-example`](https://github.com/jaywcjlove/regexp-example) 5 | 6 | 7 | 8 | ## 目录 9 | 10 | - [正则表达式的分类](#正则表达式的分类) 11 | - [基本组成部分](#基本组成部分) 12 | - [POSIX字符类](#posix字符类) 13 | - [元字符](#元字符) 14 | 15 | 16 | 17 | ## 正则表达式的分类 18 | 19 | 1. 基本的正则表达式(Basic Regular Expression 又叫Basic RegEx 简称BREs) 20 | 2. 扩展的正则表达式(Extended Regular Expression 又叫Extended RegEx 简称EREs) 21 | 3. Perl的正则表达式(Perl Regular Expression 又叫Perl RegEx 简称PREs) 22 | 23 | ## 基本组成部分 24 | 25 | 正则表达式的基本组成部分。 26 | 27 | | 正则表达式 | 描述 | 示例 | Basic RegEx | Extended RegEx | Python RegEx | Perl regEx | 28 | | ---- | ---- | ---- | ---- | ---- | ---- | ---- | 29 | | \ | 转义符,将特殊字符进行转义,忽略其特殊意义 | a\.b匹配a.b,但不能匹配ajb,.被转义为特殊意义 | \ | \ | \ | \ | 30 | | ^ | 匹配行首,awk中,^则是匹配字符串的开始 | ^tux匹配以tux开头的行 | ^ | ^ | ^ | ^ | 31 | | $ | 匹配行尾,awk中,$则是匹配字符串的结尾 | tux$匹配以tux结尾的行 | $ | $ | $ | $ | 32 | | . | 匹配除换行符\n之外的任意单个字符,awk则中可以 | ab.匹配abc或bad,不可匹配abcd或abde,只能匹配单字符 | . | . | . | . | 33 | | [] | 匹配包含在[字符]之中的任意一个字符 | coo[kl]可以匹配cook或cool | [] | [] | [] | [] | 34 | | [^] | 匹配[^字符]之外的任意一个字符 | 123[^45]不可以匹配1234或1235,1236、1237都可以 | [^] | [^] | [^] | [^] | 35 | | [-] | 匹配[]中指定范围内的任意一个字符,要写成递增 | [0-9]可以匹配1、2或3等其中任意一个数字 | [-] | [-] | [-] | [-] | 36 | | ? | 匹配之前的项1次或者0次 | colou?r可以匹配color或者colour,不能匹配colouur | 不支持 | ? | ? | ? | 37 | | + | 匹配之前的项1次或者多次 | sa-6+匹配sa-6、sa-666,不能匹配sa- | 不支持 | + | + | + | 38 | | * | 匹配之前的项0次或者多次 | co*l匹配cl、col、cool、coool等 | * | * | * | * | 39 | | () | 匹配表达式,创建一个用于匹配的子串 | ma(tri)?匹配max或maxtrix | 不支持 | () | () | () | 40 | | {n} | 匹配之前的项n次,n是可以为0的正整数 | [0-9]{3}匹配任意一个三位数,可以扩展为[0-9][0-9][0-9] | 不支持 | {n} | {n} | {n} | 41 | | {n,} | 之前的项至少需要匹配n次 | [0-9]{2,}匹配任意一个两位数或更多位数 | 不支持 | {n,} | {n,} | {n,} | 42 | | {n,m} | 指定之前的项至少匹配n次,最多匹配m次,n<=m | [0-9]{2,5}匹配从两位数到五位数之间的任意一个数字 | 不支持 | {n,m} | {n,m} | {n,m} | 43 | | `\|` | 交替匹配|两边的任意一项 | `ab(c|d)`匹配abc或abd | 不支持 | `\|` | `\|` | `\|` | 44 | 45 | ## POSIX字符类 46 | 47 | POSIX字符类是一个形如[:...:]的特殊元序列(meta sequence),他可以用于匹配特定的字符范围。 48 | 49 | | 正则表达式 | 描述 | 示例 | Basic RegEx | Extended RegEx | Python RegEx | Perl regEx | 50 | | ---- | ---- | ---- | ---- | ---- | ---- | ---- | 51 | | [:alnum:] | 匹配任意一个字母或数字字符 | [[:alnum:]]+ | [:alnum:] | [:alnum:] | [:alnum:] | [:alnum:] | 52 | | [:alpha:] | 匹配任意一个字母字符(包括大小写字母) | [[:alpha:]]{4} | [:alpha:] | [:alpha:] | [:alpha:] | [:alpha:] | 53 | | [:blank:] | 空格与制表符(横向和纵向) | [[:blank:]]* | [:blank:] | [:blank:] | [:blank:] | [:blank:] | 54 | | [:digit:] | 匹配任意一个数字字符 | [[:digit:]]? | [:digit:] | [:digit:] | [:digit:] | [:digit:] | 55 | | [:lower:] | 匹配小写字母 | [[:lower:]]{5,} | [:lower:] | [:lower:] | [:lower:] | [:lower:] | 56 | | [:upper:] | 匹配大写字母 | ([[:upper:]]+)? | [:upper:] | [:upper:] | [:upper:] | [:upper:] | 57 | | [:punct:] | 匹配标点符号 | [[:punct:]] | [:punct:] | [:punct:] | [:punct:] | [:punct:] | 58 | | [:space:] | 匹配 59 | 一个包括换行符、回车等在内的所有空白符 | [[:space:]]+ | [:space:] | [:space:] | [:space:] | [:space:] | 60 | | [:graph:] | 匹配任何一个可以看得见的且可以打印的字符 | [[:graph:]] | [:graph:] | [:graph:] | [:graph:] | [:graph:] | 61 | | [:xdigit:] | 任何一个十六进制数(即:0-9,a-f,A-F) | [[:xdigit:]]+ | [:xdigit:] | [:xdigit:] | [:xdigit:] | [:xdigit:] | 62 | | [:cntrl:] | 任何一个控制字符([ASCII](http://zh.wikipedia.org/zh/ASCII)字符集中的前32个字符) | [[:cntrl:]] | [:cntrl:] | [:cntrl:] | [:cntrl:] | [:cntrl:] | 63 | | [:print:] | 任何一个可以打印的字符 | [[:print:]] | [:print:] | [:print:] | [:print:] | [:print:] | 64 | 65 | ## 元字符 66 | 67 | 元字符(meta character)是一种Perl风格的正则表达式,只有一部分文本处理工具支持它,并不是所有的文本处理工具都支持。 68 | 69 | | 正则表达式 | 描述 | 示例 | Basic RegEx | Extended RegEx | Python RegEx | Perl regEx | 70 | | ---- | ---- | ---- | ---- | ---- | ---- | ---- | 71 | | \b | 单词边界 | \bcool\b 匹配cool,不匹配coolant | \b | \b | \b | \b | 72 | | \B | 非单词边界 | cool\B 匹配coolant,不匹配cool | \B | \B | \B | \B | 73 | | \d | 单个数字字符 | b\db 匹配b2b,不匹配bcb | 不支持 | 不支持 | \d | \d | 74 | | \D | 单个非数字字符 | b\Db 匹配bcb,不匹配b2b | 不支持 | 不支持 | \D | \D | 75 | | \w | 单个单词字符(字母、数字与_) | \w 匹配1或a,不匹配& | \w | \w | \w | \w | 76 | | \W | 单个非单词字符 | \W 匹配&,不匹配1或a | \W | \W | \W | \W | 77 | | \n | 换行符 | \n 匹配一个新行 | 不支持 | 不支持 | \n | \n | 78 | | \s | 单个空白字符 | x\sx 匹配x x,不匹配xx | 不支持 | 不支持 | \s | \s | 79 | | \S | 单个非空白字符 | x\S\x 匹配xkx,不匹配xx | 不支持 | 不支持 | \S | \S | 80 | | \r | 回车 | \r 匹配回车 | 不支持 | 不支持 | \r | \r | 81 | | \t | 横向制表符 | \t 匹配一个横向制表符 | 不支持 | 不支持 | \t | \t | 82 | | \v | 垂直制表符 | \v 匹配一个垂直制表符 | 不支持 | 不支持 | \v | \v | 83 | | \f | 换页符 | \f 匹配一个换页符 | 不支持 | 不支持 | \f | \f | 84 | -------------------------------------------------------------------------------- /example/demo1: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | NUM=10 4 | 5 | printf "输出数字$NUM\n" 6 | echo $NUM -------------------------------------------------------------------------------- /example/demo10: -------------------------------------------------------------------------------- 1 | a=1.5 2 | 3 | let "b = $a + 1.3" # 错误 4 | # t2.sh: let: b = 1.5 + 1.3: syntax error in expression (error token is ".5 + 1.3") 意为表达式错误(错误的符号".5 + 1.3") 5 | 6 | echo "b = $b" # b=1 -------------------------------------------------------------------------------- /example/demo11: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | a=24 4 | b=47 5 | 6 | if [ "$a" -eq 24 ] && [ "$b" -eq 47 ] 7 | then 8 | echo "Test #1 succeeds." 9 | else 10 | echo "Test #1 fails." 11 | fi 12 | 13 | # 错误: if [ "$a" -eq 24 && "$b" -eq 47 ] 14 | #+ 这会尝试执行' [ "$a" -eq 24 ' 15 | #+ 然后会因没找到匹配的']'而失败. 16 | # 17 | # 注意: if [[ $a -eq 24 && $b -eq 24 ]]也可以. 18 | # 双方括号的if-test比 19 | #+ 单方括号的结构更灵活. 20 | # (第17行和第6行的"&&"有不同的意思.) 21 | # 多谢Stephane Chazelas指出这一点. 22 | 23 | 24 | if [ "$a" -eq 98 ] || [ "$b" -eq 47 ] 25 | then 26 | echo "Test #2 succeeds." 27 | else 28 | echo "Test #2 fails." 29 | fi 30 | 31 | 32 | # -a和-o选项提供 33 | #+ 混合条件测试另一个选择. 34 | # 多谢Patrick Callahan指出这一点. 35 | 36 | 37 | if [ "$a" -eq 24 -a "$b" -eq 47 ] 38 | then 39 | echo "Test #3 succeeds." 40 | else 41 | echo "Test #3 fails." 42 | fi 43 | 44 | 45 | if [ "$a" -eq 98 -o "$b" -eq 47 ] 46 | then 47 | echo "Test #4 succeeds." 48 | else 49 | echo "Test #4 fails." 50 | fi 51 | 52 | 53 | a=rhino 54 | b=crocodile 55 | if [ "$a" = rhino ] && [ "$b" = crocodile ] 56 | then 57 | echo "Test #5 succeeds." 58 | else 59 | echo "Test #5 fails." 60 | fi 61 | 62 | exit 0 -------------------------------------------------------------------------------- /example/demo12: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Variables: 赋值和替换 4 | 5 | a=375 6 | hello=$a 7 | 8 | #------------------------------------------------------------------------- 9 | # =号的左右两边都不能有空白符. 10 | # 如果有一个空白符会怎么样? 11 | # 12 | # 如果用 "VARIABLE =value", 13 | # ^ 14 | #+ 脚本会以为"VARIABLE"是一个命令并且此命令带了一个参数"=value"。 15 | # 16 | # 如果用 "VARIABLE= value", 17 | #   ^ 18 | #+ 脚本会以为"value"是一个命令, 19 | #+ 并且把环境变量"VARIABLE"赋为空值:""。 20 | #------------------------------------------------------------------------- 21 | 22 | 23 | echo hello # 没有引用变量,只是输出字符串 "hello". 24 | 25 | echo $hello 26 | echo ${hello} # 这句和上面的一句一样 27 | 28 | echo "$hello" 29 | echo "${hello}" 30 | 31 | echo 32 | 33 | hello="A B C D" 34 | echo $hello # A B C D 35 | echo "$hello" # A B C D 36 | # 正如你所看到的:echo $hello和echo "$hello"产生不同的输出。 37 | #  ^ ^ 38 | # 把变量引起来会保留空白字符. 39 | 40 | echo 41 | 42 | echo '$hello' # $hello 43 | # ^ ^ 44 | # 在单引号中的变量引用会被禁止, 45 | #+ 字符"$"会仅仅被认为是一个普通的字符,而不是变量的前缀. 46 | 47 | # 注意不同引用的不同效果. 48 | 49 | 50 | hello= # Setting it to a null value. 51 | echo "\$hello (null value) = $hello" 52 | # 注意具有null值的变量不等同于废弃(unset)此变量 53 | #+ 虽然最后的结果都是一样的(看下面的). 54 | 55 | # -------------------------------------------------------------- 56 | 57 | # 在同一行里用空白字符隔开为多个变量赋值是可以的。 58 | # 59 | # 警告:这可能减少可读性,并且可能是不可移植的。 60 | 61 | var1=21 var2=22 var3=$V3 62 | echo 63 | echo "var1=$var1 var2=$var2 var3=$var3" 64 | 65 | # 在老版本的sh中这可能会引起问题 66 | 67 | # -------------------------------------------------------------- 68 | 69 | echo; echo 70 | 71 | numbers="one two three" 72 | # ^ ^ 73 | other_numbers="1 2 3" 74 | # ^ ^ 75 | # 如果给变量赋的值中有空白字符,引号是必须的。 76 | # 77 | echo "numbers = $numbers" 78 | echo "other_numbers = $other_numbers" # other_numbers = 1 2 3 79 | echo 80 | 81 | echo "uninitialized_variable = $uninitialized_variable" 82 | # 未初始化的变量具有null值 (即是没有值). 83 | uninitialized_variable= # 声明,但没有初始化它 -- 84 | #+ 这就好像上面一样给它设置一个null 值 85 | echo "uninitialized_variable = $uninitialized_variable" 86 | # 它仍然是null值. 87 | 88 | uninitialized_variable=23 # 赋值 89 | unset uninitialized_variable # 销毁变量. 90 | echo "uninitialized_variable = $uninitialized_variable" 91 | # 结果仍然是null值. 92 | echo 93 | 94 | exit 0 -------------------------------------------------------------------------------- /example/demo13: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # escaped.sh: 转义字符 3 | 4 | echo; echo 5 | 6 | echo "\v\v\v\v" # 打印出 \v\v\v\v literally. 7 | # 用带着选项-e的'echo'会打印出转义字符串. 8 | echo "=============" 9 | echo "VERTICAL TABS" 10 | echo -e "\v\v\v\v" # 打印四个垂直的制表符. 11 | echo "==============" 12 | 13 | echo "QUOTATION MARK" 14 | echo -e "\042" # 打印出字符" (引号, 它的八进制ASCII码为42). 15 | echo "==============" 16 | 17 | # 当使用像$'\X'的结构时,-e选项是多余的. 18 | echo; echo "NEWLINE AND BEEP" 19 | echo $'\n' # 新行. 20 | echo $'\a' # 警告 (蜂鸣). 21 | 22 | echo "===============" 23 | echo "QUOTATION MARKS" 24 | # 版本2开始Bash已经允许使用$'\nnn'结构了. 25 | # 注意在这里,'\nnn'表示一个八进制的值. 26 | echo $'\t \042 \t' # Quote (") framed by tabs. 27 | 28 | # 使用$'\xhhh'结构也可以使用十六进制数来转义. 29 | echo $'\t \x22 \t' # Quote (") framed by tabs. 30 | # 多谢Greg Keraunen指出这个.. 31 | # 早期的Bash版本允许用'\x022'.(译者注,现在不行了) 32 | echo "===============" 33 | echo 34 | 35 | 36 | # 用ASCII码值把字符赋给变量. 37 | # ---------------------------------------- 38 | quote=$'\042' # 引号"被赋给变量quote了. 39 | echo "$quote This is a quoted string, $quote and this lies outside the quotes." 40 | 41 | echo 42 | 43 | # 用连串的ASCII码把一串字符赋给变量.. 44 | triple_underline=$'\137\137\137' # 137是字符'_'的ASCII码. 45 | echo "$triple_underline UNDERLINE $triple_underline" 46 | 47 | echo 48 | 49 | ABC=$'\101\102\103\010' # 101, 102, 103分别是A, B, C字符的八进制ASCII码. 50 | echo $ABC 51 | 52 | echo; echo 53 | 54 | escape=$'\033' # 033是ESC的ASCII码的八进制值 55 | echo "\"escape\" echoes as $escape" 56 | # 不可见的输出. 57 | 58 | echo; echo 59 | 60 | exit 0 -------------------------------------------------------------------------------- /example/demo14: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo hello 4 | echo $? # 因为上一条命令执行成功,打印0. 5 | 6 | lskdf # 无效命令. 7 | echo $? # 因为上面的无效命令执行失败,打印一个非零的值. 8 | 9 | echo 10 | 11 | exit 113 # 返回113状态码给shell. 12 | # 可以运行脚本结束后立即执行命令"echo $?" 检验. 13 | 14 | # 依照惯例,命令'exit 0'表示执行成功, 15 | #+ 当产生一个非零退出值时表示一个错误或是反常的条件。 -------------------------------------------------------------------------------- /example/demo15: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | file="demo15" 3 | if [ -r $file ] 4 | then 5 | echo "文件可读" 6 | else 7 | echo "文件不可读" 8 | fi 9 | if [ -w $file ] 10 | then 11 | echo "文件可写" 12 | else 13 | echo "文件不可写" 14 | fi 15 | if [ -x $file ] 16 | then 17 | echo "文件可执行" 18 | else 19 | echo "文件不可执行" 20 | fi 21 | if [ -f $file ] 22 | then 23 | echo "文件为普通文件" 24 | else 25 | echo "文件为特殊文件" 26 | fi 27 | if [ -d $file ] 28 | then 29 | echo "文件是个目录" 30 | else 31 | echo "文件不是个目录" 32 | fi 33 | if [ -s $file ] 34 | then 35 | echo "文件不为空" 36 | else 37 | echo "文件为空" 38 | fi 39 | if [ -e $file ] 40 | then 41 | echo "文件存在" 42 | else 43 | echo "文件不存在" 44 | fi -------------------------------------------------------------------------------- /example/demo16: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | a=4 4 | b=5 5 | 6 | # 这儿的"a"和"b"既能被看成是整数也能被看着是字符串。 7 | # 在字符串和数字之间不是严格界定的, 8 | #+ 因为Bash变量不是强类型的。 9 | 10 | # Bash允许变量的整数操作和比较, 11 | #+ 但这些变量必须只包括数字字符. 12 | # 不管怎么样,你应该小心考虑. 13 | 14 | echo 15 | 16 | if [ "$a" -ne "$b" ] 17 | then 18 | echo "$a is not equal to $b" 19 | echo "(arithmetic comparison)" 20 | fi 21 | 22 | echo 23 | 24 | if [ "$a" != "$b" ] 25 | then 26 | echo "$a is not equal to $b." 27 | echo "(string comparison)" 28 | # "4" != "5" 29 | # ASCII 52 != ASCII 53 30 | fi 31 | 32 | # 在这个实际的例子中,"-ne"和"!="都能工作. 33 | 34 | echo 35 | 36 | exit 0 -------------------------------------------------------------------------------- /example/demo17: -------------------------------------------------------------------------------- 1 | 2 | # Bash版本信息: 3 | 4 | for n in 0 1 2 3 4 5 5 | do 6 | echo "BASH_VERSINFO[$n] = ${BASH_VERSINFO[$n]}" 7 | done 8 | 9 | # BASH_VERSINFO[0] = 3 # 主版本号. 10 | # BASH_VERSINFO[1] = 00 # 次版本号. 11 | # BASH_VERSINFO[2] = 14 # 补丁级. 12 | # BASH_VERSINFO[3] = 1 # 编译版本. 13 | # BASH_VERSINFO[4] = release # 发行状态. 14 | # BASH_VERSINFO[5] = i386-redhat-linux-gnu # 结构体系 15 | # (和变量$MACHTYPE相同). 16 | 17 | # 安装在系统里的Bash版本 18 | echo $BASH_VERSION 19 | 20 | # 在目录堆栈里面最顶端的值 21 | echo $DIRSTACK -------------------------------------------------------------------------------- /example/demo18: -------------------------------------------------------------------------------- 1 | # 子串提取 2 | stringZ=abcABC123ABCabc 3 | # 0123456789..... 4 | # 以0开始计算. 5 | 6 | echo ${stringZ:0} # abcABC123ABCabc 7 | echo ${stringZ:1} # bcABC123ABCabc 8 | echo ${stringZ:7} # 23ABCabc 9 | echo ${stringZ:7:3} # 23A 10 | # 提取的子串长为3 11 | 12 | 13 | 14 | # 有没有可能从字符串的右边结尾处提取? 15 | 16 | echo ${stringZ:-4} # abcABC123ABCabc 17 | # 默认是整个字符串,就相当于${parameter:-default}. 18 | # 然而. . . 19 | 20 | echo ${stringZ:(-4)} # Cabc 21 | echo ${stringZ: -4} # Cabc 22 | # 这样,它可以工作了. 23 | # 圆括号或附加的空白字符可以转义$position参数. 24 | 25 | 26 | #------------------------------------------------------------------------- 27 | # 28 | # expr "$string" : '\($substring\)' 29 | # 从$string字符串左边开始提取由$substring描述的正则表达式的子串。 30 | # 31 | #------------------------------------------------------------------------- 32 | 33 | 34 | stringZ=abcABC123ABCabc 35 | # └┈┈┈┈┈┈┈┈┈┈┈┈┈┈ 36 | 37 | # 下面被我注释的例子在我 Mac上报错,这个看上去是 bash 的版本问题 38 | # echo `expr match "$stringZ" '\(.[b-c]*[A-Z]..[0-9]\)'` # abcABC1 39 | echo `expr "$stringZ" : '\(.[b-c]*[A-Z]..[0-9]\)'` # abcABC1 40 | echo `expr "$stringZ" : '\(.......\)'` # abcABC1 41 | # 上面的每个echo都打印相同的结果. -------------------------------------------------------------------------------- /example/demo19: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # substring-extraction.sh 3 | 4 | String=23skidoo1 5 | # 012345678 Bash 6 | # 123456789 awk 7 | # 注意上面两个程序对索引的不同处理: 8 | # Bash把字符串的第一个字符的标号称为'0'. 9 | # Awk把字符串的第一个字符的标号称为'1'. 10 | 11 | echo ${String:2:4} # position 3 (0-1-2), 4 characters long 12 | # skid 13 | 14 | # 在awk中与Bash的${string:pos:length}等同的是substr(string,pos,length). 15 | echo | awk '{ 16 | print substr("'"${String}"'",3,4) # skid 17 | }' 18 | # 用一个空的"echo"由管道传一个空的输入给awk, 19 | #+ 这样就不必提供一个文件名给awk. 20 | 21 | exit 0 -------------------------------------------------------------------------------- /example/demo2: -------------------------------------------------------------------------------- 1 | 2 | echo hello; echo there 3 | 4 | filename='demo2' 5 | 6 | if [ -x "$filename" ]; then # 注意:"if" and "then"需要分隔符 7 | # 思考一下这是为什么? 8 | echo "File $filename exists."; cp $filename $filename.bak 9 | else 10 | echo "File $filename not found."; touch $filename 11 | fi; echo "File test complete." -------------------------------------------------------------------------------- /example/demo20: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # 检查一些系统环境变量 4 | # 这是很好的预防性防护. 5 | # 例如,如果$USER(用户在控制台的名字)没有被设置, 6 | #+ 系统将不接受你. 7 | 8 | : ${HOSTNAME?} ${USER?} ${HOME?} ${MAIL?"MAIL变量没有定义,这里是我自定义的哦"} 9 | echo 10 | echo "Name of the machine is $HOSTNAME." 11 | echo "You are $USER." 12 | echo "Your home directory is $HOME." 13 | echo "Your mail INBOX is located in $MAIL." 14 | echo 15 | echo "If you are reading this message," 16 | echo "critical environmental variables have been set." 17 | echo 18 | echo 19 | 20 | # ------------------------------------------------------ 21 | 22 | # ${variablename?}结构也能检查一个脚本中变量的设置情况 23 | # 24 | 25 | ThisVariable=Value-of-ThisVariable 26 | # 注意,顺便说一下字符串变量可能被设置成不允许的字符 27 | # 28 | : ${ThisVariable?} 29 | echo "Value of ThisVariable is $ThisVariable". 30 | echo 31 | echo 32 | 33 | 34 | : ${ZZXy23AB?"ZZXy23AB has not been set."} 35 | # 如果变量ZZXy23AB没有被设置, 36 | #+ 则脚本会打印一个错误信息而结束。 37 | 38 | # 你可以指定错误信息. 39 | # : ${variablename?"ERROR MESSAGE"} 40 | 41 | 42 | # 等同于: dummy_variable=${ZZXy23AB?} 43 | # dummy_variable=${ZZXy23AB?"ZXy23AB has not been set."} 44 | # 45 | # echo ${ZZXy23AB?} >/dev/null 46 | 47 | # 使用命令"set -u"后,再比较这些检查变量是否被设置的方法的不同... 48 | # 49 | 50 | 51 | 52 | echo "You will not see this message, because script already terminated." 53 | 54 | HERE=0 55 | exit $HERE # 不会执行到这儿. 56 | 57 | # 实际上,这个脚本会返回退出状态码为1(echo $?). -------------------------------------------------------------------------------- /example/demo21: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 变量替换和"usage"信息 3 | 4 | : ${1?"Usage: $0 ARGUMENT"} 5 | # 如果没有提供命令行参数则脚本在这儿就退出了, 6 | #+ 并打印了错误信息. 7 | # usage-message.sh: 1: Usage: usage-message.sh ARGUMENT 8 | 9 | echo "These two lines echo only if command-line parameter given." 10 | echo "command line parameter = \"$1\"" 11 | 12 | exit 0 # 仅在命令行参数提供时,才会在这儿退出. 13 | 14 | # 分别检查有命令行参数和没有命令行参数时的退出状态。 15 | # 如果有命令行参数,则"$?"为0. 16 | # 否则, "$?"为1. -------------------------------------------------------------------------------- /example/demo22: -------------------------------------------------------------------------------- 1 | 2 | echo `basename $PWD` # 当前工作目录的基本名字. 3 | echo "${PWD##*/}" # 当前工作目录的基本名字. 4 | echo 5 | echo `basename $0` # 脚本名. 6 | echo $0 # 脚本名. 7 | echo "${0##*/}" # 脚本名. 8 | echo 9 | filename=test.data 10 | echo "${filename##*.}" # data 11 | # 文件的扩展名. -------------------------------------------------------------------------------- /example/demo23: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | func1 () 4 | { 5 | echo This is a function. 6 | } 7 | 8 | declare -f # 列出上面的函数. 9 | 10 | echo 11 | 12 | declare -i var1 # var1是一个整数. 13 | var1=2367 14 | echo "var1 declared as $var1" 15 | var1=var1+1 # 整数声明后,不需要使用'let'. 16 | echo "var1 incremented by 1 is $var1." 17 | # 试图将已声明为整数的变量的值更改为浮点值. 18 | echo "Attempting to change var1 to floating point value, 2367.1." 19 | var1=2367.1 # 引起一个错误信息,此变量的值保持原样. 20 | echo "var1 is still $var1" 21 | 22 | echo 23 | 24 | declare -r var2=13.36 # 'declare'允许设置变量的属性, 25 | #+ 同时也给变量赋值. 26 | echo "var2 declared as $var2" # 试图更改只读变量的值. 27 | var2=13.37 # 引起错误,并且从脚本退出. 28 | 29 | echo "var2 is still $var2" # 这行不会被执行. 30 | 31 | exit 0 # 脚本不会从这儿退出. -------------------------------------------------------------------------------- /example/demo24: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # 每次调用$RANDOM都会返回不同的随机整数. 4 | # 范围为: 0 - 32767 (带符号的16位整数). 5 | 6 | MAXCOUNT=10 7 | count=1 8 | 9 | echo 10 | echo "$MAXCOUNT random numbers:" 11 | echo "-----------------" 12 | while [ "$count" -le $MAXCOUNT ] # 产生10($MAXCOUNT)个随机整数. 13 | do 14 | number=$RANDOM 15 | echo - $number 16 | let "count += 1" # 增加计数. 17 | done 18 | echo "-----------------" 19 | 20 | # 如果你需要某个范围的随机整数,可以使用取模操作符. 21 | # (事实上,这不是一个非常好的办法。理由请见man 3 rand) 22 | # 取模操作会返回除法的余数. 23 | 24 | RANGE=500 25 | 26 | echo 27 | 28 | number=$RANDOM 29 | let "number %= $RANGE" 30 | # ^^ 31 | echo "Random number less than $RANGE --- $number" 32 | 33 | echo 34 | 35 | 36 | 37 | 38 | #------------------ 39 | # 40 | # 产生二元值,即"真"或"假"。 41 | # 42 | #------------------ 43 | BINARY=2 44 | T=1 45 | number=$RANDOM 46 | 47 | let "number %= $BINARY" 48 | #注意 let "number >>= 14" 会产生更平均的随机分布 49 | #(译者注:正如在man手册里提到的,更高位的随机分布更平均, 50 | #+ (除了最后的二元值右移出所有的值)。 51 | #取模操作使用低位来产生随机会相对不平均) 52 | if [ "$number" -eq $T ] 53 | then 54 | echo "TRUE" 55 | else 56 | echo "FALSE" 57 | fi 58 | 59 | echo 60 | 61 | 62 | 63 | #------------------ 64 | # 65 | # 模拟掷骰子。 66 | # 67 | #------------------ 68 | SPOTS=6 # 模除 6 会产生 0 - 5 之间的值。 69 | # 结果增1会产生 1 - 6 之间的值。 70 | # 多谢Paulo Marcel Coelho Aragao的简化。 71 | die1=0 72 | die2=0 73 | # 这会比仅设置SPOTS=7且不增1好?为什么会好?为什么会不好? 74 | 75 | # 单独地掷每个骰子,然后计算出正确的机率。 76 | 77 | let "die1 = $RANDOM % $SPOTS +1" # 掷第一个。 78 | let "die2 = $RANDOM % $SPOTS +1" # 掷第二个。 79 | # 上面的算术式中,哪个操作符优先计算 -- 80 | #+ 取模 (%) 还是 加法 (+)? 81 | 82 | let "throw = $die1 + $die2" 83 | echo "Throw of the dice = $throw" 84 | echo "$die1 , $die2" 85 | 86 | exit 0 -------------------------------------------------------------------------------- /example/demo25: -------------------------------------------------------------------------------- 1 | #--------------------- 2 | # 产生 6 和 30之间的随机数。 3 | #--------------------- 4 | rnumber=$((RANDOM%25+6)) 5 | 6 | echo 1: $rnumber 7 | 8 | #--------------------- 9 | # 产生在 6 - 30 之间的随机数, 10 | #+ 但随机数还同时要满足被3整除。 11 | #--------------------- 12 | rnumber=$(((RANDOM%30/3+1)*3)) 13 | 14 | echo 2: $rnumber 15 | 16 | #--------------------- 17 | # 注意这将始终不能工作。 18 | # 如果$RANDOM返回0,它就会失败。 19 | #--------------------- 20 | 21 | rnumber=$(( RANDOM%27/3*3+6 )) 22 | 23 | echo 3: $rnumber 24 | 25 | 26 | #--------------------- 27 | # 通过 date 生成随机数 28 | #--------------------- 29 | 30 | echo "The number of days since the year's beginning is `date +%j`." 31 | # 需要在调用格式的前边加上一个 '+' 号。 32 | # %j 给出今天是本年度的第几天。 33 | 34 | echo "The number of seconds elapsed since 01/01/1970 is `date +%s`." 35 | # %s 将产生从 "UNIX 元年" 到现在为止的秒数, 36 | # yields number of seconds since "UNIX epoch" began, 37 | #+ 但是这东西有用么? 38 | 39 | prefix=temp 40 | suffix=$(date +%s) # 'date'命令的 "+%s" 选项是 GNU-特性. 41 | filename=$prefix.$suffix 42 | echo $filename 43 | # 这是一种非常好的产生 "唯一" 的临时文件的办法, 44 | #+ 甚至比使用 $$ 都强. 45 | 46 | # 如果想了解 'date' 命令的更多选项, 请查阅这个命令的 man 页. 47 | 48 | exit 0 -------------------------------------------------------------------------------- /example/demo26: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 用((...))结构来使用C风格操作符来处理变量. 3 | 4 | 5 | echo 6 | 7 | (( a = 23 )) # 以C风格来设置一个值,在"="两边可以有空格. 8 | echo "a (initial value) = $a" 9 | 10 | (( a++ )) # C风格的计算后自增. 11 | echo "a (after a++) = $a" 12 | 13 | (( a-- )) # C风格的计算后自减. 14 | echo "a (after a--) = $a" 15 | 16 | 17 | (( ++a )) # C风格的计算前自增. 18 | echo "a (after ++a) = $a" 19 | 20 | (( --a )) # C风格的计算前自减. 21 | echo "a (after --a) = $a" 22 | 23 | 24 | 25 | 26 | ######################################################## 27 | # 注意,就像在C中一样,计算前自增自减和计算后自增自减有一点不同的的副作用 28 | # 29 | 30 | n=1; let --n && echo "True" || echo "False" # False 31 | n=1; let n-- && echo "True" || echo "False" # True 32 | 33 | # 多谢Jeroen Domburg. 34 | ######################################################## 35 | 36 | echo 37 | 38 | (( t = a<45?7:11 )) # C风格的三元计算. 39 | echo "If a < 45, then t = 7, else t = 11." 40 | echo "t = $t " # Yes! -------------------------------------------------------------------------------- /example/demo27: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 列出所有的行星名称. 3 | 4 | for planet in Mercury Venus Earth Mars Jupiter Saturn Uranus Neptune Pluto 5 | do 6 | echo $planet # 每个行星被单独打印在一行上. 7 | done 8 | 9 | echo 10 | 11 | for planet in "Mercury Venus Earth Mars Jupiter Saturn Uranus Neptune Pluto" 12 | # 所有的行星名称打印在同一行上. 13 | # 整个'list'只有一个变量,用""封成一个变量. 14 | do 15 | echo $planet 16 | done 17 | 18 | 19 | 20 | 21 | # ----------------------------- 22 | # 分配行星的名字和它距太阳的距离 23 | # ----------------------------- 24 | 25 | for planet in "Mercury 36" "Venus 67" "Earth 93" "Mars 142" "Jupiter 483" 26 | do 27 | set -- $planet # Parses variable "planet" and sets positional parameters。 28 | # "--" 将防止$planet为空,或者是以一个破折号开头。 29 | 30 | # 可能需要保存原始的位置参数,因为它们被覆盖了。 31 | # 一种方法就是使用数组, 32 | # original_params=("$@") 33 | 34 | echo "$1 $2,000,000 miles from the sun" 35 | #-------two tabs---把后边的0和$2连接起来 36 | done 37 | 38 | 39 | # ------------------------------------------ 40 | # 使用两种方法来调用这个脚本,一种是带参数的情况,另一种不带参数. 41 | #+ 观察此脚本的行为各是什么样的? 42 | # ------------------------------------------ 43 | 44 | echo 45 | echo =================== 46 | 47 | for a 48 | do 49 | echo -n "$a ====" 50 | done 51 | 52 | # 没有[list],所以循环将操作'$@' 53 | #+ (包括空白的命令参数列表). 54 | 55 | echo;echo; 56 | 57 | 58 | # ------------------------------------------ 59 | #+ [list]是由命令替换产生的. 60 | # ------------------------------------------ 61 | 62 | NUMBERS="9 7 3 8 37.53" 63 | 64 | for number in `echo $NUMBERS` # for number in 9 7 3 8 37.53 65 | do 66 | echo -n "$number " 67 | done 68 | 69 | echo 70 | 71 | 72 | # ------------------------------------------ 73 | # 列出系统上的所有用户 74 | # ------------------------------------------ 75 | 76 | echo;echo; 77 | 78 | PASSWORD_FILE=/etc/passwd 79 | n=1 # User number 80 | 81 | for name in $(awk 'BEGIN{FS=":"}{print $1}' < "$PASSWORD_FILE" ) 82 | # 域分隔 = : ^^^^^^ 83 | # 打印出第一个域 ^^^^^^^^ 84 | # 从password文件中取得输入 ^^^^^^^^^^^^^^^^^ 85 | do 86 | echo "USER #$n = $name" 87 | let "n += 1" 88 | done 89 | 90 | 91 | # USER #1 = root 92 | # USER #2 = bin 93 | # USER #3 = daemon 94 | # ... 95 | # USER #30 = bozo 96 | 97 | 98 | 99 | # ------------------------------------------ 100 | # 在目录的所有文件中查找源字串 101 | # ------------------------------------------ 102 | 103 | echo;echo; 104 | 105 | directory=/usr/bin/ 106 | fstring="Free Software Foundation" # 查看那个文件中包含FSF. 107 | 108 | for file in $( find $directory -type f -name '*' | sort ) 109 | do 110 | strings $file | grep "$fstring" | sed -e "s%$directory%%" 111 | # 在"sed"表达式中, 112 | #+ 我们必须替换掉正常的替换分隔符"/", 113 | #+ 因为"/"碰巧是我们需要过滤的字串之一. 114 | # 如果不用"%"代替"/"作为分隔符,那么这个操作将失败,并给出一个错误消息.(试试) 115 | done 116 | 117 | 118 | # ------------------------------------------ 119 | # 将目录中的符号连接文件名保存到一个文件中 120 | # ------------------------------------------ 121 | 122 | 123 | exit 0 124 | 125 | -------------------------------------------------------------------------------- /example/demo28: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 将目录中的符号连接文件名保存到一个文件中 3 | 4 | OUTFILE=symlinks.list # 保存的文件 5 | 6 | directory=${1-`pwd`} 7 | # 如果没有其他的特殊指定, 8 | #+ 默认为当前工作目录. 9 | 10 | 11 | echo "symbolic links in directory \"$directory\"" > "$OUTFILE" 12 | echo "---------------------------" >> "$OUTFILE" 13 | 14 | for file in "$( find $directory -type l )" # -type l 为寻找类型为符号链接的文件 15 | do 16 | echo "$file" 17 | done | sort >> "$OUTFILE" # 循环的输出 -------------------------------------------------------------------------------- /example/demo29: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | LIMIT=19 # 上限 3 | 4 | echo 5 | echo "Printing Numbers 1 through 20 (but not 3 and 11)." 6 | 7 | a=0 8 | 9 | while [ $a -le "$LIMIT" ] 10 | do 11 | a=$(($a+1)) 12 | 13 | if [ "$a" -eq 3 ] || [ "$a" -eq 11 ] # Excludes 3 and 11. 14 | then 15 | continue # 跳过本次循环剩下的语句. 16 | fi 17 | 18 | echo -n "$a " # 在$a等于3和11的时候,这句将不会执行. 19 | done -------------------------------------------------------------------------------- /example/demo3: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 测试字符串范围 3 | 4 | echo; echo "Hit a key, then hit return." 5 | read Keypress 6 | 7 | case "$Keypress" in 8 | [[:lower:]] ) echo "Lowercase letter";; 9 | [[:upper:]] ) echo "Uppercase letter";; 10 | [0-9] ) echo "Digit";; 11 | * ) echo "Punctuation, whitespace, or other";; 12 | esac # 允许字符串的范围出现在[]中, 13 | #+ 或者POSIX风格的[[中. 14 | 15 | # 在这个例子的第一个版本中, 16 | #+ 测试大写和小写字符串使用的是 17 | #+ [a-z] 和 [A-Z]. 18 | # 这种用法将不会在某些特定的场合或Linux发行版中正常工作. 19 | # POSIX 风格更具可移植性. 20 | 21 | # 练习: 22 | # -------- 23 | # 就像这个脚本所表现的,它只允许单次的按键,然后就结束了. 24 | # 修改这个脚本,让它能够接受重复输入, 25 | #+ 报告每个按键,并且只有在"X"被键入时才结束. 26 | # 暗示: 将这些代码都用"while"循环圈起来. 27 | 28 | exit 0 -------------------------------------------------------------------------------- /example/demo30: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # 未经处理的地址资料 4 | 5 | clear # 清屏. 6 | 7 | echo " 我的简历" 8 | echo " ------- " 9 | echo "下面通过shell脚本输出我的简历" 10 | echo 11 | echo "[B]asicinfo, 基本信息" 12 | echo "[E]ducation, 教育经历" 13 | echo "[I]tskill, IT 技能" 14 | echo 15 | read person 16 | case "$person" in 17 | # 注意,变量是被引用的. 18 | "B" | "b" ) 19 | # 接受大写或小写输入. 20 | echo 21 | echo "小弟调调" 22 | echo "手 机 : 136*****13" 23 | echo "E-mail : wowohoo@qq.com" 24 | echo "首 页 : http://JSLite.io" 25 | ;; 26 | # 注意,在每个选项后边都需要以;;结尾. 27 | 28 | "E" | "e" ) 29 | # 接受大写或小写输入. 30 | echo "■ 2003年9月 到 2006年8月" 31 | echo "----------------------------" 32 | echo "› 学校 : 野鸟高中" 33 | echo "› 专业 : 艺术类" 34 | echo "› 学历 : 高中" 35 | ;; 36 | 37 | # 后边的Smith和Zane的信息在这里就省略了. 38 | 39 | "I" | "i" ) 40 | # 接受大写或小写输入. 41 | echo "■ 2003年9月 到 2006年8月" 42 | echo "----------------------------" 43 | echo "› 技能:HTML | 使用时间:N | 掌握程度:精通 | 说明:这…我写出来好吗?" 44 | echo "› 技能:CSS | 掌握程度:精通 | 说明:我选择使用Stylus写CSS" 45 | echo "› 技能:git | 掌握程度:熟练" 46 | ;; 47 | # 后边的Smith和Zane的信息在这里就省略了. 48 | * ) 49 | # 默认选项. 50 | # 空输入(敲RETURN). 51 | echo 52 | echo "没有数据!" 53 | ;; 54 | 55 | esac 56 | 57 | echo 58 | 59 | # 练习: 60 | # -------- 61 | # 修改这个脚本,让它能够接受多输入, 62 | #+ 并且能够显示多个地址. 63 | 64 | exit 0 -------------------------------------------------------------------------------- /example/demo31: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | PS3='选择你喜欢的蔬菜: ' # 设置提示符字串. 4 | 5 | echo 6 | select vegetable in "豆" "胡萝卜" "土豆" "洋葱" "芜菁甘蓝" 7 | do 8 | echo 9 | echo "你最喜欢的蔬菜是 $vegetable 。" 10 | echo "讨厌!" 11 | echo 12 | break # 如果这里没有'break'会发生什么? 13 | done 14 | exit 0 -------------------------------------------------------------------------------- /example/demo32: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | PS3='选择你喜欢的蔬菜: ' 4 | echo 5 | choice_of(){ 6 | select vegetable 7 | # [in list] 被忽略, 所以'select'用传递给函数的参数. 8 | do 9 | echo 10 | echo "你最喜欢的蔬菜是 $vegetable。" 11 | echo "讨厌!" 12 | echo 13 | break 14 | done 15 | } 16 | 17 | choice_of "豆" "米饭" "胡萝卜" "土豆" "洋葱" "芜菁甘蓝" 18 | # $1 $2 $3 $4 $5 $6 19 | # 传递给choice_of() 函数的参数 20 | 21 | exit 0 -------------------------------------------------------------------------------- /example/demo33: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | JUST_A_SECOND=1 4 | 5 | funky () 6 | { # 这是一个最简单的函数. 7 | echo "This is a funky function." 8 | echo "Now exiting funky function." 9 | } # 函数必须在调用前声明. 10 | 11 | 12 | fun () 13 | { # 一个稍复杂的函数. 14 | i=0 15 | REPEATS=30 16 | 17 | echo 18 | echo "And now the fun really begins." 19 | echo 20 | 21 | sleep $JUST_A_SECOND # 嘿, 暂停一秒! 22 | while [ $i -lt $REPEATS ] 23 | do 24 | echo "----------FUNCTIONS---------->" 25 | echo "<------------ARE-------------" 26 | echo "<------------FUN------------>" 27 | echo 28 | let "i+=1" 29 | done 30 | } 31 | 32 | # 现在,调用两个函数. 33 | 34 | funky 35 | fun 36 | 37 | exit 0 -------------------------------------------------------------------------------- /example/demo34: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | # 3 | # 汉诺塔(The Towers Of Hanoi) 4 | # Bash script 5 | # Copyright (C) 2000 Amit Singh. All Rights Reserved. 6 | # http://hanoi.kernelthread.com 7 | # 8 | # 在bash version 2.05b.0(13)-release下测试通过 9 | # 10 | # 经过作者同意后在"Advanced Bash Scripting Guide"书中使用 11 | # 12 | # 由ABS的作者做了少许修改. 13 | 14 | #=================================================================# 15 | # 汉诺塔是由Edouard Lucas提出的数学谜题 , 16 | #+ 他是19世纪的法国数学家. 17 | # 18 | # 有三个直立的柱子竖在地面上. 19 | # 第一个柱子有一组的盘子套在上面. 20 | # 这些盘子是平整的,中间带着孔, 21 | #+ 因此它们才能套在柱子上面. 22 | # 这组盘子有不同的直径,它们是依照直径从小到大来从高到低放置. 23 | # 24 | # 最小的盘在最高,最大的盘在最底部. 25 | # 26 | # 现在的任务是要把这一组的盘子从一个柱子全部地搬到另一个柱子上. 27 | # 28 | # 你只能一次从一个柱子上移动一个盘子到另一个柱子. 29 | # 允许把盘子重新移回到它原来的最初位置. 30 | # 你可以把一个小的盘子放在大的盘子上面, 31 | #+ 但不能把大的盘子放在小的盘子上面. 32 | # 请注意这一点. 33 | # 34 | # 对于这一组盘子,数量少时,只需要移动很少的次数就能达到要求. 35 | #+ 但随着这组盘子的数量的增加, 36 | #+ 移动的次数几乎成倍增长的, 37 | #+ 而移动的策略变得愈加复杂. 38 | # 39 | # 想了解更多的信息, 请访问 http://hanoi.kernelthread.com. 40 | # 41 | # 42 | # ... ... ... 43 | # | | | | | | 44 | # _|_|_ | | | | 45 | # |_____| | | | | 46 | # |_______| | | | | 47 | # |_________| | | | | 48 | # |___________| | | | | 49 | # | | | | | | 50 | # .--------------------------------------------------------------. 51 | # |**************************************************************| 52 | # #1 #2 #3 53 | # 54 | #=================================================================# 55 | 56 | 57 | E_NOPARAM=66 # 没有参数传给脚本. 58 | E_BADPARAM=67 # 传给脚本的盘子数不合法. 59 | Moves= # 保存移动次数的全局变量. 60 | # 这儿修改了原脚本. 61 | 62 | dohanoi() { # 递归函数. 63 | case $1 in 64 | 0) 65 | ;; 66 | *) 67 | dohanoi "$(($1-1))" $2 $4 $3 68 | echo move $2 "-->" $3 69 | let "Moves += 1" # 这儿修改了原脚本. 70 | dohanoi "$(($1-1))" $4 $3 $2 71 | ;; 72 | esac 73 | } 74 | 75 | case $# in 76 | 1) 77 | case $(($1>0)) in # 至少要有一个盘子. 78 | 1) 79 | dohanoi $1 1 3 2 80 | echo "Total moves = $Moves" 81 | exit 0; 82 | ;; 83 | *) 84 | echo "$0: illegal value for number of disks"; 85 | exit $E_BADPARAM; 86 | ;; 87 | esac 88 | ;; 89 | *) 90 | echo "usage: $0 N" 91 | echo " Where \"N\" is the number of disks." 92 | exit $E_NOPARAM; 93 | ;; 94 | esac 95 | 96 | # 练习: 97 | # --------- 98 | # 1) 从现在这个位置以下的命令会不会总是被执行? 99 | # 为什么? (容易) 100 | # 2) 解释这个可运行的"dohanoi"函数的原理. 101 | # (难) -------------------------------------------------------------------------------- /example/demo4: -------------------------------------------------------------------------------- 1 | script_name=`basename $0` 2 | echo "The name of this script is $script_name." 3 | 4 | textfile_listing=`ls *` 5 | # 变量中包含了当前工作目录下所有的*文件 6 | echo $textfile_listing -------------------------------------------------------------------------------- /example/demo5: -------------------------------------------------------------------------------- 1 | let "t1 = ((5 + 3, 7 - 1, 15 - 4))" 2 | echo "t1 = $t1" # t1 = 11 3 | 4 | let "t2 = ((a = 9, 15 / 3))" # 初始化"a"并求"t2"的值. 5 | echo "t2 = $t2 a = $a" # t2 = 5 a = 9 -------------------------------------------------------------------------------- /example/demo6: -------------------------------------------------------------------------------- 1 | # Bash在版本2.02引入了"**"求幂操作符. 2 | let "z=5**3" 3 | echo "z = $z" # z = 125 4 | 5 | # 求模(它返回整数整除一个数后的余数) 6 | let "y=5 % 3" 7 | echo "y = $y" # y = 2 8 | -------------------------------------------------------------------------------- /example/demo7: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # gcd.sh: 最大公约数 3 | # 用Euclid运算法则 4 | 5 | # 两个整数的"最大公约数" 6 | #+ 是能被这两个整数整除的大最整数. 7 | 8 | # Euclid运算法则采用逐次除法. 9 | # 每一次都重新赋值, 10 | #+ 被除数 <--- 除数 11 | #+ 除数 <--- 余数 12 | #+ 直到 余数 = 0. 13 | #+ 最后被传递的值中:最大公约数 = 被除数. 14 | # 15 | # 关于Euclid运算法则的讨论有一个出色的讨论, 16 | # 访问Jim Loy的网站, http://www.jimloy.com/number/euclids.htm. 17 | 18 | 19 | # ------------------------------------------------------ 20 | # 参数检查 21 | ARGS=2 22 | E_BADARGS=65 23 | 24 | if [ $# -ne "$ARGS" ] 25 | then 26 | echo "Usage: `basename $0` first-number second-number" 27 | exit $E_BADARGS 28 | fi 29 | # ------------------------------------------------------ 30 | 31 | 32 | gcd () 33 | { 34 | 35 | dividend=$1 # 随意赋值. 36 | divisor=$2 #+ 这里在两个参数赋大的还是小的都没有关系. 37 | # 为什么? 38 | 39 | remainder=1 # 如果在循环中使用未初始化的变量, 40 | #+ 在循环中第一个传递值会使它返回一个错误信息 41 | # 42 | 43 | until [ "$remainder" -eq 0 ] 44 | do 45 | let "remainder = $dividend % $divisor" 46 | dividend=$divisor # 现在用最小的两个数字来重复. 47 | divisor=$remainder 48 | done # Euclid运算法则 49 | 50 | } # 最后的$dividend变量值就是最大公约数. 51 | 52 | 53 | gcd $1 $2 54 | 55 | echo; echo "GCD of $1 and $2 = $dividend"; echo 56 | 57 | 58 | # 练习: 59 | # -------- 60 | # 检测命令行参数以确保它们是整数, 61 | #+ 如果不是整数则给出一个适当的错误信息并退出脚本. 62 | 63 | exit 0 -------------------------------------------------------------------------------- /example/demo8: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 用10种不同的方法计数到11 3 | 4 | n=1; echo -n "输出1=>$n " # 输出1 5 | 6 | let "n = $n + 1" # let "n = n + 1"也可以 7 | echo -n "输出2=>$n " # 输出2 8 | 9 | 10 | : $((n = $n + 1)) 11 | # ":"是需要的, 12 | #+ 否则Bash会尝试把"$((n = $n + 1))"作为命令运行 13 | echo -n "输出3=>$n " # 输出3 14 | 15 | (( n = n + 1 )) 16 | # 上面是更简单的可行的办法 17 | echo -n "输出4=>$n " # 输出4 18 | 19 | n=$(($n + 1)) 20 | echo -n "输出5=>$n " # 输出5 21 | 22 | : $[ n = $n + 1 ] 23 | # ":"是需要的, 24 | #+ 否则Bash会尝试把"$[ n = $n + 1 ]"作为命令运行。 25 | # 即使"n"被当作字符串来初始化也能工作。 26 | echo -n "输出6=>$n " # 输出6 27 | 28 | n=$[ $n + 1 ] 29 | # 即使"n"被当作字符串来初始化也能工作。 30 | #* 应避免这种使用这种结构,因为它是被废弃并不可移植的。 31 | echo -n "输出7=>$n " # 输出7 32 | 33 | # 现在是C风格的增加操作。 34 | 35 | let "n++" # let "++n"也可以。 36 | echo -n "输出8=>$n " # 输出8 37 | 38 | (( n++ )) # (( ++n )也可以。 39 | echo -n "输出9=>$n " # 输出9 40 | 41 | : $(( n++ )) # : $(( ++n ))也可以。 42 | echo -n "输出10=>$n " # 输出10 43 | 44 | : $[ n++ ] # : $[ ++n ]]也可以。 45 | echo -n "输出11=>$n " # 输出11 46 | 47 | echo 48 | 49 | exit 0 -------------------------------------------------------------------------------- /example/demo9: -------------------------------------------------------------------------------- 1 | a=2147483646 2 | echo "a1 = $a" # a = 2147483646 3 | let "a+=1" # 把变量"a"的值自增一。 4 | echo "a2 = $a" # a = 2147483647 5 | let "a+=1000000000" # 再自增"a"一次,超过这个限制。 6 | 7 | # 到2.05b版本为止,Bash支持64位的整数。 8 | echo "a3 = $a" # a = -2147483648 9 | # 错误 (溢出) 10 | # 新的版本中不再错误 -------------------------------------------------------------------------------- /idoc.yml: -------------------------------------------------------------------------------- 1 | site: Shell Tutorial 2 | logo: "./logo.svg" 3 | favicon: "./logo.svg" 4 | 5 | menus: 6 | Home: index.html 7 | Regex: shell-regex.html 8 | Sponsor: https://jaywcjlove.github.io/#/sponsor 9 | 10 | editButton: 11 | label: Edit this page on GitHub 12 | url: https://github.com/jaywcjlove/shell-tutorial/blob/master/ 13 | footer: | 14 | Released under the MIT License. Copyright © 2022 Kenny Wong
15 | Generated by idoc v{{idocVersion}} -------------------------------------------------------------------------------- /logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/package.json", 3 | "private": true, 4 | "name": "shell-tutorial", 5 | "description": "Shell Tutorial 入门教程(Shell tutorial book)", 6 | "version": "1.0.0", 7 | "scripts": { 8 | "start": "idoc --watch", 9 | "build": "idoc" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "https://github.com/jaywcjlove/shell-tutorial" 14 | }, 15 | "keywords": [ 16 | "Shell", 17 | "tutorial" 18 | ], 19 | "dependencies": { 20 | "idoc": "^1.26.6" 21 | } 22 | } --------------------------------------------------------------------------------