无法使用 bash 脚本追加到文件



这是我的代码:

#!/bin/bash -e
dirs=( * )
for f in "${dirs[@]}"
do
  while IFS= read -r line; do
     case "$line" in
       *disabled>true* )
          sed -i '1i "$f"' list.txt;;
     esac
  done < "$f/config.xml"
done

代替 sed,我也尝试了 echo 和 printf,但文件列表.txt总是空的。为什么我无法追加到文件?

echo "$f" >> list.txt;;
printf '%sn' "$f" >> list.txt;;

示例配置.xml测试文件夹下的文件:

<?xml version='1.0' encoding='UTF-8'?>
<project>
<disabled>true</disabled>
</project>

目标: 将"test"打印到list.txt中,如果test/config.xml其中有<disabled>true

为了有效地完成你在问题中使用(需要 GNU 实用程序)中尝试做的事情:

grep -FZl 'disabled>true' */*.config.xml | xargs -0 dirname | tac > list.txt

这假定您需要:

  • 仅记录在list.txt中的目录名称(如果需要(相对)文件路径,只需删除| xargs -0 dirname)。

  • 字母倒序排列 - 如果您想要升序,只需省略| tac部分即可。

解释:

  • Glob */*.config.xml有效地返回当前目录的任何子目录中config.xml文件的(相对)文件路径。

  • grep -FZl 'disabled>true'在输入文件中搜索文字 (-Fdisabled>true,并且只有在找到第一个匹配项时,才会停止搜索并打印输入文件路径 (-l ),多个路径用NUL个字符分隔。 (-Z)。

  • xargs -0 dirname 将输入(按
  • NUL0) 拆分为参数)传递给 dirname ,从而得到那些config.xml文件包含感兴趣字符串的目录的目录名

  • tac反转线条

  • > list.txt将总体结果写入文件list.txt


至于你尝试的问题

  • dirs=( * )捕获当前目录中的任何类型的文件系统项目(如果您知道当前目录仅包含目录,这可能不是问题);
    dirs=( */ )将仅将匹配限制为目录(但请注意,匹配将包括尾随/)。

  • 正如 user2021201 在他们的答案中指出的那样,sed -i '1i ...' list.txt的根本问题是,如果它开始时是(零字节)文件,则不会向list.txt添加任何内容:脚本根本不会为空输入文件执行。

    • 对于此问题,没有简单的单命令sed解决方案;对于增量追加到文件,>>是正确的选择。
  • 此外,正如Rany Albeg Wein在对该问题的评论中指出的那样,sed -i '1i "$f"' list.txt试图在单引号字符串中扩展$f,这是行不通的 - 相反,文字"$f"将被写入文件(假设文件为非空)。
    此外,通过使用1i(在第 1 行之前插入),每个新条目都将作为第一行添加,从而有效地产生包含匹配目录的list.txt

  • 如果找到匹配项后不退出 while 循环,您不仅会不必要地处理文件中的剩余行,而且还有可能多次添加手头的目录,以便在搜索字符串在多行中找到时list.txt

  • 正如user2021201在他们的答案中指出的那样,您的方法效率低下,因为(a)可以使用诸如*/*.config.xml之类的多级glob代替循环,以及(b)因为grep可用于更有效地搜索每个文件的内容,并在找到第一个匹配项后退出(使用grep -m1grep -qgrep -l, 所有语义略有不同)。

首先,请注意,您的 sed 表达式不适用于空文件。我修改了代码,它满足了您的目标:

#!/bin/bash -e
for f in */config.xml # process all config.xml files
do
  while IFS= read -r line; do
     case "$line" in
       *disabled>true* )
          # obtain directory name of the file
          # and append it to list.txt
          dirname "$f" >> list.txt 
     esac
  done < "$f"
done

但是,我宁愿使用以下方法:

#!/bin/bash -e
for f in */config.xml 
do
  # -m1 - exit at first occurence for performance
  if grep -m1 'disabled>true' "$f" >/dev/null; then
      dirname "$f" >> list.txt
  fi
done

甚至更简单:

grep -l 'disabled>true' */config.xml | cut -d/ -f1 > list.txt

最新更新