однострочный: вывести все строки, кроме последних 3?
Я хотел бы смоделировать GNU head -n -3
, который печатает все строки, кроме последних 3, потому что head
во FreeBSD не имеет этой функции. Поэтому я думаю о чем-то вроде
seq 1 10 | perl -ne ...
Здесь я использовал 10 строк, но это может быть любое число больше 3.
Можно ли это сделать на Perl или каким-то другим способом на FreeBSD в BASH?
Супер примитивное решение будет
seq 1 10 | sed '$d' | sed '$d' | sed '$d'
10 ответов:
seq 1 10 | perl -e '@x=("")x3;while(<>){print shift @x;push @x,$_}'
Или
perl -e '@x=("")x3;while(<>){print shift @x;push @x,$_}' file
Или
command | perl -pe 'BEGIN{@x=("")x3}push @x,$_;$_=shift @x' perl -pe 'BEGIN{@x=("")x3}push @x,$_;$_=shift @x' file
Чистый Баш и простые инструменты (wc и cut):
head -n $(($(wc -l file | cut -c-8)-3)) file
Отказ от ответственности-у меня сейчас нет доступа к FreeBSD, но это работает на OSX bash.
Похоже, никто не использует
sed
иtac
, поэтому вот один:$ seq 10 | tac | sed '1,3d' | tac 1 2 3 4 5 6 7
Как насчет:
seq 1 10 | perl -ne 'print if ( !eof )' | perl -ne 'print if ( !eof )' | perl -ne 'print if ( !eof )'
Или сделайте это с bash самостоятельно, если у вас есть версия 4.0 или новее:
seq 1 10 | (readarray -t LINES; printf '%s\n' "${LINES[@]:(-3)}")
Обновление: в этом случае будут удалены последние три строки, а не показаны только они.
seq 1 10 | (readarray -t L; C=${#L[@]}; printf '%s\n' "${L[@]:0:(C > 3 ? C - 3 : 0)}")
Для удобства его можно поместить на функцию:
function exclude_last_three { local L C readarray -t L; C=${#L[@]} printf '%s\n' "${L[@]:0:(C > 3 ? C - 3 : 0)}" } seq 1 10 | exclude_last_three seq 11 20 | exclude_last_three
Вот запоздалый ответ, потому что вчера я столкнулся с чем-то подобным.
Это решение:
- чистый Баш
- однострочный
- считывает входной поток только один раз
- считывает входной поток построчно, а не все сразу
Протестировано на Ubuntu, Redhat и OSX.
Он работает путем считывания строк в циклический буфер, реализованный в виде N-элементного массива.$ seq 1 10 | { n=3; i=1; while IFS= read -r ln; do [ $i -gt $n ] && cat <<< "${buf[$((i%n))]}"; buf[$((i%n))]="$ln"; ((i++)); done; } 1 2 3 4 5 6 7 $
N-число строк, которые нужно отрезать от конца строки. файл.
Для каждой строки i, которую мы читаем, мы можем воспроизвести строку i-n из кругового буфера, а затем сохранить строку i в круговом буфере. Ничто не повторяется до тех пор, пока не будут прочитаны первые n строк. (I mod n) - это индекс в массиве, который реализует циклический буфер.
Поскольку требование касается однострочного текста, я постарался сделать его достаточно кратким, к сожалению, в ущерб удобочитаемости.