Leia um arquivo linha por linha e se a condição for atendida continue lendo até a próxima condição [fechado]

3

Eu tenho um arquivo foo.txt

test
qwe
asd
xca
asdfarrf
sxcad
asdfa
sdca
dac
dacqa
ea
sdcv
asgfa
sdcv
ewq
qwe
a
df
fa
vas
fg
fasdf
eqw
qwe
aefawasd
adfae
asdfwe
asdf
era
fbn
tsgnjd
nuydid
hyhnydf
gby
asfga
dsg
eqw
qwe
rtargt
raga
adfgasgaa
asgarhsdtj
shyjuysy
sdgh
jstht
ewq
sdtjstsa
sdghysdmks
aadfbgns,
asfhytewat
bafg
q4t
qwe
asfdg5ab
fgshtsadtyh
wafbvg
nasfga
ghafg
ewq
qwe
afghta
asg56ang
adfg643
5aasdfgr5
asdfg
fdagh5t
ewq

Eu quero imprimir todas as linhas entre qwe e ewq em um arquivo separado. Isso é o que eu tenho até agora:

#!/bin/bash

filename="foo.txt"

#While loop to read line by line
while read -r line
do
    readLine=$line
    #If the line starts with ST then echo the line
    if [[ $readLine = qwe* ]] ; then
        echo "$readLine"
        read line
        readLine=$line
        if [[ $readLine = ewq* ]] ; then
            echo "$readLine"
        fi
    fi
done < "$filename"
    
por gkmohit 18.01.2016 / 20:05

4 respostas

7

Você precisa fazer algumas alterações no seu script (em nenhuma ordem específica):

  • Use IFS= antes de read para evitar a remoção de espaços iniciais e finais.
  • Como $line não é alterado em lugar nenhum, não há necessidade da variável readLine .
  • Não use a leitura no meio do loop !!.
  • Use uma variável booleana para controlar a impressão.
  • Deixe claro o início e o fim da impressão.

Com essas alterações, o script se torna:

#!/bin/bash

filename="foo.txt"

#While loop to read line by line
while IFS= read -r line; do
    #If the line starts with ST then set var to yes.
    if [[ $line == qwe* ]] ; then
        printline="yes"
        # Just t make each line start very clear, remove in use.
        echo "----------------------->>"
    fi
    # If variable is yes, print the line.
    if [[ $printline == "yes" ]] ; then
        echo "$line"
    fi
    #If the line starts with ST then set var to no.
    if [[ $line == ewq* ]] ; then
        printline="no"
        # Just to make each line end very clear, remove in use.
        echo "----------------------------<<"
    fi
done < "$filename"

O que poderia ser condensado desta maneira:

#!/bin/bash
filename="foo.txt"
while IFS= read -r line; do
    [[ $line == qwe* ]]       && printline="yes"
    [[ $printline == "yes" ]] && echo "$line"
    [[ $line == ewq* ]]       && printline="no"
done < "$filename"

Isso imprimirá as linhas inicial e final (inclusive). Se não houver necessidade de imprimi-los, troque os testes de início e fim:

#!/bin/bash
filename="foo.txt"
while IFS= read -r line; do
    [[ $line == ewq* ]]       && printline="no"
    [[ $printline == "yes" ]] && echo "$line"
    [[ $line == qwe* ]]       && printline="yes"
done < "$filename"

No entanto, seria bem melhor (se você tiver o bash versão 4.0 ou melhor) usar readarray e fazer um loop com os elementos do array:

#!/bin/dash
filename="infile"

readarray -t lines < "$filename"


for line in "${lines[@]}"; do
    [[ $line == ewq* ]]       && printline="no"
    [[ $printline == "yes" ]] && echo "$line"
    [[ $line == qwe* ]]       && printline="yes"
done

Isso evitará a maioria dos problemas de usar read .

É claro que você pode usar a linha recomendada (em comentários; Obrigado, @costas) sed para obter apenas as linhas a serem processadas:

    #!/bin/bash
filename="foo.txt"

