web-dev-qa-db-ja.com

result.txtのサブフォルダー名と内容を.csvに出力します

複数のサブフォルダーとサブサブフォルダーを持つフォルダーがあります。多くのサブフォルダーまたはサブサブフォルダーにある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にはmutilinesがあります。特定のresult.txtは、単一のセルにあるのではなく、異なるセルにあふれます。提案はありますか?

6
user8109

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という名前の現在のディレクトリ内またはその下のすべてのファイルを検索し、execサブシェル内のprintfコマンドをbashutesします。 printfコマンドは、サブディレクトリの名前、コンマ、およびファイルの内容に続いて\newlineを出力します。この出力をファイルに書き込みたい場合は、たとえば>final.csvをコマンドに。

さらにシンプルに

steeldriver が提案する-printfアプローチです。

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

これにより、最初の列に追加のスラッシュが出力されます。 sed 's|/,|,|'

複数行result.txtコンテンツを1つのセルにマージする

改行文字をたとえばスペースは、上記のコマンドのいずれかで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

区切り文字として他の文字列が必要な場合は、/ /部分を/your_delimiter/に置き換えますが、スラッシュはそのままにしてください。

8
dessert

さて、ここに方法があります(現在、改行をスペースに変換するために編集されています スタックオーバーフローに関するこの答え ):

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で設定解除するか、シェルを終了して新しいファイルを開始します)
  • :lこのアクションのラベルを設定します
  • Nは2行をパターンスペースに読み込みます(これにより、\nを使用できます)
  • \$!これがファイルの最終行ではない場合... $をエスケープする必要があります。これは、コマンド全体が 二重引用符 であるため、シェルが$iなど。しかし、これは$をそのままsedに渡す必要があり、「ファイルの最後の行」を意味します。シェル変数を渡す必要がない限り、sedスクリプトには 単一引用符 を使用することをお勧めします。
  • bl ...ラベル付けするブランチ(もう一度実行)
  • s/old/newoldnewに置き換えます
  • s/\n/ /gパターンスペース内のすべての改行文字(最後の1つを除くすべて)について、改行をスペースに置き換えます
  • .*任意の数の任意の文字(ファイル内の任意の文字)
  • $((++n))ループの各反復でnをインクリメント
  • \.リテラルドット(カンマはsedによって特別に扱われません。文字どおりに印刷されます)
  • "${i%%/*}"処理しているファイルのパスにある現在のサブディレクトリの最初のサブディレクトリの名前(最初の/の後のすべての文字を削除します)
  • &検索セクションからの一致したパターン(ファイル内のすべて)
  • --は、後続の引数の先頭の-をオプションフラグの先頭として解釈しません。これにより、-で始まるファイル名がオプションとして解釈されなくなります。 result.txtを明示的に検索しており、この正確な名前のファイルのみがループに渡されるため、これはこの特定のケースでは不要です。ただし、このスクリプトをグロブで再利用する必要がある場合に備えて、これを含めました。

これはより読みやすいバージョンで、コマンドを分離するために;の代わりに改行を使用するため、より移植性があります(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
5
Zanna

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.shなどのファイルとしてこれを保存し、chmod +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を呼び出してデータをファイルに出力するだけです。

2