Imprima o nome da subpasta e o conteúdo do resultado .txt para .csv -- and-line campo com bash campo com find camp askubuntu Relacionado O problema

Print sub-folder name and content of result.txt to .csv


6
vote

problema

português

Eu tenho uma pasta que possui várias sub-pastas e sub-pastas sub. Eu quero imprimir o conteúdo de um arquivo chamado result.txt que está presente em muitas pastas ou sub-pastas sub em um arquivo CSV junto com o nome da subpasta.

Isso significa se os arquivos chamados result.txt estão em

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

Então eu preciso de um arquivo CSV que deve ter

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

e assim por diante.

Eu comecei com o seguinte find comando

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

Como devo proceder a partir daqui?

Nota: (8 de dezembro de 2017) Embora as soluções abaixo exibam o conteúdo corretamente no terminal, nenhum deles funciona quando eu adiciono & gt; final.csv. Como já mencionado, meu resultado.txt tem mutilina. O conteúdo de um determinado resultado.TXT é derramado para diferentes células em vez de estar em uma única célula. Quaisquer sugestões?

english

I have a folder which has multiple sub-folders and sub sub folders. I want to print the content of a file called result.txt which is present in many sub folders or sub sub folders into a csv file along with the name of the sub-folder.

That means if the files named result.txt are in

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

Then I need a csv file which should have

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

and so on.

I started with the following find command

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

How should I proceed from here ?

Note: (8th December, 2017) Although the below solutions display the content properly on the terminal , none of them work when I add >final.csv. As already mentioned, my result.txt has mutilines. The content of a particular result.txt gets spilled to different cells rather than being in a single cell. Any suggestions ?

        
         
         

Lista de respostas

8
 
vote

Eu acho que find é a escolha certa:

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