readarray -t lines <<< "$(sed -n '/^qwe.*/,/^ewq.*/p' "$filename")"

for line in "${lines[@]}"; do

     : # Do all your additional processing here, with a clean input.

done 
    
por 18.01.2016 / 20:55
4

Como @Costas apontou, a ferramenta correta a ser usada para este trabalho é sed :

sed '/qwe/,/ewq/ w other.file' foo.txt

Pode haver outro processamento necessário nas linhas a serem impressas. Isso é bom; apenas faça assim:

sed -e '/qwe/,/ewq/{w other.file' -e 'other processing;}' foo.txt

(Claro, "outro processamento" não é um comando sed real.) O acima é o padrão a ser usado se você precisar fazer o seu processamento após imprimir a linha. Se você quiser fazer algum outro processamento e depois imprimir uma versão alterada da linha (que parece mais provável), você usaria:

sed -e '/qwe/,/ewq/{processing;w other.file' -e '}' foo.txt

(Observe que é necessário colocar o } em seu próprio argumento, caso contrário ele será interpretado como parte do nome other.file .)

Você (o OP) não declarou o que "outro processamento" você precisa fazer nas linhas, ou eu poderia ser mais específico. Mas seja qual for o processamento, você pode fazê-lo em sed - ou se isso se tornar muito complicado, você pode fazê-lo em awk com muito pouca alteração no código acima:

awk '/qwe/,/ewq/ { print > "other.file" }' foo.txt

Em seguida, você tem todo o poder da linguagem de programação awk à sua disposição para fazer o processamento nas linhas antes de executar essa instrução print . E é claro que awk (e sed ) são projetados para processamento de texto, diferente de bash .

    
por 18.01.2016 / 23:33
1
qwe(){ printf %s\n "$1"; }
ewq(){ :; }
IFS=   ### prep  the  loop, only IFS= once
while  read -r  in
do     case $in in
       (qwe|ewq)
           set "$in"
       ;;
       ("$processing"?)
           "$process"
       esac
       "$1" "$in"
done

Essa é uma maneira muito lenta de fazer isso. Com um% GNUgrep e um regular infile :

IFS=
while grep  -xm1 qwe
do    while read  -r  in  &&
            [ ewq != "$in" ]
      do    printf %s\n "$in"
            : some processing
      done
done <infile

... pelo menos otimizaria metade das leituras ineficientes ...

sed  -ne '/^qwe$/,/^ewq$/H;$!{/^qwe$/!d;}' \
      -e "x;s/'"'/&\&&/g;s/\n/'"' '/g"    \
      -e "s/\(.*\) .e.*/p '/p" <input    |
sh    -c 'p(){  printf %s\n "$@"
                for l do : process "$l"
                done
          }; . /dev/fd/0'

E isso evitaria as ineficiências de read para a maioria dos sh , embora tenha que imprimir a saída duas vezes - uma vez cotado para sh e uma vez sem aspas para stdout. Ele funciona de maneira diferente porque o comando . tende a ler entrada por bloco, e não por byte, para a maioria das implementações. Ainda assim, elidia o ewq - qwe ao todo, e trabalharia para entrada em fluxo - como um FIFO.

qwe
asd
xca
asdfarrf
sxcad
asdfa
sdca
dac
dacqa
ea
sdcv
asgfa
sdcv
qwe
a
df
fa
vas
fg
fasdf
qwe
aefawasd
adfae
asdfwe
asdf
era
fbn
tsgnjd
nuydid
hyhnydf
gby
asfga
dsg
qwe
rtargt
raga
adfgasgaa
asgarhsdtj
shyjuysy
sdgh
jstht
qwe
asfdg5ab
fgshtsadtyh
wafbvg
nasfga
ghafg
qwe
afghta
asg56ang
adfg643
5aasdfgr5
asdfg
fdagh5t
    
por 19.01.2016 / 00:57
-1
sed '/./=' input2.file | sed -n '/./N;s/\n/  /; /qwe/,/ewq/p'
    
por 11.10.2017 / 22:03