Bash中能否重复读取同一管道?解决文件删除未记录问题
问题
现有以下脚本代码:
ls | grep -E '^application--[0-9]{4}-[0-9]{2}.tar.gz$' | awk '{if($1<"application--'"${CLEAR_DATE_LEVEL0}"'.tar.gz") print $1}' | xargs -r echo ls | grep -E '^application--[0-9]{4}-[0-9]{2}.tar.gz$' | awk '{if($1<"application--'"${CLEAR_DATE_LEVEL0}"'.tar.gz") print $1}' | xargs -r rm
这段代码先筛选符合命名规则的文件并打印日志,再删除这些文件,但存在缺陷:两行命令执行期间若有新文件创建,会出现日志未记录但被删除的情况。
需求:实现同一管道的输出同时传给xargs echo(日志)和xargs rm(删除),且不使用临时文件作为缓冲区。
解决方案
以下是几种无需临时文件的实现方式:
方法1:利用tee + 进程替换
通过tee命令将awk的输出同时分流到两个进程,一个负责日志输出,一个负责删除操作:
ls | grep -E '^application--[0-9]{4}-[0-9]{2}.tar.gz$' | awk '{if($1<"application--'"${CLEAR_DATE_LEVEL0}"'.tar.gz") print $1}' | tee >(xargs -r echo) | xargs -r rm
>(xargs -r echo)是bash的进程替换语法,tee会把输出同时写入标准输出(后续传给xargs rm)和这个进程管道,实现一次筛选、两处使用。
方法2:在awk中直接完成日志与删除
借助awk的system函数,在筛选后直接打印日志并调用删除命令,减少管道层级:
ls | grep -E '^application--[0-9]{4}-[0-9]{2}.tar.gz$' | awk -v target="application--${CLEAR_DATE_LEVEL0}.tar.gz" '$1 < target { print; system("rm -- " $1) }'
- 用
-v参数将Shell变量CLEAR_DATE_LEVEL0传入awk,避免引号嵌套问题; rm --用于处理文件名以-开头的特殊情况,防止被误判为命令选项。
方法3:通过xargs调用Shell批量执行
让xargs将每个文件名传给Shell,同时执行日志和删除操作:
ls | grep -E '^application--[0-9]{4}-[0-9]{2}.tar.gz$' | awk '{if($1<"application--'"${CLEAR_DATE_LEVEL0}"'.tar.gz") print $1}' | xargs -r -I {} sh -c 'echo {}; rm -- {}'
-I {}指定替换标记,每个筛选出的文件名会替换{};sh -c用于在单个Shell进程中依次执行echo和rm命令。
内容的提问来源于stack exchange,提问作者HubertNNN
相关产品推荐
相关产品推荐

