Imprime texto entre duas strings - omite a segunda ocorrência

2

Eu tenho um arquivo de instruções com muitos passos. Eu gostaria de ver uma única etapa desejada em um shell, sem ambiente gráfico. O que eu faço agora, como apontado por alemol , está usando sed para extrair o que está dentro do número da etapa Eu gostaria de ver e o próximo.

cat file | sed -n '/12/,/13/p'

Step 12: something
commands to execute
Step 13: something else

O que sinto falta ao fazer exatamente o que eu quero é omitir a última linha: o que sed faz é imprimir da primeira ocorrência para a segunda, incluindo aquelas. Eu gostaria de evitar o segundo, removendo a última linha da saída e imprimindo apenas

Step 12: something
commands to execute

Como modifico meu comando para conseguir isso? Eu prefiro não comandos adicionais, pipelines e tal. Se for possível, gostaria apenas de modificar o sed que uso (ou um uso único igualmente direto de alguma outra ferramenta como grep ), diferenciando o primeiro padrão do segundo, como "imprima um, mas omita o outro ".

Além disso, quero que o comando seja o mais fácil possível, para que eu possa memorizá-lo sem muito esforço e escrevê-lo em pouco tempo.

    
por Jeffrey Lebowski 02.06.2016 / 15:57

2 respostas

1

Don_crissti deu-lhe uma solução nos comentários (e eu seguirei o arquivo convenção de nomenclatura ):

sed -n '/12/,/13/{/13/!p}' UUoC.txt

Isso selecionará todas as linhas desde o primeiro 12 até o primeiro 13 no arquivo UUoC.txt e as imprimirá, a menos que correspondam a 13 (é o que o /13/!p faz).

Outra abordagem seria usar head para descartar a última linha:

sed -n '/12/,/13/p' file.txt | head -n -1
    
por 02.06.2016 / 16:39
1

don_crissti e terdon fizeram todo o trabalho duro; Eu vi essa pergunta e queria sugerir o uso de uma função shell para reduzir ainda mais a digitação / memorização e, talvez, adicionar alguma flexibilidade. Ao digitá-lo, também tomei a liberdade de apertar a expressão regular alguns, para evitar falsos positivos.

Aqui está a função do shell:

function step () (
  STEPS=./steps.txt
  start=$1
  stop=$((start+1))
  sed -n "/^Step $start:/,/^Step $stop:/ { /^Step $stop:/ !p }" $STEPS
)

Você está obviamente livre para nomear o que quiser; step pareceu intuitivo para mim com base no texto do arquivo de instrução. Ajuste a variável STEPS para ser o caminho completo para o arquivo de instruções real; Dessa forma, você não precisa se lembrar - ou digitar! - o caminho para esse arquivo na linha de comando. Eu fiz a função usar um subshell - parênteses ao redor do corpo em vez de chaves - porque eu não queria poluir o namespace do seu shell com as variáveis STEPS, start e stop. Se a criação de um subshell for mais problemática do que três novas variáveis, sinta-se à vontade para alterar o segundo conjunto de parênteses para chaves.

Eu fiz duas coisas básicas na expressão regular que don & terdon usado:

  1. Forçou a partida para começar no início da linha e
  2. exigiu que ele contivesse a palavra "Etapa" seguida pelo número, seguido por dois pontos.

Eu fiz as duas coisas porque posso imaginar o arquivo de instrução contendo potencialmente números dentro do commands to execute e, assim, fazer com que o regex mais simples correspondesse falsamente.

Aqui está o arquivo steps.txt do "advogado do diabo" que eu estava usando:

Step 1: one
commands to execute
Step 2: two
commands to execute
commands to execute
Step 3: three
commands to execute skip to Step 7
Step 4: four
commands to execute not step 5
commands to execute
commands to execute
commands to execute
Step 5: five
commands to execute
commands to execute
commands to execute skip to Step 6:
commands to execute
Step 6: six
commands to execute
Step 7: seven
commands to execute
Step 8: eight
commands to execute
Step 9: nine
commands to execute
Step 10: ten
commands to execute
Step 11: eleven
commands to execute
Step 12: something
commands to execute
commands to execute
Step 13: something else
commands to execute

... e os resultados de um equipamento de teste (a saída de step 14 está vazia):

$ for step in $(seq 1 14); do step $step; echo; done
Step 1: one
commands to execute

Step 2: two
commands to execute
commands to execute

Step 3: three
commands to execute skip to Step 7

Step 4: four
commands to execute not step 5
commands to execute
commands to execute
commands to execute

Step 5: five
commands to execute
commands to execute
commands to execute skip to Step 6:
commands to execute

Step 6: six
commands to execute

Step 7: seven
commands to execute

Step 8: eight
commands to execute

Step 9: nine
commands to execute

Step 10: ten
commands to execute

Step 11: eleven
commands to execute

Step 12: something
commands to execute
commands to execute

Step 13: something else
commands to execute
    
por 05.06.2016 / 04:08