Shell脚本学习指南-第二章-入门
第2章 入门
2.1 脚本语言 vs 编译型语言
| 类型 | 代表语言 | 特点 |
|---|---|---|
| 编译型 | C/C++、Java、Pascal | 源码编译成机器码;执行速度快;偏向底层字节、数字处理,做文件操作麻烦 |
| 脚本解释型 | Shell、awk、Python、Perl | 由解释器逐行读取执行;开发快,擅长文件目录;性能偏弱,大部分场景够用 |
shell优势:POSIX标准化,各大UNIX/Linux都自带,一次编写,多处运行。三大优点:简单、可移植、开发快。
2.2 第一个简单脚本
需求:统计当前登录用户数量
交互式命令:
|
|
开发流程:先命令行测试 → 写入文件 → 增加执行权限 → 运行脚本
2.3 #! 幻数行(脚本第一行)
内核读取脚本文件开头两个字符
#!,后面是解释器完整路径,告诉内核用哪个程序执行本脚本。
示例:
#!/bin/sh:指定使用sh解释器执行脚本- 末尾的
-安全用途,防御欺骗攻击。
其他解释器例子:
⚠注意坑点
#!这一行有字符长度限制,尽量控制在64字符以内。- 必须写解释器完整绝对路径;不同系统路径不一样。
- 老系统内核不识别
#!,shell会自动降级调用/bin/sh。 - POSIX标准没有定义
#!,属于系统扩展功能。
2.4 Shell基础元素
2.4.1 命令、选项、参数
命令行格式:命令 选项 参数
- 选项以
-开头;短选项可以合并:ls -lt等价ls -l -t --:双横杠,代表选项结束,后面全部当做文件名参数。;:同一行多条命令分隔,顺序执行。&:放到命令末尾,放到后台执行,不等命令结束继续往下跑。
shell三类命令
- 内建命令:shell程序本身实现,不创建新进程。
cd read echo printf test。 - shell函数:shell语言编写,像命令一样调用,第6章讲解。
- 外部命令:磁盘上可执行程序。执行流程:
- fork创建子进程
- 在
$PATH目录列表搜索命令;如果命令带/(比如./nusers)跳过PATH查找 - 子进程替换为目标程序执行
- 程序结束,回到父shell。
2.4.2 Shell变量
变量:给一段字符串起名字;shell变量全部保存字符串,可以是空。
规则:
- 变量名:字母/下划线开头,后面字母数字下划线。
- 赋值:
变量=值,等号两边绝对不能有空格! - 取值:
$变量名;复杂场景${变量名}。 - 变量值包含空格,必须使用双引号包裹。
示例
2.4.3 echo输出(可移植性差)
不同系统echo行为不一样,
‑n、转义字符行为实现各不相同。简单输出可以用;复杂输出优先选printf。
| 转义序列 | 含义 |
|---|---|
\a |
蜂鸣告警 |
\b |
退格 |
\c |
停止输出,不输出换行 |
\n |
换行 |
\t |
制表tab |
|
|
2.4.4 printf输出(推荐,POSIX标准,跨平台)
语法:
|
|
重点:printf不会自动输出换行!需要手动写\n。
%s字符串占位%d数字占位 模板用完参数还没处理完,会循环复用模板。
示例
|
|
- 格式串里的占位符
%s %d,按顺序,依次消耗后面的参数。 - ⚠特殊规则:参数比占位符多的时候,会循环复用格式字符串! 这个是 shell printf 非常容易踩坑的点。
2.5 I/O重定向与管道
三个标准流:
标准输入stdin、标准输出stdout、标准错误stderr;默认绑定终端。
过滤器:读取标准输入,输出标准输出的程序。
重定向符号
| 符号 | 作用 |
|---|---|
< file |
把程序标准输入改为来自file文件 |
> file |
标准输出重定向到file;文件存在直接覆盖! |
>> file |
标准输出追加到file末尾,不覆盖原有内容 |
cmd1 cmd2 |
管道:cmd1 标准输出 → cmd2 标准输入 |
示例
管道技巧:尽量把过滤数据的操作放管道前面,减少后续程序处理的数据量。例如先grep筛选,再sort排序。
两个特殊文件
/dev/null位桶:写入的数据全部丢弃;读取立刻返回EOF。
/dev/tty:永远指向当前终端。适合输入密码场景,即使标准输入被重定向,仍然从键盘读取。
tr命令速查
| 选项 | 说明 |
|---|---|
-c |
取反(字节层面) |
-C |
取反(字符locale层面,部分系统不支持) |
-d |
删除字符,不做替换 |
-s |
压缩连续重复字符为1个 |
tr只处理标准输入输出,不接收文件名参数,必须配合
<重定向。
2.6 PATH命令查找路径
$PATH:冒号分隔目录列表,shell在这里搜索外部命令。
实践:建立个人$HOME/bin存放自己写的脚本
永久生效写进
~/.profile。
⚠安全提示:不要把当前目录.放在PATH,有安全风险。
2.7 位置参数(脚本命令行参数)
脚本执行 ./finduser betsy,betsy 就是第一个参数。
$1第1个参数,$2第2个- 参数大于9,必须大括号
${10}
示例脚本 finduser
运行:./finduser betsy
如果不给参数,
$1为空,grep直接报错。第6章学习判断参数是否存在。
2.8 脚本调试:执行跟踪 set -x
set -x:打开跟踪,执行每条命令前打印这条命令,前面带+set +x:关闭跟踪
两种使用方式
- 命令行调试脚本:
- 写在脚本内部:
2.9 locale 国际化本地化
locale影响排序、字符集、时间、货币格式。写可移植脚本经常设置
LC_ALL=C强制ASCII传统行为。
环境变量优先级:LC_ALL > 各个LC_* > LANG
| 变量 | 用途 |
|---|---|
| LANG | 总默认设置 |
| LC_ALL | 最高优先级,全部覆盖,脚本常用LC_ALL=C sort |
| LC_COLLATE | 排序规则(sort受它影响) |
| LC_CTYPE | 字符集,字母、标点识别 |
| LC_MESSAGES | 程序提示报错消息语言 |
| LC_TIME | 时间日期格式 |
查看本机支持哪些locale:
C/POSIXlocale:传统ASCII模式,写shell脚本推荐用,行为稳定,不受系统语言干扰。
本章速查表
| 语法/命令 | 简单说明 | 示例 |
|---|---|---|
#!/bin/sh - |
脚本开头幻数行 | #!/bin/sh - |
var=value |
变量赋值,等号无空格 | name="hello shell" |
$var ${var} |
读取变量 | echo "$name" |
printf "a %s\n" x |
格式化输出,必须手动\n |
printf "num:%d\n" 10 |
cmd < file |
标准输入重定向 | tr -d '\r' < in.txt |
cmd > file |
输出覆盖文件 | who > user.txt |
cmd >> file |
输出追加文件 | echo hello >> log.txt |
cmd1 cmd2 |
管道,前输出作为后输入 | who | wc -l |
/dev/null |
丢弃输出 | grep xxx f >/dev/null |
/dev/tty |
强制终端读写 | read pass </dev/tty |
$PATH |
命令搜索目录 | PATH=$PATH:$HOME/bin |
$1 ${10} |
脚本位置参数 | ./script arg1 arg2 |
set -x / set +x |
打开关闭脚本调试跟踪 | set -x; command; set +x |
LC_ALL=C |
强制C locale,脚本稳定 | LC_ALL=C sort file.txt |
小结
- 变量赋值
=两边不能空格。 echo各个系统行为不一致,复杂输出优先printf,记得加\n换行。> file会直接覆盖原有文件,注意不要误删数据;追加用>>。- 管道尽量先过滤再排序,减少数据量,提升效率。
- 脚本参数大于9,用
${10},不能写$10。 - 排序、正则字符范围受locale影响,可移植脚本加上
LC_ALL=C。 - 外部命令带斜杠
./xxx不会走PATH搜索。
文章作者 会写代码的小郎中
上次更新 2011-07-15
许可协议 CC BY-NC-ND 4.0