深浅模式
当我们在终端里执行一个命令时,程序通常会默认连接三个通道:
0stdin标准输入:程序从哪里读东西,默认是键盘1stdout标准输出:程序把正常结果写到哪里,默认是终端屏幕2stderr标准错误:程序把错误信息写到哪里,默认也是终端屏幕
比如执行:
bash
echo helloecho 会把 hello 显示在终端上。这里的终端屏幕,就是标准输出。
再比如执行:
bash
mysql -uroot -p123456 wolf_blog进入 MySQL 客户端后,你手动输入 SQL:
sql
SELECT * FROM user_account;这时候 MySQL 客户端就是从键盘读取内容。键盘输入,就是它的标准输入。
所以默认情况下,程序的输入输出大概是这样:
text
键盘输入 → 程序 → 终端显示而所谓“重定向”,就是把这条默认路线改掉。
<:输入重定向
< 表示输入重定向。
它的作用是:不让程序从键盘读取输入,而是从文件读取输入。
例如:
bash
mysql -uroot -p123456 wolf_blog < test.sql这条命令的意思是:把 test.sql 文件里的内容交给 MySQL 客户端执行。原本 MySQL 会等你在键盘上输入 SQL,现在它直接从 test.sql 里读取 SQL。
可以这样理解:
text
普通输入:
键盘 → mysql
输入重定向:
test.sql → mysql所以 SQL 文件导入经常会这样写:
bash
mysql -uroot -p123456 wolf_blog < test.sql这里要注意一点:< test.sql 不是 MySQL 自己处理的,而是当前 shell 处理的。
shell 会先打开当前环境下的 test.sql 文件,然后把它接到前面命令的标准输入上。MySQL 并不关心输入来自键盘还是文件,它只知道自己从标准输入里读到了 SQL 内容。
这也是 Docker 导入 SQL 时经常写成这样的原因:
bash
docker exec -i mysql mysql -uroot -p123456 wolf_blog < test.sql这条命令如果是在宿主机终端执行,那么 < test.sql 就是由宿主机 shell 处理的。它找的是宿主机当前目录下的 test.sql,不是容器里的文件。
这句话很关键:
text
重定向由当前 shell 处理,文件属于当前 shell 所在的环境。>:输出重定向
> 表示输出重定向。
它的作用是:不把正常输出显示在终端上,而是写入文件。
例如:
bash
echo hello > a.txt执行后,终端上不会显示 hello,而是会把 hello 写入 a.txt。
如果 a.txt 不存在,会创建这个文件;如果已经存在,会覆盖原来的内容。
比如:
bash
echo wolf > name.txt
echo cloud > name.txt最后 name.txt 里只会剩下:
text
cloud因为第二次 > 覆盖了第一次的内容。
数据库导出时也经常用 >:
bash
mysqldump -uroot -p123456 wolf_blog > backup.sql这条命令的意思是:把 mysqldump 生成的 SQL 内容写入 backup.sql 文件。
所以导入和导出刚好是一对反方向的操作:
bash
mysql -uroot -p123456 wolf_blog < backup.sql这是导入,把文件内容交给 MySQL 执行。
bash
mysqldump -uroot -p123456 wolf_blog > backup.sql这是导出,把命令输出写成 SQL 文件。
可以简单记成:
text
< 从文件读进来
> 向文件写出去>>:追加输出
>> 也是输出重定向,但它不会覆盖文件,而是追加到文件末尾。
例如:
bash
echo wolf >> name.txt
echo cloud >> name.txt最后 name.txt 里会有:
text
wolf
cloud所以 > 和 >> 的区别很简单:
text
> 覆盖写入
>> 追加写入日志记录里经常使用 >>,因为日志通常不希望每次都覆盖旧内容。
比如:
bash
date >> run.log每执行一次,就会把当前时间追加到 run.log 文件末尾。
2>:错误输出重定向
前面提到,程序有两种输出:
text
1 stdout 标准输出,正常结果
2 stderr 标准错误,错误信息默认情况下,它们都会显示在终端上,所以很多时候我们感觉不到区别。
但实际上,正常输出和错误输出是两条不同的通道。
> 默认重定向的是标准输出,也就是编号 1。
所以:
bash
command > out.log其实等价于:
bash
command 1> out.log如果想把错误信息单独写入文件,就要用 2>:
bash
mysql -uroot -p123456 wolf_blog < test.sql > import.log 2> error.log这条命令的意思是:
text
正常输出写入 import.log
错误输出写入 error.log这样导入大 SQL 时,如果失败了,错误信息会单独保存在 error.log 里,更方便排查。
如果只想记录错误,也可以写:
bash
mysql -uroot -p123456 wolf_blog < test.sql 2> error.log这时正常输出仍然显示在终端,错误输出会写入 error.log。
2>&1:合并正常输出和错误输出
有时候不想分两个文件,只想把正常输出和错误输出都写进同一个日志文件。
这时可以写:
bash
mysql -uroot -p123456 wolf_blog < test.sql > import.log 2>&1这里的 2>&1 意思是:把标准错误 2 重定向到标准输出 1 当前指向的位置。
前面已经写了:
bash
> import.log这表示标准输出已经写到 import.log 里了。
后面的:
bash
2>&1表示错误输出也跟着写到标准输出所在的位置,也就是 import.log。
所以整条命令的效果是:
text
正常输出 → import.log
错误输出 → import.log这个写法顺序很重要。常用写法是:
bash
command > log.txt 2>&1意思是先把标准输出指向 log.txt,再把标准错误也指向标准输出当前所在的位置。
先记住这个常用格式就够了:
bash
命令 > 日志文件 2>&1它表示:正常输出和错误输出都写进同一个日志文件。
|:管道
管道符 | 的作用是:把前一个命令的标准输出,交给后一个命令当标准输入。
例如:
bash
cat test.sql | mysql -uroot -p123456 wolf_blog这条命令的过程是:
text
cat test.sql 输出文件内容
↓
通过管道 |
↓
mysql 把这些内容当作输入执行所以它也能导入 SQL。
不过,如果只是单纯把一个文件交给命令执行,更推荐使用输入重定向:
bash
mysql -uroot -p123456 wolf_blog < test.sql因为没有必要先 cat 一次。
管道更适合多个命令组合处理。
比如查看日志时:
bash
cat app.log | grep ERROR意思是把 app.log 的内容交给 grep,过滤出包含 ERROR 的行。
如果继续统计错误行数,可以写:
bash
cat app.log | grep ERROR | wc -l这条命令的过程是:
text
读取 app.log
↓
过滤包含 ERROR 的行
↓
统计行数也就是统计日志里有多少行错误信息。
重定向和管道的区别
重定向和管道都和输入输出有关,但它们解决的问题不同。
重定向主要处理命令和文件之间的关系:
text
命令 ↔ 文件管道主要处理命令和命令之间的关系:
text
命令 ↔ 命令例如:
bash
mysql -uroot -p123456 wolf_blog < test.sql这是把文件内容作为命令输入。
bash
mysqldump -uroot -p123456 wolf_blog > backup.sql这是把命令输出写入文件。
bash
cat app.log | grep ERROR这是把一个命令的输出交给另一个命令。
所以可以简单记成:
text
重定向:改变命令从哪里读、往哪里写
管道:把一个命令的输出交给另一个命令常用场景
导入 SQL:
bash
mysql -uroot -p123456 wolf_blog < test.sql导出数据库:
bash
mysqldump -uroot -p123456 wolf_blog > backup.sql追加日志:
bash
echo "start import" >> import.log只保存错误信息:
bash
mysql -uroot -p123456 wolf_blog < test.sql 2> error.log正常输出和错误输出都保存:
bash
mysql -uroot -p123456 wolf_blog < test.sql > import.log 2>&1过滤日志:
bash
cat app.log | grep ERROR
评论