logger 参数列表过长_[源码级解析] 巧妙解决并深度分析Linux下rm命令提示参数列表过长的问题...

0ab300abbac2abdd5d39ae90987c07af.png
在维护实习单位服务器的过程中,偶然发现一个有350万文件的文件夹需要清理,于是我习惯性执行了rm -rf ./*,却在数秒后被告知“参数列表过长”。在一番折腾过后,我终于通过取巧的办法完成了这一任务,也随着相关内核源码的阅读,了解到了关于Linux Shell的一些有趣特性。
本文原载于未命名小站,由作者本人同步至知乎,转载请注明原作者博客地址或本链接,谢谢!

0x01

最初我以为是rm命令对文件数量存在限制,但当我尝试ls ./*touch ./*等命令都遇到这一问题之后,我开始将注意力放在Bash本身上。也许是通配符的限制。

突然,我想起来rm命令支持管道送入参数,也许可以通过这样的办法变相完成任务。于是我在另一个目录做了个测试:

$ touch 123-1
$ touch 123-2
$ echo "123-1 123-2" | xargs rm

这两个文件果然消失。

于是我尝试使用find将目录下面所有文件列出:

find . -name "*" | more         # 使用more避免350多万个文件把终端挤崩溃
find . -name "*" | wc -l        # 大致了解文件个数

输出的文件个数与我通过ls -l命令输出的个数基本一致,果然输出了所有文件。接下来要做的就是将这些文件送到rm中。

0x02

但事实并非如此简单,当我执行以下命令,以为可以一口气顺利删除所有文件的时候,我傻眼了:

$ find . -name "*" | xargs rm
rm: log: No such file or directory
rm: 20190601-110204.log: No such file or directory
... # 所有待删除文件均发生报错

我重新观察文件名,发现文件名格式均为log yyyymmdd-hhmmss.log,众所周知Bash靠空格分割参数,文件名被传入rm的时候照着空格被截断,成为了两个文件名,难怪删除失败!

0x03

吸取教训,我使用了一个新的参数:

find . -name "*" -print0 | xargs -0 rm

注意这一参数里的-print0-0,这是为了区分空格与分界符,加入参数后此前用于分隔参数的空格(0x0a)则会变成NUL(0x00),这一参数的效果可以通过16进制查看器体现:

$ ls
123 321 
123 322
$ find . -name "12*" > 1.log        
$ find . -name "12*" -print0 > 2.log
$ hexdump -C 1.log     
00000000  2e 2f 31 32 33 20 33 32  31 0a 2e 2f 31 32 33 20  |./123 321../123 |
00000010  33 32 32 0a                                       |322.|
00000014
$ hexdump -C 2.log
00000000  2e 2f 31 32 33 20 33 32  31 00 2e 2f 31 32 33 20  |./123 321../123 |
00000010  33 32 32 00                                       |322.|
00000014

可以发现在两个不同输出模式下,分隔符不一样。默认的分隔符与空格一致,即0x0a,但当开启-print0模式后,分隔符变成了0x00,配合管道接收端的-0参数将NUL字符正确解析成参数定界符,则可以完成带空格文件名的正常解析。

解决了这一问题,我们再次执行,问题随即解决。

0x04

过了两天,又有一台服务器需要删除大量文件,且领导要求只删除文件不删除里面的目录,我一看,又是400多万个文件。但在之前的折腾过程中,我早有准备。

find命令默认开启递归模式,如果只删除文件不删除目录,只需要配置递归深度为1即可:

find . -maxdepth 1 -name "*" -print0 | xargs -0 rm

执行命令后再执行ls -l,发现问题果然解决,所有目录完好无损。

0x05

快速解决完问题后,我一看离下班还有好一阵子,便开始琢磨Bash内通配符的长度限制到底从哪来。

我首先找了另一个有大量文件的目录开始实验,换用zsh进行ls ./*操作,得到的确是一样结果。看来该长度限制与Bash无关。

我突然想起来曾经看过的一个安全类视频:Youtube - Bash injection without letters or numbers - 33c3ctf hohoho (misc 350) - LiveOverFlow,其中解释了通配符(Wildcard)的原理。

当我们输入*的时候,Shell所做的是列举出满足通配符规则的所有文件,并以空格分割,然后送进Shell。举例而言,如果你有一个全是txt文件的目录,你直接执行*,就会发现以下错误:

$ touch abc.txt
$ touch bbc.txt
$ *
bash: command not found: abc.txt
$ echo *
abc.txt bbc.txt

相信看到这里,大家都明白通配符的行为以及为什么上述示例会报错,在上述示例中,Shell将abc.txt看做命令,而将bbc.txt看做参数。这也说明了通配符的行为:将满足条件的文件输出为文本(并默认输出到Shell)。

0x06

当我们继续向下挖掘,我们会想到Shell执行命令的本质:exec()类系统调用。这一限制如果并非来自于Shell(因为find . -name "*"并不会报错),那么就一定来自于系统调用。而一个Shell命令被执行的第一站则是exec()及其六个实际调用:execl(),execle(),execlp(),execv(),execvp(),execve()

于是我们使用strace工具来检查所有系统调用:

$ ls
1234.txt
123.txt
$ strace ls *
execve("/usr/bin/ls", ["ls", "1234.txt", "123.txt"], [/* 28 vars */]) = 0
...

