重定向
标准输入重定向(STDIN,文件描述符为0)
- 默认从键盘输入,也可从其他文件或命令中输入标准输出重定向(STDOUT,文件描述符为1)
- 默认输出到屏幕错误输出重定向(STDERR,文件描述符为2)
- 默认输出到屏幕
输出重定向
- 输出重定向:
将执行的命令所输出的内容,重定向到指定的文件内
输出重定向符号分为以下几种
正确输出重定向
命令 > 文件
-正确输出重定向(覆盖)
命令 1> 文件
-重定向中的标准输出模式,可以省略文件描述符1不写
命令 >> 文件
-正确输出重定向(追加)
错误输出重定向
命令 2> 文件
-错误输入重定向(覆盖)
错误输出模式的文件描述符2是必须要写的
命令 2>> 文件
-错误输入重定向(追加)
共同输出重定向
命令 &> 文件
-正确和错误输出重定向到同一个文件(覆盖)
另一种写法
命令 > 文件 2>&1
命令 &>> 文件
-正确和错误输出重定向到同一个文件(追加)
另一种写法
命令 >> 文件 2>&1
2>&1
是一个固定的格式,是覆盖还是追加,取决于前面的重定向是覆盖还是追加
正确输出重定向
将原本要显示在屏幕上的内容(命令返回的状态码为0
),输出到指定的文件中一定要注意自己是需要追加还是覆盖,将文件内容覆盖,相当于自己把文件内容删了,毕竟
Linux 一切皆文件
,执行重定向操作前,要有备份
的习惯,误操作覆盖了,也能还原原来的内容
- 利用正确输出重定向生成一个新的文件
- 当我们不使用
> study.txt
这一部分,只使用echo 'hello world'
,回车后,会在显示器上返回hello world
这一段字符- 完成后,执行
cat study.txt
就可以看到文件的内容是hello world
- 但是如果存在
study.txt
这个文件时,多次执行下面的命令,文件内永远都只有hello world
这一段内容
echo 'hello world' > study.txt
- 将正确输出重定向追加到指定的文件
- 执行了下面的命令后,我们查看(
cat study.txt
) 文件时,发现里面有两行内容,一行是前面覆盖
重定向写入的hello world
,另一行就是这个时候插入的good luck, have fun
这段内容- 每执行一次下面的命令,study.txt 文件内就会增加一行
good luck, have fun
- 如果执行上面的
echo 'hello world' > study.txt
,那 study.txt 文件内就只有hello world
一行内容了
echo 'good luck, have fun' >> study.txt
配合
<<EOF
实现静态写入多行内容- 一般写入多行内容,都会使用
vim
或者vi
这类文本编辑器去操作,但是写 shell 就需要去规避这一类的人为干预,重定向配合<<EOF
就是一个很好的方法 <<EOF
以EOF
为结尾来结束文件内容的写入
- 一般写入多行内容,都会使用
同样也是要注意是覆盖还是插入的方式
- 覆盖写入
cat <<EOF > study.txt
this is a test1
this is a test2
this is a test3
EOF
- 追加写入
cat <<EOF >> study.txt
this is a test1 too
this is a test2 too
this is a test3 too
EOF
多次执行就可以看出效果和区别
错误输出重定向
将原本要显示在屏幕上的内容(命令返回的状态码为非0
),输出到指定的文件中同样也是要注意需要使用的是覆盖还是追加
- 本机 PATH 变量没有
whoami.bak
这个命令
使用
which whoami.bak
,屏幕上会输出/usr/bin/which: no whoami.bak in (/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin)
- 假设我写脚本,需要判断一个命令是否存在,但我有强迫症,我不想看到这些报错,我只想知道有没有,可以使用下面的方式,将错误输出重定向到
/dev/null
文件内,这个文件是一个黑洞文件,所有进入到这个文件的文件或目录,都会不复存在(比如mv xxx.txt > /dev/null
相当于执行了rm -f
命令)
which whoami.bak 2> /dev/null
将上面的命令,重定向到 study.txt 文件内
- 分别为覆盖和追加,相信通过上面的操作,大家已经心里有了13数了
which whoami.bak 2> study.txt
which whoami.bak 2>> study.txt
共同输出重定向
将正确的和错误的输出都重定向到同一个文件内同样也是要注意需要使用的是覆盖还是追加
- 同样引用上面的案例,我需要去判断当前的 PATH 变量里面有没有我需要的命令,如果没有,我就执行其他的操作
$?
是用来判断上一条命令执行的状态为成功(返回状态码0
)还是不成功(返回状态码为非0
)- 重定向操作是成功的,但是命令实际是属于错误输出的,此时的
$?
返回的状态码为非0命令操作是成功的,但是重定向操作时不正常的,
$?
返回的状态码也是非0
- 比如重定向的文件名称和目录名称一样,就会返回
Is a directory
的报错
# || 在 shell 中作为判断使用,表示的是 '或者' ,|| 前后只需要有一个命令可以正确执行即可
## whoami.bak 不存在,因此会返回 true
which whoami.bak &> /dev/null || echo "true"
## whoami 存在,因此不会返回 true
which whoami &> /dev/null || echo "true"
敲黑板
- 无论是哪种输出重定向,一定要注意指向的文件,如果执行的文件是二进制文件,那二进制文件就会变成普通文件,可以通过
file
命令去比较重定向对目录类型的文件操作会返回 Is a directory 的报错
输出重定向千万不要对块设备操作,比如磁盘,会导致数据被覆盖,后果很严重
- 举个离(例)子(子) [
离谱的例子
]
备份一个 whoami 命令来玩,不能直接玩,不然命令就废了
cp /usr/bin/whoami{,.bak}
先查看 whoami.bak 文件的属性
file /usr/bin/whoami.bak
可以看到,是一个ELF
文件,属于executable
可执行文件
/usr/bin/whoami.bak: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.32, BuildID[sha1]=0cd50214a1dcf60ac187526884bb621ede7ef83a, stripped
咱们重定向一些内容给 whoami.bak追加的情况下,不会影响文件本身的类型
echo 'this is a test' >> /usr/bin/whoami.bak
覆盖的情况下,文件本身的类型就会变为
ASCII text
文本文件
echo 'this is a test' > /usr/bin/whoami.bak
所以输出重定向所指定的文件一定要注意了,不要指定错了,不然后悔都来不及
输入重定向
- 输入重定向:
从文件内将内容重定向给命令
- 这个例子比较奇葩,因为可以直接使用
cat study.txt
命令,这里是用来做一个理解使用的例子
cat < study.txt
- 一般像 mysql 需要导入 sql 文件,就可以使用输入重定向的方式
mysql -u<user_name> -p<user_passworld> -h<mysql_host> -P<mysql_port> < xxx.sql
输入输出重定向
- 输入输出重定向:
将文件的内容输入重定向给命令后,将命令输出的内容重定向到指定的文件内
命令 < 文件1 > 文件2
-将文件1的输入重定向输出到文件2
同样是一个不太恰当的例子,主要是用来理解思路的
- 将 study.txt 文件内容
输入
给cat 命令
,再将cat 命令
输出的内容,输出
到 study.txt.bak 文件内 - 实际使用
cat study.txt > study.txt.bak
就可以了
- 将 study.txt 文件内容
cat < study.txt > study.txt.bak
字符串重定向
- 字符串重定向:
将字符串输入重定向给命令
命令 <<< "字符串"
-字符串输入重定向
- 以上的输入输出重定向,都是需要针对文件的情况下操作的,但是在写脚本的时候,遇到一些变量需要被引用,但是写
echo ${xxx} | awk '{print $1}'
又觉得没有13格,这个时候,就轮到字符串重定向上场了
先定义一个变量
test='hello world'
当我只想要得到
hello
这一个内容,我又不想用 echo,那就可以用字符串重定向的方式
# --color 参数是高亮过滤到的内容,有的 linux 发行版的 grep 命令默认不带这个参数
grep --color hello <<< ${test}
# 过滤 hello,我只要输出 hello
grep -o hello <<< ${test}
# awk 的形式
awk '{print $1}' <<< ${test}
管道符
进程管道
|
表示管道符,将管道符前面操作的输出转交给管道符后面的来操作和处理同样是一个奇葩的例子,用来便于理解
- 实际直接使用
grep root /etc/passwd
就可以了
- 实际直接使用
cat /etc/passwd | grep root
123 木头人,不许说话不许动
- 通过 echo 将 sleep 3 输出,然后交给 bash 执行
- 实际直接使用
sleep 3
就可以了
echo "sleep 3" | bash
三通管道
tee
是一个命令,加上管道符,可以变成三通管道,将三通管道前面操作的输出做一个副本- 三通管道和正确输出重定向的区别在于,正确输出重定向不会在屏幕上显示输出的内容,直接写入指定的文件内;而三通管道会输出到屏幕上,并写入到指定的文件内
- 三通管道和错误输出重定向的区别在于,错误输出重定向不会屏幕上显示输出的内容,直接写入指定的文件内;而三通管道会输出到屏幕上,并不会将错误内容写入到文件内,文件内容会被空内容覆盖
所以三通管道属于
覆盖重定向
- 若为
正确输出重定向
,则将正确输出覆盖到指定文件内
- 若为
错误输出重定向
,则将清空指定文件的内容
- 若为
cat study.txt | tee study-bak.txt
参数传递
xargs
命令表示参数传递-
- xargs(英文全拼: eXtended ARGuments)是给命令传递参数的一个过滤器,也是组合多个命令的一个工具
- xargs 可以将管道或标准输入(stdin)数据转换成命令行参数,也能够从文件的输出中读取数据
- xargs 也可以将单行或多行文本输入转换为其他格式,例如多行变单行,单行变多行
- xargs 默认的命令是 echo,这意味着通过管道传递给 xargs 的输入将会包含换行和空白,不过通过 xargs 的处理,换行和空白将被空格取代
- xargs 是一个强有力的命令,它能够捕获一个命令的输出,然后传递给另外一个命令
- 之所以能用到这个命令,是由于很多命令不支持
| 进程管道
来传递参数
xargs 参数
-a
-file 从文件中读入作为 stdin
-e
-flag ,注意有的时候可能会是-E,flag必须是一个以空格分隔的标志,当xargs分析到含有flag这个标志的时候就停止
-p
-当每次执行一个argument的时候询问一次用户
-n
-num 后面加次数,表示命令在执行的时候一次用的argument的个数,默认是用所有的
-t
-表示先打印命令,然后再执行
-i
-或者是-I,这得看linux支持了,将xargs的每项名称,一般是一行一行赋值给 {},可以用 {} 代替
-r
-no-run-if-empty 当xargs的输入为空的时候则停止xargs,不用再去执行了
-s
-num 命令行的最大字符数,指的是 xargs 后面那个命令的最大命令行字符数
-L
-num 从标准输入一次读取 num 行送给 command 命令
-l
-同 -L
-d
-delim 分隔符,默认的xargs分隔符是回车,argument的分隔符是空格,这里修改的是xargs的分隔符
-x
-exit的意思,主要是配合 -s 使用
-P
-修改最大的进程数,默认是 1,为 0 时候为as many as it can
又是一个不太恰当的例子,主要用于理解
- echo 命令无法获取通过
| 进程管道
传递的内容
- echo 命令无法获取通过
ls | echo
- 使用
| xagrs
后可以获取到ls
传递的内容
ls | xargs echo
多行内容通过 xargs 变成单行内容
- 继续使用上面用到的 study.txt 文件
cat study.txt | xargs
常用的
-i
或者-I
参数,配合{}
来指定参数传递的指定位置- 在当前路径下的
.txt
文件后面加上-bak
的后缀
- 在当前路径下的
ls *.txt | xargs -i mv {}{,-bak}
{}
的位置决定了参数传递的位置
ls *.txt* | xargs -i echo "{} is a test"
ls *.txt* | xargs -i echo "test for {}"