Bash 脚本解决方案

Bash 脚本解决方案

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

这意味着如果文件result.txt位于

abc/def/result.txt
efg/result.txt

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

1. abc   content of its result.txt
2. efg    content of its result.txt

等等。

我从以下find命令开始

find . -iname 'result.txt' "a portion of path" "content">final.csv

我该如何继续?

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

答案1

我认为find是正确的选择:

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

示例运行

$ 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

解释

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

更简单

-printf建议的方法是钢铁司机

$ find */ -name 'result.txt' -printf '%H,' -exec cat {} \;
a/,r1
c/,r2

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

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

要用空格替换换行符,只需在上述命令之一中替换为,cat例如sed ":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

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

答案2

好吧,这里有一种方法(现在已编辑,可以将换行符转换为空格,这要感谢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

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

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

笔记

  • n=0设置变量以增加
  • shopt -s globstar打开递归通配符来**查找此目录下的所有文件(shopt -u globstar之后使用取消设置,或退出 shell 并启动一个新目录)
  • :l为该操作设置标签
  • N读入两行到模式空间(这允许我们使用\n
  • \$!如果这是文件的最后一行,则不行...我们必须退出,$因为整个命令是双引号以便 shell 可以展开$i等。但这$需要完整地传递给sed,它表示“文件的最后一行”。我建议使用单引号对于sed脚本,除非您必须在其中传递 shell 变量。
  • bl...分支到标签(再做一次)
  • s/old/newold用。。。来代替new
  • 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

答案3

Bash 脚本解决方案

#!/bin/bash
# If $1 is not given, find will assume cwd
print_file(){
    local inputfile="$1"
    while IFS= read -r line || [ -n "$line" ];do
        printf "%s\\" "$line"
    done < "$inputfile"
}

get_file_info(){
    local filepath="$1"
    counter=$((counter+1))
    parent=${filepath%/*}
    if [ "$parent" = "$filepath"  ]; then
        parent="."
    fi
    printf "%d,%s," "$counter" "$parent"
}

main(){
    if [ -z "$1"  ];then
        set "."
    fi

    find "$1" -type f -name "result.txt" -print0 |
    while IFS= read -r -d ''  path
    do
        get_file_info "$path"
        print_file "$path"
        printf "\n"
    done
}

main "$@"

其工作方式是,您应该将其保存为文件,例如results2csv.shchmod +x通过提供脚本的完整路径或将其放入~/bin文件夹中,运行source ~/.bashrc并通过名称调用脚本来使之可执行并运行。

此脚本的工作原理如下:

$ ./result2csv.sh things                                                    
1,things/thing2,to be or not to be\that's Boolean logic\
2,things/thing1,one potato\two potato\

为脚本提供最顶层目录,它将遍历子目录查找文件并根据您指定的最顶层目录输出文件路径。因此,例如,如果您指定./things为最顶层,则会导致第一行具有./thing/things2文件路径。换行符被替换为反斜杠以显示文件内容。请注意,如果未指定目录,它还将假定当前工作目录为“。”。

$ cd things
$ ../result2csv.sh                                                          
1,./thing2,to be or not to be\that's Boolean logic\
2,./thing1,one potato\two potato\

你现在要做的就是调用results2csv.sh directory > output.csv将数据输出到文件中,就完成了

答案4

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

https://stackoverflow.com/questions/37644441/python-run-script-in-all-subdirectories

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

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

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

相关内容