看到这里,相信读者已经心里有数了,我们的命令与参数都被作为execve()函数的第二个参数,以数组形式被传入。考虑到数组默认存储在栈中,该限制是否来自于Shell对栈空间的限制呢?

我查阅了Linux的源码,在fs/exec.c:478中找到了我要的内容:源码

static int prepare_arg_pages(struct linux_binprm *bprm,struct user_arg_ptr argv, struct user_arg_ptr envp)
{unsigned long limit, ptr_size;bprm->argc = count(argv, MAX_ARG_STRINGS);if (bprm->argc < 0)return bprm->argc;bprm->envc = count(envp, MAX_ARG_STRINGS);if (bprm->envc < 0)return bprm->envc;/** Limit to 1/4 of the max stack size or 3/4 of _STK_LIM* (whichever is smaller) for the argv+env strings.* This ensures that:*  - the remaining binfmt code will not run out of stack space,*  - the program will have a reasonable amount of stack left*    to work from.*/limit = _STK_LIM / 4 * 3;limit = min(limit, bprm->rlim_stack.rlim_cur / 4);/** We've historically supported up to 32 pages (ARG_MAX)* of argument strings even with small stacks*/limit = max_t(unsigned long, limit, ARG_MAX);/** We must account for the size of all the argv and envp pointers to* the argv and envp strings, since they will also take up space in* the stack. They aren't stored until much later when we can't* signal to the parent that the child has run out of stack space.* Instead, calculate it here so it's possible to fail gracefully.*/ptr_size = (bprm->argc + bprm->envc) * sizeof(void *);if (limit <= ptr_size)return -E2BIG;limit -= ptr_size;bprm->argmin = bprm->p - limit;return 0;
}

从完整的注释中我们可以得知,限制最大参数长度的参数叫做ARG_MAX,而且其大小为栈的1/4(可能是为了保证参数以外还有多的空间可以存储其他数据)。当然,如果你是个考古爱好者,你会发现在2.6版本内核(低于2.6.22)的include/linux/limits.h文件中,ARG_MAX是一个写死的常量 :考古链接。

关于ARG_MAX我们可以通过Linux下的getconf命令来获取,这是一个获取Linux下所有全局变量/常量的命令:

$ getconf ARG_MAX
2097152

我们再查询当前配置的栈大小:

$ ulimit -s
8192

ARG_MAX参数的单位是Byte,ulimit -s命令的单位是MB,可以看到当前最大参数数量的确是栈空间的1/4。那如果我们把栈空间增大呢?

$ ulimit -s 81920
$ ulimit -s
81920
$ getconf ARG_MAX
20971520

可以看到,允许的最大参数数量立马随着栈空间增大而同步增大。这个时候我们再来删除之前那个大目录,就不会出现『参数列表过长』的错误提示了。

实际上这一限制在大多数现代操作系统中均存在(例如MacOS、Windows等),可参考此处获得更多信息:ARG_MAX, maximum length of arguments for a new process

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/503690.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

collect的功能是什么?其底层如何实现的?_为什么你要用 Spring ?

