複数のサブフォルダーとサブサブフォルダーを持つフォルダーがあります。多くのサブフォルダーまたはサブサブフォルダーにある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は、単一のセルにあるのではなく、異なるセルにあふれます。提案はありますか?
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
コマンドをbash
utesします。 printf
コマンドは、サブディレクトリの名前、コンマ、およびファイルの内容に続いて\n
ewlineを出力します。この出力をファイルに書き込みたい場合は、たとえば>final.csv
をコマンドに。
steeldriver が提案する-printf
アプローチです。
$ find */ -name 'result.txt' -printf '%H,' -exec cat {} \;
a/,r1
c/,r2
これにより、最初の列に追加のスラッシュが出力されます。 sed 's|/,|,|'
。
result.txt
コンテンツを1つのセルにマージする改行文字をたとえばスペースは、上記のコマンドのいずれかで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/
に置き換えますが、スラッシュはそのままにしてください。
さて、ここに方法があります(現在、改行をスペースに変換するために編集されています スタックオーバーフローに関するこの答え ):
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/new
old
をnew
に置き換えます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
#!/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
を呼び出してデータをファイルに出力するだけです。