У меня есть папка с несколькими подпапками и подпапками. Я хочу напечатать содержимое файла с именем 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 попадает в разные ячейки, а не в одну ячейку. Есть предложения?
Я думаю 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 \n
ewline. Если Вы хотите записать этот вывод в файл, просто добавьте, например. >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/
, но сохраните наклонные черты.
Хорошо, вот способ (теперь отредактированный, чтобы превратить разрывы строк в пробелы, благодаря этому ответу о переполнении стека ):
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
#!/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
для вывода данных в файл, и все готово
Я не знаю точно, как это сделать только с помощью команд терминала, но я проделал аналогичную работу, используя скрипт 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