前言现在Spring几乎成为了Java在企业级复杂应用开发的代名词&#xff0c;得益于Spring简单的设计哲学和其完善的生态圈&#xff0c;确实为廉颇老矣&#xff0c;尚能饭否的 Java 带来了“春天”&#xff0c;有很多同学刚接触Java就直接从Spring框架开始学习&#xff0c;导致产生…

2.3.0配置 spark_配置scala 2.11.12的spark-2.3.0 maven依赖项的问题

我在尝试在POM.xml中为spark-scala应用程序设置maven denpendency时遇到问题 .我在用 &#xff1a;SCALA版本$ scalaWelcome to Scala 2.11.8 (OpenJDK 64-Bit Server VM, Java 1.8.0_162).SPARK版本&#xff1a;$ ./spark-shellWelcome to SPARK version 2.3.0Using Scala ver…

m3u8合并mp4软件_m3u8格式转mp4究极办法!

你们来这个号这么久了&#xff01;还没给你们分享过一些实用的干货。打今天起这个公众号将给大家推荐一些APP和实用的小软件和一些小教程。生命太短&#xff0c;没时间留给遗憾。若不是终点&#xff0c;都不要把自己留在原地&#xff0c;请一直微笑向前&#xff01;我是帮忙坏哥…

android 左移动画_android旋转动画和平移动画详解,补充说一下如果制作gif动画放到csdn博客上...

先上效果图:制作过程是先起一个模拟器&#xff0c;然后把GifCam的框拖到模拟器上面&#xff0c;点击Rec的new先&#xff0c;然后点击Rec,然后就save到本地成gif文件这里做一个左右旋转&#xff0c;上下旋转&#xff0c;和左右移动的动画&#xff0c;先自己建立一个View的类&…

vm虚拟机安装包_一次Miniconda虚拟机安装的神奇踩坑记录

本人一直都是在物理机环境下使用Anaconda&#xff0c;好处是提供了比较完全的机器学习包&#xff0c;还有方便的虚拟环境&#xff0c;缺点是体积太大。但如果直接用Anaconda中的根目录环境作为pycharm中的Python解释器&#xff0c;因为在运行程序前会不断加载根目录中的Python包…

css3弧形跑道效果_Css 实现漂亮弧形

在实现页面五花八门的有特色的ui时&#xff0c;我们有时会遇到要用实现一个弧形&#xff0c;而这样的弧形要怎么实现呢&#xff1f;用图片&#xff1f;好像不大现实&#xff0c;因为这样就要无故多加载一张图片了&#xff0c;这里我们来说说怎么用css的after伪类来实现弧形。先…

python螺旋圆的绘制_python 使用turtule绘制递归图形(螺旋、二叉树、谢尔宾斯基三角形)...

插图工具使用Python内置的turtle模块&#xff0c;为什么叫这个turtle乌龟这个名字呢&#xff0c;可以这样理解&#xff0c;创建一个乌龟&#xff0c;乌龟能前进、后退、左转、右转&#xff0c;乌龟的尾巴朝下&#xff0c;它移动时就会画一条线。并且为了增加乌龟画图的艺术价值…

教室信息管理系统mysql_教师信息管理系统(方式一:数据库为oracle数据库;方式二:存储在文件中)...

方式一&#xff1a;运行截图数据库的sql语句&#xff1a;/*Navicat Oracle Data TransferOracle Client Version : 12.1.0.2.0Source Server : ORCZYTSource Server Version : 120100Source Host : localhost:1521Source Schema : C##ZYTTarget Server Type : ORACLETarget Ser…

mysql having ct_mysql中where和having子句的区别和具体用法

1.mysql中的where和having子句的区别having的用法having字句可以让我们筛选成组后的各种数据&#xff0c;where字句在聚合前先筛选记录&#xff0c;也就是说作用在group by和having字句前。而 having子句在聚合后对组记录进行筛选。SQL实例&#xff1a;1.1.显示每个地区的总人口…

Linux rcp命令教程:如何在远程主机和本地之间复制文件(附实例详解和注意事项)

Linux rcp命令介绍 rcp是remote copy的缩写&#xff0c;它是Linux系统中用于在本地主机和远程主机之间复制文件或目录的命令。虽然有更安全的方法&#xff08;如scp或rsync&#xff09;可以完成这项任务&#xff0c;但rcp命令让你以简单的方式在两台计算机之间复制文件&#x…

python圆形_Python中的圆形范围

非常感谢大家。在 我实现了我想要的crange&#xff08;参考Ni和J.F.Sebastian&#xff09;。在import math class crange: def __init__(self, start, stop, stepNone, moduloNone): if step 0: raise ValueError(crange() arg 3 must not be zero) if step is None and modul…

python实例化对象有new吗_PHP new:实例化对象

前面我们介绍了怎么定义一个类&#xff0c;但是我们要使用类中的属性和方法并不像使用变量和函数那样简单&#xff0c;首先要对类进行实例化才行&#xff0c;下面就来详细介绍一下。 实例化对象 将类实例化成对象非常容易&#xff0c;只需要使用new关键字并在后面加上一个和类名…

note.js和mysql的优劣_nginx与Node.js的优缺点是什么?

ngx相对node有这么几个较大的优点&#xff1a;1.性能更高&#xff0c;毕竟C写的&#xff0c;而且ngx的epoll是裸的&#xff0c;node的epoll是libuv封装的。2.完备的静态资源支持……这个就不说了&#xff0c;你用node写一个和ngx工作完全一致的静态资源库试试……当然npm上有现…

python实现xmind_Python xmind库(生成框架图)

小编在测试日常工作中遇到一个费时的问题&#xff0c;如何将excel中的测试用例&#xff0c;生成测试框架图&#xff1f;经过查阅发现的python xmind库 将excel中的测试用例&#xff0c;生成测试框架图&#xff0c;分为2步 1.解析excel&#xff0c;取出excel中数据&#xff08;此…

pythondistutils安装_python – 与distutils / pip一起安装Bash完成

我创建了一个简单的Python模块,并希望用pip进行分发.我还想与模块一起安装Bash完成文件.我正在使用Python 2.7.1和pip 0.8.2安装模块.我有这个setup.py&#xff1a;setup(namejenkinsmon,version0.0.1,descriptionJenkins Job Monitor,long_descriptionopen(README.txt).read()…

python代码解读软件_5种带你轻松分析Python代码的软件库

通常&#xff0c;人们会使用两种速度来衡量某种编程语言的优劣&#xff0c;即&#xff1a;开发速度和执行速度。对于Python而言&#xff0c;大家往往受益的是它能够快速地编写代码&#xff0c;而忽略了它是否能够快速地运行&#xff0c;并及时完成既定的任务。因此&#xff0c;…

python try else_python try/except/else与递归

Python版本&#xff1a;2.7。操作系统&#xff1a;Windows 10 64位。在 注意&#xff1a;我找到了一种方法来解决下面描述的问题&#xff0c;它不使用try/except/else语句。我问下面的问题仅仅是因为我很好奇为什么代码的行为方式是这样的&#xff0c;以及是否有一种方法可以使…

python闭包怎么理解_Python:闭包的理解

很多函数型的语言都有闭包这一概念&#xff0c;比如python的兄弟js。人们刚听到闭包这一概念总会觉得它很晦涩&#xff0c;难以理解。其实不然&#xff0c;主要是他的名字起得太抽象了&#xff0c;让人误以为很难。下面举一个例子&#xff1a;#coding:utf-8def foo():nums [0]…

mac ssh客户端_Electerm for Mac(ssh客户端)

Electerm for Mac是一款功能强大的&#xff0c;作为终端或ssh / sftp客户端(类似于xshell)为一体的工具&#xff0c;支持多平台(linux&#xff0c;mac&#xff0c;win)&#xff0c;还有自定义终端样式&#xff0c;全局/会话代理&#xff0c;将书签/主题/快速命令同步到github s…

alpine登陆mysql_如何构建一个php7-alpine的docker镜像

我花了大概一周的时间进行了各种踩坑及实验&#xff0c;最终得出了一份可以使用的dockerfile及compose内含如下支持php7mysql_pdopostgre_pdophpredisswoole(可选&#xff0c;如应用swoole&#xff0c;dockerfile及nginx的配置会有所变化)dockerfile分为两部分&#xff0c;一部…