Exemplo Executar

  $ 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 " "${0%%/*}" "$(cat $0)"' {} ; a,r1 c,r2    

Explicações

Este comando result.txt pesquisa de cada arquivo ou no diretório atual do nome result.txt e exec utes o abcdefghijklmn6 Comando em um ABCDEFGHIJKLMNABCDEFGHIJKLMN7 Subshell. O comando ABCDEFGHIJKLMNABCDEFGHIJKLMN8 Imprime o nome do Subdir, uma vírgula e o conteúdo do arquivo seguido por um ewline. Se você quiser escrever esta saída para um arquivo, basta anexar e. find */ -name "result.txt" -exec bash -c 'printf "%s,%s " "${0%%/*}" "$(cat $0)"' {} ; 0 para o comando.

Mesmo mais simples

é o find */ -name "result.txt" -exec bash -c 'printf "%s,%s " "${0%%/*}" "$(cat $0)"' {} ; 1 Abordagem sugerida por steeldriver :

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

Isto imprime uma barra adicional na primeira coluna que você pode facilmente remover encanando a saída através de e.g. find */ -name "result.txt" -exec bash -c 'printf "%s,%s " "${0%%/*}" "$(cat $0)"' {} ; 3 .

mesclando multilina find */ -name "result.txt" -exec bash -c 'printf "%s,%s " "${0%%/*}" "$(cat $0)"' {} ; 4 conteúdo em uma célula

Para substituir os caracteres da Newline por e. Espaços apenas substituem find */ -name "result.txt" -exec bash -c 'printf "%s,%s " "${0%%/*}" "$(cat $0)"' {} ; 5 com find */ -name "result.txt" -exec bash -c 'printf "%s,%s " "${0%%/*}" "$(cat $0)"' {} ; 6 em um dos comandos acima, e.

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

Se você quiser alguma outra string como o delimitador substituir o find */ -name "result.txt" -exec bash -c 'printf "%s,%s " "${0%%/*}" "$(cat $0)"' {} ; 8 peça com find */ -name "result.txt" -exec bash -c 'printf "%s,%s " "${0%%/*}" "$(cat $0)"' {} ; 9 , mas mantenha as barras.

 

I think find is the right choice:

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

Example run

$ echo r1 >a/b/result.txt $ echo r2 >c/result.txt $ tree . xe2x94x9cxe2x94x80xe2x94x80 a xe2x94x82xc2xa0xc2xa0 xe2x94x94xe2x94x80xe2x94x80 b xe2x94x82xc2xa0xc2xa0     xe2x94x94xe2x94x80xe2x94x80 result.txt xe2x94x94xe2x94x80xe2x94x80 c     xe2x94x94xe2x94x80xe2x94x80 result.txt $ find */ -name "result.txt" -exec bash -c 'printf "%s,%s\n" "${0%%/*}" "$(cat $0)"' {} \; a,r1 c,r2 

Explanations

This find command searches every file in or under the current directory of the name result.txt and executes the printf command in a bash subshell. The printf command prints the subdir's name, a comma and the file content followed by a \newline. If you want to write this output to a file, just append e.g. >final.csv to the command.

Even simpler

is the -printf approach suggested by steeldriver:

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

This prints an additional slash in the first column which you can easily remove by piping the output through e.g. sed 's|/,|,|'.

Merging multiline result.txt content into one cell

To replace newline characters with e.g. spaces just replace cat with sed ":a;N;\$!ba;s/\n/ /g" in one of the above commands, e.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 

If you want some other string as the delimiter replace the / / part with /your_delimiter/, but keep the slashes.

 
 
 
 
5
 
vote

Bem, aqui está um caminho (agora editado para transformar pausas de linha em espaços, graças a esta resposta sobre overflow da pilha ) :

  shopt -s globstar n=0; for i in **/result.txt; do sed -e ":l;N;$!bl;s/ / /g; s/.*/$((++n)). "${i%%/*}" &/" "$i"; done    

Você pode adicionar um redirecionamento para gravar em um arquivo

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

Notas

  • n=0 Defina uma variável para incrementar
  • shopt -s globstar ativar o globbing recursivo com ** para encontrar todos os arquivos em diretórios abaixo este (dessetido com shopt -u globstar depois ou sair do shell e iniciar um novo)
  • :l Defina um rótulo para esta ação
  • N Leia duas linhas no espaço do padrão (isso nos permite usar )
  • $! não se esta é a última linha do arquivo ... Temos que escapar n=0; for i in **/result.txt; do sed ":l;N;$!bl;s/ / /g; s/.*/$((++n)). "${i%%/*}" &/" "$i"; done > outfile 0 porque o comando inteiro é Duplo citado para que o shell possa expandir n=0; for i in **/result.txt; do sed ":l;N;$!bl;s/ / /g; s/.*/$((++n)). "${i%%/*}" &/" "$i"; done > outfile 1 etc. Mas Este ABCDEFGHIJKLMNABCDEFGHIJKLMN12 precisa ser passado intacto para n=0; for i in **/result.txt; do sed ":l;N;$!bl;s/ / /g; s/.*/$((++n)). "${i%%/*}" &/" "$i"; done > outfile 3 , onde significa "a última linha do arquivo". Eu recomendo usar Citações simples para abcdefghijklmn14 scripts, a menos que você tenha que passar variáveis ​​de shell neles.
  • n=0; for i in **/result.txt; do sed ":l;N;$!bl;s/ / /g; s/.*/$((++n)). "${i%%/*}" &/" "$i"; done > outfile 5 ... ramifique para rótulo (faça de novo)
  • n=0; for i in **/result.txt; do sed ":l;N;$!bl;s/ / /g; s/.*/$((++n)). "${i%%/*}" &/" "$i"; done > outfile 6 substitua n=0; for i in **/result.txt; do sed ":l;N;$!bl;s/ / /g; s/.*/$((++n)). "${i%%/*}" &/" "$i"; done > outfile 7 com n=0; for i in **/result.txt; do sed ":l;N;$!bl;s/ / /g; s/.*/$((++n)). "${i%%/*}" &/" "$i"; done > outfile 8
  • n=0; for i in **/result.txt; do sed ":l;N;$!bl;s/ / /g; s/.*/$((++n)). "${i%%/*}" &/" "$i"; done > outfile 9 para todos os caracteres da nova linha no espaço padrão (tudo menos o último), substitua a nova linha por um espaço
  • n=00 qualquer número de caracteres (qualquer coisa no arquivo)
  • n=01 incremento n=02 com cada iteração do loop
  • n=03 ponto literal (vírgulas não são tratadas especialmente por n=04 ; eles serão impressos literalmente)
  • n=02 O nome do primeiro subdiretório do atual no caminho do arquivo que estamos lidando (tira todos os caracteres após o primeiro ABCDEFGHIJKLMNABCDEFGHIJKLMN26 )
  • n=07 o padrão correspondente na seção de pesquisa (qualquer coisa no arquivo)
  • n=08 Não interprete a liderança n=09 em argumentos subseqüentes como pré-adiantamento de sinalizadores de opção. Isso evita que nomes de arquivos começando com shopt -s globstar0 sendo interpretado como opções. Isso é desnecessário nesse caso específico, porque estamos pesquisando explicitamente para ABCDEFGHIJKLMNABCDEFGHIJKLMN31 e somente os arquivos com este nome exato serão passados ​​para o loop. No entanto, eu incluí, caso alguém precise reutilizar este script com um glob.

Aqui está uma versão mais legível, que também é mais portátil (deve funcionar em todas as versões de shopt -s globstar2 ) como é usa newlines em vez de shopt -s globstar3 para separar comandos:

.
  shopt -s globstar4   
 

Well, here's a way (now edited to turn line breaks into spaces, thanks to this answer on 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 

You can add a redirection to write to a file

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

Notes

  • n=0 set a variable to increment
  • shopt -s globstar Turn on recursive globbing with ** to find all files in directories below this one (unset with shopt -u globstar afterwards, or exit the shell and start a new one)
  • :l set a label for this action
  • N read two lines into the pattern space (this allows us to use \n)
  • \$! not if this is the last line of the file... we have to escape $ because the whole command is double quoted so that the shell can expand $i etc. But this $ needs to be passed intact to sed, where it means "the last line of the file". I recommend using single quotes for sed scripts unless you have to pass shell variables in them.
  • bl ...branch to label (do it again)
  • s/old/new replace old with new
  • s/\n/ /g for all the newline characters in the pattern space (all but the last one), replace the newline with a space
  • .* any number of any characters (anything in the file)
  • $((++n)) increment n with each iteration of the loop
  • \. literal dot (commas are not treated specially by sed; they will be printed literally)
  • "${i%%/*}" the name of the first subdirectory of the current one in the path of the file we are dealing with (strip all characters after the first /)
  • & the matched pattern from the search section (anything in the file)
  • -- do not interpret leading - in subsequent arguments as prepending option flags. This prevents filenames beginning with - being interpreted as options. This is unnecessary in this specific case, because we are searching explicitly for result.txt and only files with this exact name will be passed to the loop. However, I have included it, in case anyone needs to reuse this script with a glob.

Here's a more readable version, which is also more portable (should work in all versions of sed) as is uses newlines instead of ; to separate commands:

#!/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 
 
 
         
         
2
 
vote

Bash Script Solution

#!/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 " " done } main "$@"

A maneira como esta funciona é que você deve salvar isso como arquivo, por exemplo results2csv.sh , tornar executável com chmod +x e executar dando caminho completo para o script ou lugar em ABCDEFGHIJKLMNABCDEFGHIJKLMN3 Pasta, Executar ABCDEFGHIJKLMNABCDEFGHIJKLMN4 e Chame o script pelo nome.

Aqui está como este script funciona:

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

Dê o script o diretório superior da maioria, e ele passará pelos subdiretórios encontrando os arquivos e a saída do caminho para arquivar de acordo com a forma como você especifica a maior parte do diretório. Assim, por exemplo, se você especificou ./things como superior, resultaria na primeira linha tendo ./thing/things2 como caminho para arquivo. As newlines são substituídas por barras invertidas para mostrar o conteúdo do arquivo. Note que também assumirá o diretório de trabalho atual "." Se o diretório não for especificado.

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

Tudo o que você precisa fazer agora, é chamada results2csv.sh directory > output.csv para saída de dados em um arquivo e terminar

 

Bash script solution

#!/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 "$@" 

The way this works is that you should save this as file, for example results2csv.sh, make executable with chmod +x and run either by giving full path to the script or place it into ~/bin folder, run source ~/.bashrc and call the script by name.

Here's how this script works:

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

Give the script the top-most directory, and it will go through the subdirectories finding the files and output the path to file in accordance with how you specified top most directory. So, for example if you specified ./things as top most, it would result in first line having ./thing/things2 as path to file. Newlines are replaced with backslashes to show file contents. Note that it will also assume current working directory "." if directory isn't specified.

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

All you have to do now, is call results2csv.sh directory > output.csv to output data into a file, and you're done

 
 
-1
 
vote

Eu não sei exatamente como fazer com apenas comandos de terminal, mas eu fiz uma coisa semelhante usando o script Python deste thread:

https://stackoverflow.com/questions/37644441/python-run-script -in-all-subdiretórios

Com isso, você pode facilmente adicionar funcionalidade a gravar linhas no arquivo CSV:

https://docs.python.org/2/library/csv.html para python 2

https://docs.python.org/3/library/csv.html para python 3

 

I don't know exactly how to do it with just terminal commands, but I have done similar thing using python script from this thread:

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

With this you can easily add funcionality to write lines to CSV file:

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

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

 
 
 
 

Perguntas relacionadas

9  Tirando linhas de "acesso negado"  ( Taking out access denied lines ) 
Quando eu uso find para ver todos os arquivos PDF no diretório /home , estou vendo access denied . Para eliminá-los, tentei: find /home -iname "*.pdf" ...

1  Bash: arquivo não encontrado  ( Bash file not found ) 
Está tudo na captura de tela abaixo ...

1  Criando um diretório de arquivos criados recentemente  ( Creating a directory of recently created files ) 
Em um sistema que eu tenho, os arquivos são carregados por meio de uma série de vários mecanismos em um diretório "recebendo" central. Uma vez por dia, esses ...

1  Eu usei o comando Localizar sem especificar um diretório  ( I used the find command without specifying a directory ) 
Eu usei este comando sem especificar um diretório: sudo find -type d -exec chmod 755 {} O que poderia ter acontecido? Eu estava na pasta / var / www /...

44  Encontre o arquivo mais recente por data modificada  ( Find the latest file by modified date ) 
se eu quiser encontrar o arquivo mais recente (mtime) em um diretório (grande) contendo subdiretórios, como eu faria isso? Lotes de postagens que eu encon...

1  Obtenha uma utilização de disco por tipo de arquivo?  ( Get disk utilization by file type ) 
Existe uma maneira de obter uma quebra de uma utilização de disco (ou diretório) com base no tipo de arquivo na linha de comando? Eu acho que eu poderia esc...

34  encontrar vs. localize  ( Find vs locate ) 
Existem os comandos find e locate para procurar arquivos no disco. Eu sei que find Recursivamente processa todos os subdiretórios necessários para pes...

2  A mtime de um diretório pode ser mais antiga que a do arquivo dentro?  ( Can a directorys mtime be older than that of file inside ) 
Eu tenho essa pergunta muito boba e muito básica, mas eu pensei melhor perguntar antes de começar a podar minha NAS. Eu quero excluir todos os diretórios no...

32  Como encontrar arquivos entre duas datas usando "Localizar"?  ( How to find files between two dates using find ) 
Eu tenho uma conta de e-mail que passou por 60 GB de e-mails, e atualmente estou tendo muitos problemas usando um cliente de e-mail para arquivar e-mails do a...

12  Como posso encontrar todos os arquivos de vídeo no meu sistema?  ( How can i find all video files on my system ) 
Eu tentei usar a pesquisa de arquivos de lente de unidade para *.* e filtragem por último modificado = tudo, digite = videos, e tamanho = tudo, mas não enco...




© 2022 pergunte.org All Rights Reserved. Casa de perguntas e respostas todos os direitos reservados


Licensed under cc by-sa 3.0 with attribution required.