将 result.txt 的子文件夹名称和内容打印到 .csv

use*_*109 6 command-line bash find

我有一个文件夹,其中包含多个子文件夹和子子文件夹。我想将result.txt存在于许多子文件夹或子子文件夹中的名为的文件的内容连同子文件夹的名称一起打印到 csv 文件中。

这意味着如果命名的文件result.txt

abc/def/result.txt
efg/result.txt
Run Code Online (Sandbox Code Playgroud)

然后我需要一个 csv 文件,它应该有

1. abc   content of its result.txt
2. efg    content of its result.txt
Run Code Online (Sandbox Code Playgroud)

等等。

我从以下find命令开始

find . -iname 'result.txt' "a portion of path" "content">final.csv
Run Code Online (Sandbox Code Playgroud)

我应该如何从这里开始?

注意:(2017 年 12 月 8 日)虽然以下解决方案在终端上正确显示了内容,但当我添加 >final.csv 时,它们都不起作用。如前所述,我的 result.txt 有多行。特定 result.txt 的内容会溢出到不同的单元格中,而不是在单个单元格中。有什么建议 ?

des*_*ert 8

我认为find是正确的选择:

find */ -name "result.txt" -exec bash -c 'printf "%s,%s\n" "${0%%/*}" "$(cat $0)"' {} \;
Run Code Online (Sandbox Code Playgroud)

示例运行

$ echo r1 >a/b/result.txt
$ echo r2 >c/result.txt
$ tree
.
??? a
?   ??? b
?       ??? result.txt
??? c
    ??? result.txt
$ find */ -name "result.txt" -exec bash -c 'printf "%s,%s\n" "${0%%/*}" "$(cat $0)"' {} \;
a,r1
c,r2
Run Code Online (Sandbox Code Playgroud)

说明

find命令搜索名称的当前目录中或下的每个文件,result.txt并在子 shell 中执行execprintf命令bash。该printf命令打印子目录的名称、逗号和文件内容,后跟一个\newline。如果您想将此输出写入文件,只需将 eg 附加>final.csv到命令。

更简单

steeldriver-printf建议的方法:

$ find */ -name 'result.txt' -printf '%H,' -exec cat {} \;
a/,r1
c/,r2
Run Code Online (Sandbox Code Playgroud)

这会在第一列中打印一个额外的斜线,您可以通过将输出通过管道轻松删除,例如sed 's|/,|,|'.

将多行result.txt内容合并到一个单元格中

要用例如空格替换换行符,只需在上述命令之一中替换catsed ":a;N;\$!ba;s/\n/ /g",例如

$ find */ -name "result.txt" -exec bash -c 'printf "%s,%s\n" "${0%%/*}" "$(sed ":a;N;\$!ba;s/\n/ /g" $0)"' {} \;
a,r1 r1
c,r2
Run Code Online (Sandbox Code Playgroud)

如果您想要一些其他字符串作为分隔符,请将/ /部分替换为/your_delimiter/,但保留斜线。

  • 如果(如 OP 在注释中所述)在每个顶级目录下只出现一次 `result.txt`,那么您可以将其简化为 `find */ -name 'result.txt' -printf '%H,' -exec cat {} \;` 我想 (2认同)

Zan*_*nna 5

好吧,这是一种方法(现在编辑以将换行符转换为空格,感谢Stack Overflow 上的这个答案):

shopt -s globstar
n=0; for i in **/result.txt; do sed -e ":l;N;\$!bl;s/\n/ /g; s/.*/$((++n))\. "${i%%/*}"\t&/" "$i"; done
Run Code Online (Sandbox Code Playgroud)

您可以添加重定向以写入文件

n=0; for i in **/result.txt; do sed ":l;N;\$!bl;s/\n/ /g; s/.*/$((++n))\. "${i%%/*}"\t&/" "$i"; done > outfile
Run Code Online (Sandbox Code Playgroud)

笔记

  • n=0 将变量设置为递增
  • shopt -s globstar打开递归通配以**查找此目录下目录中的所有文件(shopt -u globstar之后取消设置,或退出外壳并启动一个新的)
  • :l 为这个动作设置一个标签
  • N将两行读入模式空间(这允许我们使用\n
  • \$!如果这是文件的最后一行,则不是...我们必须转义,$因为整个命令是双引号的,以便 shell 可以扩展$i等。但这$需要完整地传递给sed,它的意思是“文件的最后一行文件”。我建议对脚本使用单引号sed除非您必须在其中传递 shell 变量。
  • bl ...分支到标签(再做一次)
  • s/old/new替换oldnew
  • s/\n/ /g 对于模式空间中的所有换行符(除了最后一个),用空格替换换行符
  • .* 任意数量的任意字符(文件中的任意内容)
  • $((++n))n随着循环的每次迭代递增
  • \.文字点(逗号没有被 特殊处理sed;它们将按字面打印)
  • "${i%%/*}"我们正在处理的文件路径中当前目录的第一个子目录的名称(去掉第一个之后的所有字符/
  • & 来自搜索部分的匹配模式(文件中的任何内容)
  • --不要将-后续参数中的引导解释为前置选项标志。这可以防止文件名开头-被解释为选项。在这种特定情况下这是不必要的,因为我们正在显式搜索result.txt并且只有具有此确切名称的文件才会传递给循环。但是,我已经包含了它,以防有人需要使用 glob 重用这个脚本。

这是一个更易读的版本,它也更便携(应该适用于 的所有版本sed),因为它使用换行符而不是;分隔命令:

#!/bin/bash

shopt -s globstar
n=0
for i in **/result.txt; do
         sed ":l      
              N        
              \$!bl     
              s/\n/ /g
              s/.*/$((++n))\.,"${i%%/*}",&/" -- "$i"
done > outfile
Run Code Online (Sandbox Code Playgroud)

  • @Zanna 如果有人可以通过 sed 弄明白,那就是你 :) 因为你很棒 (2认同)

小智 -1

我不知道如何仅使用终端命令来完成此操作,但我已经使用此线程中的 python 脚本完成了类似的操作:

/sf/ask/2635110901/

有了这个,您可以轻松添加将行写入 CSV 文件的功能:

Python 2 的https://docs.python.org/2/library/csv.html

Python 3 的https://docs.python.org/3/library/csv.html

  • 引用您所做的旧工作是很好的,并且 Python 在这里非常受欢迎,但是如果您实际上调整代码以适应特定问题,并将其发布在答案中,而不仅仅是提供链接,那就更好了 (4认同)