é sed essencial? se você não se importar com duas passagens pelo arquivo de origem, isso é feito facilmente com o grep.
por exemplo,
grep '^\*' input > outputfile
grep -v '^\*' input >> outputfile
Eu me vejo em extrema necessidade de alguma sed magic (eu realmente preciso me sentar e aprender isso). Eu tenho um arquivo com muitas linhas. Depois de analisá-lo, marquei algumas linhas adicionando uma estrela (*) ao início da linha.
O que eu gostaria de fazer com algum truque sed (se possível), é mover todas as linhas marcadas para o início (ou o final - eu não sou exigente) do arquivo, então elas formam um bloco único. As outras linhas (não marcadas) devem ser deixadas intactas.
Como posso fazer algo assim com sed ? Eu sei sed tem alguns buffers para mover o texto ...
é sed essencial? se você não se importar com duas passagens pelo arquivo de origem, isso é feito facilmente com o grep.
por exemplo,
grep '^\*' input > outputfile
grep -v '^\*' input >> outputfile
Dependendo do tamanho do arquivo, você pode fazer:
sed '/^*/!H;//p;$!d;g;s/\n//'
Isso empilha em H as linhas de espaço antigas que não correspondem a /^*/ . Aqueles que correspondem são p rinted quando ocorrem na entrada. Então, todas as linhas que são ! e não $ last são d da saída. Na última linha, g et ocupamos espaço sobrescrevendo o espaço padrão, então o primeiro caractere \n ewline é s/\n// substituído, porque a primeira linha H resultará em um extra a cada vez.
Isso requer um buffer grande, já que armazena todas essas linhas em H old space. Isso, por outro lado ...
sed '/^*/p;$!d;g;r file' <file |
sed -e '1,/^$/{/./p;d' -e '};/^*/d'
... não tem esse requisito.
A primeira sed p apenas rima /^*/ linhas coincidentes até a $ última linha, quando ela imprime uma linha em branco, então r extrairá todo o arquivo de entrada novamente.
O segundo sed funciona primeiro em um intervalo de linha da primeira linha até a primeira linha em branco p rinting todas as linhas que correspondem a pelo menos um único caractere, em seguida d eleting the lot. Depois de encontrar a primeira linha em branco, então d elimina todas as linhas que correspondem a /^*/ .
Você não precisa de sed para fazer isso, você pode usar alguns greps básicos para puxar as linhas star (*) para o topo. Digamos, por exemplo, que você tinha esse arquivo:
$ cat sample.txt
1
2
3
4
* 5
* 6
* 7
8
9
10
Agora para grep o arquivo sample.txt colocando as linhas estrela (*) primeiro:
$ cat <(grep '*' sample.txt) <(grep -v '*' sample.txt)
* 5
* 6
* 7
1
2
3
4
8
9
10
O acima irá executar 2 greps, o 1º puxa todas as linhas com estrelas, enquanto o 2º puxa todas as linhas sem estrela. A saída desses dois comandos é redirecionada como entrada para o comando cat usando a notação <() .
Se você não quiser usar cat + os 2 subshells que você pode fazer como foi sugerido por @terdon:
$ grep '*' sample.txt; grep -v '*' sample.txt
Isso irá retirar todas as linhas de sample.txt que incluem uma estrela (*) seguida por todas as linhas que não o fazem.
Aqui está uma solução usando ed (e expandindo um pouco na solução vi postada por Kaz, que usa abaixo ex - um ed reforçado).
Substitua w por ,p\n para imprimir a saída em vez de gravar no arquivo.
Mova as linhas correspondentes para o final do arquivo:
ed -s file <<< $'g/^\*/m$\nwq\n'
O subcomando g lobal marca todas as linhas que correspondem ao padrão. Então, para cada linha marcada, ela define a linha atual para a linha marcada e executa o subcomando : m ove após a linha no endereço $ (após a última linha); então w rite para o arquivo e q uit o editor.
Mova as linhas correspondentes para o início do arquivo (ou melhor, mova as linhas não correspondentes para o final do arquivo - mas é o resultado final que importa - mantenha a ordem da linha):
ed -s file <<< $'v/^\*/m$\nwq\n'
O subcomando global v erse marca cada linha que NÃO corresponde ao padrão. Então, para cada linha marcada, ela define a linha atual para a linha marcada e executa o subcomando : m ove após a linha no endereço $ (após a última linha); então w rite para o arquivo e q uit o editor.
O motivo pelo qual eu uso correspondência inversa para alcançar o resultado desejado, em vez de g/^\*/m0 (mover todas as linhas correspondentes para o início) sugerido por Kaz, é que (como ele apontou) quando você move as linhas correspondentes para o início vai acabar na ordem inversa.
Isso - como enfatizei acima - é porque g e v , para cada linha marcada, defina a linha atual para a linha marcada e execute o (s) subcomando (s) especificado (s) . A primeira linha marcada como correspondência é movida após a linha endereçada pelo parâmetro após m : 0 neste caso - portanto, ela se torna a primeira linha. Em seguida, a segunda linha correspondente é movida para o mesmo endereço, de modo que se torne a primeira linha e a linha que foi movida antes de se tornar a segunda linha ... e assim por diante ... até que a última linha correspondente seja movida para o endereço especificado.
Se você não se importa com a linha vazia entre os dois blocos:
sed -n -e '/^* /{H;$!d}' -e '/^* /!p' -e '${g;p}'
ou o contrário
sed -n -e '/^* /{p;$!d}' -e '/^* /!H' -e '${g;p}' file
Como você adicionou os caracteres * manualmente, pode continuar trabalhando de maneira interativa. Em vi , você pode fazer isso:
:g/pattern/m<address>
para mover todas as linhas que correspondem a um padrão para o endereço fornecido. Se o endereço for dado como 0, ele moverá as linhas antes da primeira linha do arquivo. Então:
:g/^\*/m0
Note, no entanto, que as linhas marcadas aparecem em ordem inversa! Eu fiz esse tipo de coisa muitas vezes no passado. Para manter as linhas em ordem, o que faço é movê-las depois da última linha, em vez de antes da primeira linha, usando o endereço '$' em vez de '0':
:g/^\*/m$
Se eu os quiser na frente, então é um movimento manual fácil. Por exemplo, usando a seleção visual do Vim.
Na próxima vez, escolha um marcador que não seja um operador de regex que exija escape, embora neste caso o Vim aceite :g/^*/ . :)
É possível mover as linhas até o final e excluir o caractere de marcação em uma etapa. Isso funciona no Vim; O único problema é que não consigo encontrar a documentação que diz que é:
:g/^*/s/^.//|m$
Aqui é uma maneira com awk :
awk '!/^\*/{a[++i]=$0;next}1 END{while(x<length(a))print a[++x]}' file
Tags text-processing sed sort