逐行读取文件,如果满足条件则继续读取直到下一个条件

逐行读取文件,如果满足条件则继续读取直到下一个条件

我有一个文件 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

我想在一个单独的文件中打印之间qwe和之间的所有行。ewq这是我到目前为止所拥有的:

#!/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"

答案1

您需要对脚本进行一些更改(排名不分先后):

  • 使用IFS=beforeread以避免删除前导和尾随空格。
  • 由于$line没有任何地方改变,所以不需要变量readLine
  • 不要在循环中间使用 read !!。
  • 使用布尔变量来控制打印。
  • 明确打印的开始和结束。

经过这些更改,脚本变为:

#!/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"

可以这样概括:

#!/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"

这将打印起始行和结束行(包括)。
如果不需要打印它们,请交换开始和结束测试:

#!/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"

readarray但是,使用数组元素并循环会更好(如果您有 bash 版本 4.0 或更高版本) :

#!/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

这将避免使用read.


当然,您可以使用推荐的(在评论中;谢谢,@costas)sed行来仅获取要处理的行:

    #!/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 

答案2

正如@Costas 指出的,用于这项工作的正确工具是sed

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

可能需要对要打印的行进行其他处理。没关系;就像这样做:

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

(当然,“其他处理”不是真正的sed命令。)上面是如果您需要进行处理时要使用的模式你打印该行。如果您想做一些其他处理,然后打印该行的更改版本(这似乎更有可能),您可以使用:

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

(请注意,必须将 放入}其自己的参数中,否则它将被解释为名称的一部分other.file。)


您(OP)还没有说明您必须在线执行哪些“其他处理”,或者我可以更具体。但无论该处理是什么,您绝对可以在其中完成- 或者如果这变得太笨拙,您可以在对上述代码进行很少的更改的情况下sed完成它:awk

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

然后,您可以使用编程语言的所有功能awk在执行该语句之前对各行进行处理print。当然awk(和sed)是设计的用于文本处理,与bash.

答案3

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

这是一种非常缓慢的方法。有了 GNUgrep和常规infile:

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

...至少会优化一半的低效读取...

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

这将避免read大多数sh情况下的低效率,尽管它确实必须打印输出两次 - 一次引用到 stdout sh,一次不引用到 stdout。它的工作方式有所不同,因为.对于大多数实现来说,该命令倾向于按块而不是按字节读取输入。尽管如此,它还是完全消除了 ewq - qwe,并且适用于流输入 - 例如 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

答案4

sed '/./=' input2.file | sed -n '/./N;s/\n/  /; /qwe/,/ewq/p'

相关内容