Reordenar vários blocos de linha com Sed

4

Estou tentando reordenar a saída gerada da seguinte maneira; Dado um fragmento tex como segue, eu quero mover o /^Constant/ abaixo de /Dummies/ , com a linha antes e depois de /^Constant/ . Além disso, eu gostaria de um resultado estável, que não faça uma alteração se Constant já estiver abaixo da última Dummies row (ele é executado como parte de um script para corrigir meus resultados de pesquisa e, até agora, esse script é estável).

Esta é a entrada do fragmento tex:

[1em]
Application Grade&                  &                  &  -0.0857\sym{***}& -0.00412\sym{***}\
                &                  &                  & (0.0149)         &(0.00107)         \
[1em]
Constant        &   -3.701\sym{***}&   -0.311\sym{***}&        0         &        0         \
                &  (1.130)         & (0.0853)         &      (.)         &      (.)         \
[1em]
Major Dummies   &       No         &       No         &      Yes         &      Yes         \
[1em]
Semester Dummies &       No         &       No         &      Yes         &      Yes         \
\hline

Esta é a saída desejada, que permanece estável (retorna o mesmo) se fornecida como entrada:

[1em]
Application Grade&                  &                  &  -0.0857\sym{***}& -0.00412\sym{***}\
                &                  &                  & (0.0149)         &(0.00107)         \
[1em]
Major Dummies   &       No         &       No         &      Yes         &      Yes         \
[1em]
Semester Dummies &       No         &       No         &      Yes         &      Yes         \
[1em]
Constant        &   -3.701\sym{***}&   -0.311\sym{***}&        0         &        0         \
                &  (1.130)         & (0.0853)         &      (.)         &      (.)         \
\hline

O código a seguir fará isso, mas não é estável. Basicamente, ele corresponde a Constant , descarta e reinsere [1em] (o que é necessário fazer em uma chamada separada, pois i\ afeta a saída, não o espaço padrão) e cola abaixo do destino. Não é estável porque adicionará [1em] s falsos após Semester Dummies . Francamente, é um pouco feio (exigindo chamadas separadas para sed ).

sed -E -i'' -e'/^Constant/ {N;h;N;d;}; /^Semester Dummies/ {G;};' fragment.tex
sed -E -i'' -e '/^Semester Dummies/ {a\
\[1em\]
};' fragment.tex

Acredito que um one-liner fará isso, combinando o padrão de múltiplas linhas /^\[1em\]\nConstant.*/ (após uma chamada para N;N; ), colocando isso no buffer h old e colando-o ( G; ) após %código%. Mas depois de muitas horas, man pages, pesquisas na web e upgrade para /Semester Dummies/ para uma boa medida, não consigo fazer com que esse script funcione. Isso expressa a ideia básica, mas não é válida (s não pode condicionalmente gnused old como eu faço aqui):

sed -E -i'' -e'/\[1em\]/ {N;N;s/^\[1em\]\nConstant.*//h;}; /Semester Dummies/ {G;}' fragment.tex

Eu tentei muitas variações confiando em h com vários endereçamentos, mas ainda não entendi a ordem de execução para comandos com vários destinatários quando você está mexendo com o espaço de padrão (chamando N;P;D;h;G; ). N; e N funcionam bem, mas até onde eu sei, P é completamente inútil.

Sim, sei que é mais fácil com D e awk ; na verdade, eu estaria interessado em ver todas as três soluções para comparação - mas aqui estou perguntando especificamente como fazer isso em perl .

    
por rjturn 05.02.2014 / 19:01

2 respostas

1

Aqui está uma peça feia de awk que joga truques com o separador de registro

awk -v RS='\[1em\]\n|\\hline' '
    !/[^[:space:]]/ {next} 
    /^Constant/ {c=$0; next} 
    {printf "[1em]\n%s", $0} 
    END {printf "[1em]\n%s\hline\n", c}
'

Como o texto começa e termina com "separador de registro", existem alguns registros espúrios vazios, daí a primeira regra

    
por 05.02.2014 / 20:56
1

Aqui está uma maneira de fazer isso com sed usando o buffer de retenção:

sed '/\[1em\]/{N;/Constant/{N;x;/^$/d;x};/Semester/{x;/^$/!{H;x};//g}}' file

Em cada linha que corresponde a [1em] , lê a linha N ext e, em seguida,

  1. Se o espaço de padrão corresponder a Constant em uma linha N other, e x alterará os buffers e se o espaço de padrão for apenas uma linha vazia (isso significa que o buffer de retenção estava vazio antes da troca) é d eletizado. Se o espaço do padrão não for uma linha vazia (o que significa que havia algo no espaço de espera por causa de 2 ), ele e x alterará os buffers novamente, retornando as linhas no espaço padrão.
  2. Se o espaço de padrão corresponder a Semester it e x alterar os buffers, se o espaço de padrões não estiver vazio, isso significa que as linhas [1em]\nConstant.* estavam no buffer de retenção, portanto, as anexa à [1em]\nSemester.* linhas (que estão agora no buffer Hold) e, em seguida, e x altera os buffers novamente. Se o espaço de padrão estiver vazio, isso significa que as linhas [1em]\nConstant.* estão após a linha Semester , de modo que apenas copia o espaço de espera sobre o espaço de padrão. Dessa forma, as linhas [1em]\nSemester.* são restauradas, mas agora há algo no buffer de retenção quando atinge 1 .

Dessa forma, as linhas [1em]\nConstant.* são movidas somente se estiverem antes de [1em]\nSemester.* , caso contrário, nada acontece.

sed '/\[1em\]/{             # if line matches [1em]
N                           # read in the next line
/Constant/{                 # if pattern space matches Constant
N                           # read in another line
x                           # exchange buffers
/^$/d                       # if pattern space is now empty, delete it
x                           # otherwise, exchange again
}
/Semester/{                 # if pattern space matches Semester
x                           # exchange buffers
/^$/!{                      # if pattern space is not empty
H                           # append it to hold space
x                           # then exchange buffers
}
//g                         # if pattern space is currently empty
}                           # copy the hold space over the pattern space
}' infile                   # so now the hold space is no longer empty

Isso é mais fácil com ed . Basta selecionar o intervalo de linhas e movê-lo após a linha correspondente a Semester :

ed -s infile <<<$'/Constant/-1,/Constant/+1m/Semester/\n,p\nq'

substitua ,p por w para gravar as alterações no arquivo:

ed -s infile <<IN
/Constant/-1,/Constant/+1m/Semester/
w
q
IN
    
por 18.07.2015 / 16:41

Tags