Заданный вопросздесьзапрашивает несколько строк до и после сопоставления с образцом.
Но здесь цель — взять номер строки и извлечь из файла несколько строк до и после него.
НАПРИМЕР:
seq 10
1
2
3
4
5
6
7
8
9
10
Если номер строки 6, то должно быть 4 цифры до этой строки и 3 цифры после этой строки. То есть
2
3
4
5
6
7
8
9
решение1
z=6 # focus line
x=4 # lines before
y=3 # lines after
start=$(( z - x ))
end=$(( z + y ))
С использованием sed
:
seq 10 | sed -n "$start,${end}p"
2
3
4
5
6
7
8
9
p
Это просто использует команду print ( ) sed
с явным диапазоном строк для печати. Остальные строки игнорируются с помощью -n
.
С использованием awk
:
seq 10 | awk -v start="$start" -v end="$end" 'NR >= start { print } NR >= end { exit }'
2
3
4
5
6
7
8
9
Это похоже на ответ Стефана Шазеля, но реализовано в awk
; скрипт начинает выводить входные строки после прочтения start
определенного количества строк. При end
достижении определенного количества строк скрипт завершает работу.
Оба варианта будут отображать часть входных данных, начиная со x
строк, предшествующих строке, z
и заканчивая y
строками после строки z
.
решение2
С оболочками POSIX:
$ before=4 after=3 line=6
$ seq 10 | sed "$((line-before)),\$!d; $((line+after))q"
2
3
4
5
6
7
8
9
Переводится как:
- гудалить любую строку, кроме (
!
) из диапазона отлиния - передй до конца ($
). - дуит настрока + послей строка
Таким образом, мы даже не будем читать дальшестрока + послей строка.
Однако это означает, что команда, передающая данные, sed
будет прервана с помощью SIGPIPE, если она продолжит отправлять данные вскоре после этого, что может быть как желательным, так и нежелательным.
решение3
Просто для полноты картины:
$ l=60;seq 100 |head -n$((l+3)) |tail -n+$((l-4))
56
57
58
59
60
61
62
63
Слухи и различные тесты говорят о том, что комбинация «голова + хвост» намного быстрее, чем любой другой инструмент:
$ a=1000000000
$ time seq $a |awk 'NR>=499998{print}NR >= 500004 { exit }'
499998
499999
500000
500001
500002
500003
real 0m0.158s
user 0m0.152s
sys 0m0.004s
$ time seq $a |sed -n "499998,500003p"
499998
499999
500000
500001
500002
500003
real 1m30.249s
user 1m21.284s
sys 0m12.312s
$ time seq $a |sed "$((500000-2)),\$!d; $((500000+3))q" #Stephan's Solution
499998
499999
500000
500001
500002
500003
real 0m0.052s
user 0m0.044s
sys 0m0.004s
$ time seq $a |head -n$((500000+3)) |tail -n+$((500000-2))
499998
499999
500000
500001
500002
500003
real 0m0.024s
user 0m0.024s
sys 0m0.004s
$ time seq $a |sed -n "499998,500003p;500004q"
499998
499999
500000
500001
500002
500003
real 0m0.056s
user 0m0.048s
sys 0m0.004s
решение4
# define line range constants
before=4
line=6
after=3
# setup the sed commands s.t. pattern space holds $before number
# of lines before we hit the line number $line and $after after
s='$!N'
p=`seq -s "$s" "$before"`
a=`seq -s "$s" 0 "$after"`
N=${p//[0-9]/;}
n=${a//[0-9]/;}
# main...
seq 10 |
sed -e "
1{ $N }
\$d;N
$line!D
$n;q
"
Другой метод — это выжать файл и установить FS
так \n
, чтобы поля (теперь строки) были в @F
. Остается только разрезать его по 6-й строке и 4 элементам до и 3 строкам после:
perl -alF\\n -0777ne '$,=$\;print @F[6-4-1..6+3-1]' yourfile
Полученные результаты
2
3
4
5
6
7
8
9