Вывести имя подпапки и содержимое файла 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

Как мне действовать дальше?

Примечание: (8 декабря 2017 г.) Хотя приведенные ниже решения правильно отображают контент на терминале, ни одно из них не работает, когда я добавляю> final.csv. Как уже упоминалось, мой result.txt имеет mutilines. Содержимое определенного файла result.txt попадает в разные ячейки, а не в одну ячейку. Есть предложения?

6
задан 8 December 2017 в 21:20

4 ответа

Я думаю 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 команда в a bash подоболочка. printf управляйте печатает имя subdir, запятую и содержание файла, сопровождаемое a \newline. Если Вы хотите записать этот вывод в файл, просто добавьте, например. >final.csv к команде.

Еще более простой

-printf подход предлагается steeldriver:

$ 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/, но сохраните наклонные черты.

8
ответ дан 8 December 2017 в 21:20

Хорошо, вот способ (теперь отредактированный, чтобы превратить разрывы строк в пробелы, благодаря этому ответу о переполнении стека ):

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 прочитайте две строки в пространство шаблонов (это позволяет нам использовать \n)
  • \$! не в том случае, если это последняя строка файла ... нам нужно экранировать $, потому что вся команда ] в двойных кавычках , чтобы оболочка могла расширяться $i и т. д. Но этот $ необходимо передать в целости sed, где он означает «последнюю строку файла». Я рекомендую использовать одинарные кавычки для сценариев sed, если вам не нужно передавать переменные оболочки в них.
  • bl ... переходить к метке (сделать это снова)
  • s/old/new заменить old на new
  • s/\n/ /g для всех символов новой строки в пробел (все, кроме последнего), замените новую строку пробелом
  • .* любым количеством любых символов (что угодно в файле)
  • $((++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
ответ дан 8 December 2017 в 21:20

Решение сценария 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\

Дайте скрипту самый верхний каталог, и он пройдет через подкаталоги, находящие файлы, и выведет путь к файлу в соответствии с тем, как вы указали top самый каталог. Так, например, если вы указали ./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
ответ дан 8 December 2017 в 21:20

Я не знаю точно, как это сделать только с помощью команд терминала, но я проделал аналогичную работу, используя скрипт Python из этой темы:

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

При этом вы можете легко добавить функциональность для записи строк в файл CSV:

https: // docs .python.org / 2 / library / csv.html для Python 2

https://docs.python.org/3/library/csv.html для Python 3

-1
ответ дан 8 December 2017 в 21:20

Другие вопросы по тегам:

Похожие вопросы: