Как напечатать всю строку, которая содержит указанное байтовое смещение в файле? - PullRequest
6 голосов
/ 15 мая 2019

У меня есть такой пример input.txt file:

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut
enim ad minim veniam, quis nostrud exercitation ullamco laboris
nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor
in reprehenderit in voluptate velit esse cillum dolore eu fugiat
nulla pariatur. Excepteur sint occaecat cupidatat non proident,
sunt in culpa qui officia deserunt mollit anim id est laborum.

Теперь я могу легко найти слово и получить смещение в байтах:

$ grep -ob incididunt /dev/null input.txt 
input.txt:80:incididunt

К сожалению, информация о содержании строки и о найденном слове теряется. Я знаю только имя файла и смещение байта 80. Я хочу напечатать всю строку, которая содержит это байтовое смещение внутри файла.

В идеале это было бы для получения script.sh, что с двумя параметрами, именем файла и смещением байтов, выводится искомая строка:

$ ./script.sh input.txt 80
eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut

Другие примеры:

Для файла = input.txt и байтового смещения = 130 вывод должен быть:

enim ad minim veniam, quis nostrud exercitation ullamco laboris

Для файла = input.txt и любого байтового смещения от 195 до 253 вывод должен быть:

nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor

Для файла = input.txt и байтового смещения = 400 вывод должен быть:

sunt in culpa qui officia deserunt mollit anim id est laborum.

Я пробовал:

Я могу печатать от байтового смещения до конца строки с помощью gnu sed, однако это пропускает часть eiusmod tempor. Я не могу придумать ни малейшего представления о том, как вернуться «назад» в файл, чтобы извлечь часть с новой строки до смещения этого байта.

$ sed -z 's/.\{80\}\([^\n]*\).*/\1\n/' input.txt 
incididunt ut labore et dolore magna aliqua. Ut

Я могу читать символ за символом, запоминать последний символ новой строки и печатать с последнего символа новой строки до следующего. Это не будет работать с оболочками read, так как пропускает символы новой строки. Я думаю, что смогу заставить его работать с помощью dd, но наверняка должно быть более простое решение.

set -- inpux.txt 80
exec 10<"$1"
pos=0
lastnewlinepos=0
for ((i=0;i<"$2";++i)); do
        IFS= read -r -u 10 -N 1 c
        pos=$((pos+1))
        # this will not work..., read omits newlines
        if [ "$c" = $'\n' ]; then
                lastnewlinepost="$pos"
        fi
done
# as I know the last newline before the offset, it's ok to use this now
sed -z 's/.\{'"$lastnewlinepos"'\}\([^\n]*\).*/\1\n/' "$1"

Как напечатать всю строку, которая "содержит" смещение байта внутри файла, используя специальные инструменты bash и * nix?

Ответы [ 2 ]

5 голосов
/ 15 мая 2019

Сохраните количество байтов, прочитанных до сих пор в переменной, когда оно достигнет вашего смещения байтов напечатайте текущую строку и выйдите.

$ awk '{read+=1+length} read>=80{print;exit}' input.txt
eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut
$ awk '{read+=1+length} read>=130{print;exit}' input.txt
enim ad minim veniam, quis nostrud exercitation ullamco laboris

length - длина текущей строки, нам нужно добавить к ней 1, потому что awk обрезает разделитель записей (\n по умолчанию) из строк.


Обратите внимание, что length будет подсчитывать символы, которые могут занимать до шести байтов в зависимости от локали. Для подсчета байтов необходимо установить переменную окружения LC_ALL в C при запуске awk, например:

LC_ALL=C awk '{read+=1+length} read>=130{print;exit}' input.txt
1 голос
/ 15 мая 2019

Пожалуйста, попробуйте следующее, вы можете настроить ввод / вывод в соответствии с вашими потребностями, но при этом вы получите фактическое смещение слова и строку, содержащую слово:

#!/bin/bash
SEARCH_TERM="$1"
SEARCH_FILE="$2"
OFFSET_OF_WORD="`grep -ob $SEARCH_TERM $SEARCH_FILE | cut -d':' -f1`"

lastNewLinePos=0
lineNumber=0
for newLinePos in $(grep -b '$' $SEARCH_FILE | cut -d':' -f1)
do
    if (( $OFFSET_OF_WORD >= lastNewLinePos && $OFFSET_OF_WORD < $newLinePos )); then
        echo "Offset: $OFFSET_OF_WORD"
        echo "Line: `sed -n ${lineNumber}p $SEARCH_FILE`"
        break
    fi
    lastNewLinePos=$newLinePos
    let lineNumber++
done

РЕДАКТИРОВАТЬ: Протестировано с вашимзаданный ввод и выполняется как

./getLineByOffset.sh incididunt input.txt

Редактировать 2: Если вы знаете только смещение, а не фактический поисковый термин

#!/bin/bash
OFFSET_OF_WORD="$1"
SEARCH_FILE="$2"

lastNewLinePos=0
lineNumber=0
for newLinePos in $(grep -b '$' $SEARCH_FILE | cut -d':' -f1)
do
    if (( $OFFSET_OF_WORD >= lastNewLinePos && $OFFSET_OF_WORD < $newLinePos )); then
        echo "Offset: $OFFSET_OF_WORD"
        echo "Line: `sed -n ${lineNumber}p $SEARCH_FILE`"
        break
    fi
    lastNewLinePos=$newLinePos
    let lineNumber++
done
...