【问题标题】:How to print the whole line that contains a specified byte offset in a file?如何打印文件中包含指定字节偏移的整行?
【发布时间】:2019-05-15 09:24:41
【问题描述】:

我有这样一个例子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 和 byte 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 特定工具打印文件内“包含”字节偏移的整行?

【问题讨论】:

  • 您的示例input.txt 不太清楚它是否是很长的行(包裹在几行以适应此页面布局)或者这些实际上是 3 个不同的行。在长线的情况下,“80”偏移量是有意义的,但不是“理想情况”的输出。
  • 不,它们是几行,这就是我使用代码格式化的原因。我将尝试发布更多 lorem ipsum 和更多示例。

标签: bash shell gnu-coreutils


【解决方案1】:

使用 GNU awk,将目前读取的字节数保存在一个变量中,当它达到您的字节偏移量时,打印当前行并退出。例如:

$ awk -b '{ nb += length + 1 } nb >= 80 { print; exit }' file
eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut

关键字lengthlength($0) 的简写,它以字节为单位返回当前行的长度(感谢-b)。我们需要给它加 1,因为 awk 去掉了行终止符。

【讨论】:

    【解决方案2】:

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

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

    【讨论】:

    • 我确实没有有关于搜索词的信息,我只知道一个字节偏移量和一个文件名。你期望grep -ob '$' $SEARCH_FILE 输出什么?输入文件中没有$ 字符。
    • 然后用你知道的偏移量替换OFFSET_OF_WORD变量,grep -ob $SEARCH_TERM $SEARCH_FILE ...只是搜索单词的偏移量。如果您知道。美好的。 grep -ob '$' $SEARCH_FILE 在文件中搜索行尾。我使用的算法是“检查给定的偏移量是否在一行内,即给定的偏移量在最后一个新行和下一个新行之间”
    • 这很好!但是grep -ob '$' input.txt 什么也不输出,并以零状态退出。我有grep (GNU grep) 3.3
    • 这很奇怪,不知何故-ob 的组合不适用于所有grep 版本。 (我自己用另一个版本测试过)但是对于这个用例,仅使用-b 时的结果是相同的。我编辑了我的答案。
    猜你喜欢
    • 1970-01-01
    • 2012-11-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多