如何在文件中打印包含指定字节偏移量的整行?

时间:2019-05-15 09:24:42

标签: bash shell gnu-coreutils

我有一个示例input.txt文件:

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输入一个单词并获取其字节偏移量:

$ 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

对于file = input.txt和195到253之间的任何字节偏移,输出应为:

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

对于file = input.txt和字节offset = 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

我可以逐字符读取字符,记住上一个换行符,并从最后一个换行符打印到下一个。这将不适用于shell 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 个答案:

答案 0 :(得分:5)

当变量达到您的 byte offset 打印当前行并退出时,请保留变量中到目前为止已读取的字节数。

$ 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将对字符进行计数,根据语言环境的不同,最多可能需要六个字节。要使其计数字节,您需要在运行awk时将环境变量LC_ALL设置为C,例如:

LC_ALL=C awk '{read+=1+length} read>=130{print;exit}' input.txt

答案 1 :(得分:1)

请尝试以下操作,您可以根据需要调整输入/输出,但这会向您输出单词和包含单词的行的实际偏移量:

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