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