公众号旧文
粘贴多行 Linux 命令时,为什么会乱成一团
在使用 Linux 终端时,我经常会遇到下面的现象。
当复制粘贴了多行命令时,比如:
ping -c 4 1.1.1.1
ping -c 4 8.8.8.8
ping -c 4 vault.centos.org
ping -c 4 130.110.193.0
结果屏幕上却变成了这样:
ping -c 4 1.1.1.1
PING 1.1.1.1 (1.1.1.1) 56(84) bytes of data.
64 bytes from 1.1.1.1: icmp_seq=1 ttl=58 time=0.815 ms
ping -c 4 8.8.8.8
ping -c 4 vault.centos.org
ping -c 4 130.110.193.064 bytes from 1.1.1.1: icmp_seq=2 ttl=58 time=0.735 ms
乍一看,好像命令被改坏了:
130.110.193.0
像是变成了:
130.110.193.064
这只是一个非常常见的终端显示现象:
输入的内容和前台程序的输出,同时出现在了同一个屏幕上。
一、终端里有三个角色
在 Linux 终端里,可以简单理解为有三个角色:
键盘输入
↓
终端
↓
前台程序
平时我们看到的命令行提示符:
[root@server ~]#
是 shell 给的提示。
比如输入:
ls
是 shell 接收了这个命令,然后执行 ls。
但是当执行:
ping -c 4 1.1.1.1
之后,情况就变了。
这时前台程序已经不是 shell,而是 ping。
也就是说,在 ping 运行期间,当前控制终端的是:
ping
而不是:
bash / shell
所以你这时候再粘贴命令,并不是马上由 shell 执行,而是会先进入终端输入缓冲区。
二、为什么命令会和 ping 输出混在一起?
假设先执行:
ping -c 4 1.1.1.1
屏幕开始输出:
64 bytes from 1.1.1.1: icmp_seq=1 ttl=58 time=0.815 ms
这时候 ping 还没结束。
然后马上粘贴了三行:
ping -c 4 8.8.8.8
ping -c 4 vault.centos.org
ping -c 4 130.110.193.0
终端会把粘贴的字符显示出来。
但与此同时,ping 1.1.1.1 还在继续输出:
64 bytes from 1.1.1.1: icmp_seq=2 ttl=58 time=0.735 ms
64 bytes from 1.1.1.1: icmp_seq=3 ttl=58 time=0.775 ms
64 bytes from 1.1.1.1: icmp_seq=4 ttl=58 time=0.733 ms
于是屏幕上就会出现这种交错:
ping -c 4 130.110.193.064 bytes from 1.1.1.1: icmp_seq=2 ttl=58 time=0.735 ms
这里真正的内容其实应该拆开看:
ping -c 4 130.110.193.0
64 bytes from 1.1.1.1: icmp_seq=2 ttl=58 time=0.735 ms
只是因为两边几乎同时往屏幕上写内容,所以视觉上粘在了一起。
这不是 IP 变成了 .064。
也不是命令被系统改写了。
只是:
130.110.193.0
后面紧接着出现了:
64 bytes from 1.1.1.1
所以看起来像:
130.110.193.064 bytes from...
三、粘贴进去的多行命令到底有没有执行?
一般来说,会执行。
但是要等前一个前台程序结束之后。
比如正在运行:
ping -c 4 1.1.1.1
又粘贴:
ping -c 4 8.8.8.8
ping -c 4 vault.centos.org
ping -c 4 130.110.193.0
那么流程是:
1. ping 1.1.1.1 正在运行
2. 你粘贴的几行命令进入终端缓冲区
3. ping 继续输出自己的结果
4. ping 结束
5. shell 重新拿回控制权
6. shell 读取刚才缓冲区里的命令
7. 后面的 ping 命令依次执行
所以看到屏幕上乱,不代表执行逻辑乱了。
它只是显示层面有点乱。
四、Debian、Ubuntu、CentOS 都会这样吗?
会。
这不是某个发行版的问题。
Debian 会这样。
Ubuntu 会这样。
CentOS 会这样。
Rocky Linux、AlmaLinux、Arch Linux 也会这样。
因为这不是 ping 特有的问题,也不是某个 Linux 发行版特有的问题,而是交互式终端的基本行为。
终端本质上只有一个屏幕输出区域。
前台程序可以往这里输出。
你粘贴的字符也会被回显到这里。
两者如果时间上重叠,就可能交错。
五、为什么有时看起来像“还没回车,命令就显示出来了”?
因为终端通常会回显你输入的字符。
所谓回显,就是你键盘输入什么,终端就把什么显示到屏幕上。
你粘贴多行命令时,本质上相当于非常快速地输入了一大堆字符,包括换行符。
所以它们会被显示出来。
但显示出来,不等于已经被 shell 执行。
尤其是在前台程序还没结束的时候,shell 还没有重新拿回控制权。
这也是为什么你会看到:
ping -c 4 8.8.8.8
ping -c 4 vault.centos.org
ping -c 4 130.110.193.0
这些内容提前出现在屏幕上,但真正执行要等上一个 ping 结束之后。
六、哪些命令最容易出现这种现象?
最典型的是会持续输出的命令,比如:
ping 1.1.1.1
journalctl -f
tail -f /var/log/syslog
top
htop
tcpdump
watch
docker logs -f xxx
这些命令运行时,会不断占用终端输出。
如果你在它们运行期间粘贴多行命令,就很容易看到输入内容和程序输出混在一起。
尤其是:
journalctl -u xxx -f
和:
tail -f xxx.log
因为它们会持续打印日志。
这时你粘贴命令,看起来会更乱。
七、这种现象危险吗?
大多数情况下不危险。
如果你粘贴的是普通查看命令,比如:
ping -c 4 8.8.8.8
ls
df -h
free -h
一般没什么问题。
但是如果你粘贴的是危险命令,就要小心了。
比如:
rm -rf /some/path
iptables -F
reboot
systemctl
restart xxx
因为你可能以为命令没有执行,实际上它已经进入了输入缓冲区,等前台程序结束后就会执行。
所以在 Linux 终端里,复制粘贴多行命令时要特别注意。
尤其是在前台还有命令没结束的时候,不要随便粘贴高风险命令。
八、怎么避免这种情况?
最简单的方法是:等上一个命令结束后再粘贴。
比如看到提示符重新出现:
[root@server ~]#
再粘贴下一组命令。
如果前台命令一直不结束,比如:
ping 1.1.1.1
没有加 -c,那就按:
Ctrl + C
先停止它,然后再粘贴后续命令。
九、更推荐的写法:用分号串起来
如果你本来就想连续执行多条命令,可以写成一行:
ping -c 4 1.1.1.1;
ping -c 4 8.8.8.8;
ping -c 4 vault.centos.org;
ping -c 4 130.110.193.0
这种写法的含义是:
不管前一个命令成功还是失败,后面的都继续执行。
适合做简单测试。
十、如果希望前一个成功后才执行后一个,用 &&
比如:
ping -c 4 1.1.1.1 && ping -c 4 8.8.8.8 && ping -c 4 vault.centos.org && ping -c 4 130.110.193.0
只有前一个命令成功,才继续执行后一个。
比如 ping 1.1.1.1 成功了,才会继续 ping 8.8.8.8。
如果中间某个失败,后面就不继续执行。
十一、如果只是想让输出更清楚,可以加 echo 分隔
比如:
echo "===== ping 1.1.1.1 ====="
ping -c 4 1.1.1.1
echo "===== ping 8.8.8.8 ====="
ping -c 4 8.8.8.8
echo "===== ping vault.centos.org ====="
ping -c 4 vault.centos.org
echo "===== ping 130.110.193.0 ====="
ping -c 4 130.110.193.0
这样输出会清楚很多。
你能明显看到每一段结果属于哪个命令。
十二、总结
同时复制粘贴多行 Linux 命令时,屏幕上出现命令和输出混在一起,是非常常见的现象。
它的本质不是命令被改了,也不是系统执行乱了,而是:
粘贴的字符在终端里被回显
前台程序也在往终端输出
两者共用同一个屏幕
所以视觉上交错在一起
尤其是在运行 ping、tail -f、journalctl -f、tcpdump 这类持续输出命令时,更容易看到这种情况。
记住一句话就行:
显示乱,不代表执行乱;但前台程序没结束时,不要随便粘贴危险命令。