Embora a resposta do muru seja a mais simples, existem várias outras maneiras sem o uso do awk.
Perl
A abordagem com o awk é basicamente que escrevemos em um nome de arquivo específico, e alteramos esse nome de arquivo se e somente se encontrarmos o kpoint no começo da linha. Mesma abordagem pode ser feita com o Perl:
$ perl -ane '$p=$F[0] if $F[0] =~ /kpoint/;open($f,">>",$p . ".dat"); print $f $_' input.txt
Veja como isso funciona:
-
-a
flag nos permite usar o array @F
especial de palavras que foram automaticamente divididas a partir de cada linha do arquivo de entrada. Assim, $F[0]
refere-se à primeira palavra, assim como $1
no awk
-
$p=$F[0] if $F[0] =~ /kpoint/
destina-se a alterar $p
(que deve ser a variável do prefixo) se e somente se kpoint
estiver na linha. A melhoria para essa correspondência de padrões pode ser /^ *kpoint/
-
em cada iteração, abrimos para acrescentar um arquivo com o nome $p
associado a .dat
string; note que acrescentar parte é importante. Se você deseja executar com clareza, provavelmente quer se livrar dos antigos arquivos kpoint
. Se quisermos que o arquivo seja sempre criado novo e sobrescrito, podemos reescrever o comando original como:
$ perl -ane 'if ($F[0] =~ /kpoint/){$p=$F[0]; open($f,">",$p . ".dat")}; print $f $_' input.txt
- E, finalmente,
print $f $_
apenas imprime em qualquer nome de arquivo que tenhamos aberto.
split
No seu exemplo, parece que cada entrada consiste em 5 linhas. Se isso for constante, podemos dividir o arquivo dessa maneira, sem depender da correspondência de padrões com split
. Especificamente, este comando:
$ split --additional-suffix=".dat" --numeric-suffixes=1 -l 5 input.txt kpoint
Neste comando, as opções são as seguintes:
-
--additional-suffix=".dat"
é o sufixo .dat
estático que será adicionado a cada arquivo criado
-
--numeric-suffixes=1
nos permitirá adicionar números que mudam a partir de 1 para cada nome de arquivo
-
-l 5
permitirá dividir o arquivo de entrada a cada 5 linhas
-
input.txt
é o arquivo que estamos tentando dividir
-
kpoint
será o prefixo do nome de arquivo estático
E como isso funciona na prática:
$ split --additional-suffix=".dat" --numeric-suffixes=1 -l 5 input.txt kpoint
$ cat kpoint01.dat
kpoint1 : 0.0000 0.0000 0.0000
band No. band energies occupation
1 -52.8287 2.00000
2 -52.7981 2.00000
3 -52.7981 2.00000
$ cat kpoint02.dat
kpoint2 : 0.0000 0.0000 0.0000
band No. band energies occupation
1 -52.8287 2.00000
2 -52.7981 2.00000
3 -52.7981 2.00000
Opcionalmente, também podemos adicionar --suffix-length=1
para manter o comprimento de cada sufixo numérico menor, como kpoint1
em vez de kpoint01
, mas isso pode ser um problema se você tiver um grande número de kpoint
s.
alternativa do awk
Este é similar a resposta do muru , exceto que aqui usamos combinações de padrões diferentes, bem como uma abordagem diferente para criar o variável de nome de arquivo via sprintf()
$ awk '/^\ *kpoint/{f=sprintf("%s.dat",$1)};{print > f}' input.txt
Python
Embora as abordagens awk
e split
sejam mais curtas, outras ferramentas, como Python, são adequadas para o processamento de texto, e podemos usá-las para implementar soluções mais detalhadas, mas úteis.
O script abaixo faz exatamente isso e opera com a ideia de olhar para trás na lista de linhas que salvamos. O script continua salvando linhas, até encontrar kpoint
no início da linha, o que significa que chegamos a uma nova entrada, o que também significa que precisamos gravar a entrada anterior em seu respectivo arquivo.
#!/usr/bin/env python3
import sys
def write_entry(pref,line_list):
# this function writes the actual file for each entry
with open(".".join([pref,"dat"]),"w") as entry_file:
entry_file.write("".join(line_list))
def main():
prefix = ""
old_prefix = ""
entry=[]
with open(sys.argv[1]) as fd:
for line in fd:
# if we encounter kpoint string, that's a signal
# that we need to write out the list of things
if line.strip().startswith('kpoint'):
prefix=line.strip().split()[0]
# This if statement counters special case
# when we just started reading the file
if not old_prefix:
old_prefix = prefix
entry.append(line)
continue
write_entry(old_prefix,entry)
old_prefix = prefix
entry=[]
# Keep storing lines. This works nicely after old
# entry has been cleared out.
entry.append(line)
# since we're looking backwards, we need one last call
# to write last entry when input file has been closed
write_entry(old_prefix,entry)
if __name__ == '__main__': main()
Pure Bash
Quase a mesma ideia da abordagem Perl - continuamos escrevendo tudo para um nome de arquivo específico e alteramos o nome do arquivo apenas quando encontramos uma linha com kpoint
nele.
#!/usr/bin/env bash
while IFS= read -r line;
do
case "$line" in
# We found next entry. Use word-splitting to get
# filename into fname variable, and truncate that filename
*kpoint[0-9]*) read fname trash <<< $line &&
echo "$line" > "$fname".dat ;;
# That's just a line within entry. Append to
# current working file
*) echo "$line" >> "$fname".dat ;;
esac
done < "$1"
# Just in case there are trailing lines that weren't processed
# in while loop, append them to last filename
[ -n "$line" ] && echo "$line" >> "$fname".dat ;