【问题标题】:Find pattern in line and find last word, if matches write line and previous one to file在行中查找模式并查找最后一个单词,如果匹配写入行和前一个到文件
【发布时间】:2021-05-31 22:39:08
【问题描述】:

查找以“DESC:”开头的行,找到最后一个单词,如果匹配:将行和上一行写入另一个文件

我读到了这个Replacing the last word of a line only if match string found 但不幸的是

我有一个长文本文件,其中包含 2 行连续的行:一个带有文件路径,下一个带有描述,例如 f.i.:

PATH: /all movies/DE/0051.mkv
DESC: Bloodshot German
PATH: /all movies/DE/0052.mkv
DESC: Birds of Prey German
PATH: /all movies/EN/0074.mkv
DESC: Army of One English

实际上,如果最后一个单词匹配以“DESC:”开头的行,则将行和上一行写入另一个文件。

我现在使用“while read loop”,但这太慢了。

DIR="c:/all movies/"
FILE1="${DIR}/movies_GE.txt"; echo "MOVIES GERMAN"  > ${FILE1)
FILE2="${DIR}/movies_EN.txt"; echo "MOVIES ENGLISH" > ${FILE2)

while read LINE1; do
  if [[ ${LINE1:0:4} = "PATH:" ]]; then
    read LINE2
    if [[ ${LINE2:0:4} = "DESC:" ]]; then
      LASTWORD=`awk '{print $NF}' <<< ${LINE2}`
      if grep -iq "German"  <<< ${LASTWORD}; then echo ${LINE1} >> ${FILE1}; echo ${LINE2} >> ${FILE1}; fi
      if grep -iq "English" <<< ${LASTWORD}; then echo ${LINE1} >> ${FILE2}; echo ${LINE2} >> ${FILE2}; fi
    fi
  fi
done < ${DIR}/all movies/movies_ALL.txt

是否有(很多)更好/更快的解决方案 f.i.用 sed?

我试过了:

sed -ir '/^"DESC:":.*/s/^(.* )German$//g'  ${FILE1}
sed -ir '/^"DESC:":.*/s/^(.* )English$//g' ${FILE2}

【问题讨论】:

  • sed 无法将行重定向到不同的文件。使用awk

标签: bash shell sed grep


【解决方案1】:

这可能对你有用(GNU sed):

sed -ne 'N;/\nDESC/{/.*German$/w file1' -e '/.*English/w file2' -e '};D' file

关闭隐式打印-n

维护一个两行窗口。

如果第二行以DESC 开头并且最后一个单词是GermanEnglish,则写入file1 或file2。

删除第一行并重复。

【讨论】:

  • 谢谢。我对我不理解的 D(删除第一行)感到困惑,但它是在模式空间中删除而不是物理行;)我怎样才能让它不区分大小写?我试过了。使用'/w',使用'/iw'或'/Iw',可惜
  • @ni_hao 在 GNU sed 中,您可以添加 I 标志以匹配不区分大小写,即 sed -ne 'N;/\nDESC/I{/.*German$/Iw file1' -e '/.*English/Iw file2' -e '};D' file
  • 谢谢,我在 sed 是 busybox v1.24.1 的一部分并且不支持 -I 参数的设备上使用它。
【解决方案2】:
awk -v dir=/some/dir '
BEGIN{
   # provide mapping: last word -> filename to write to
   files["English"] = dir "movies_EN.txt"
   files["German"] = dir "movies_DE.txt"
}
# remember path
/^PATH: /{path=$0}
# when desc
/^DESC: /{
   # extract last word
   w=$0; gsub(/ *$/, "", w); gsub(/.* /, "", w);
   # write to one of files, if exsits
   if (w in files) {
       printf "%s\n%s", $0, path >> files[w]
   }
}'

【讨论】:

    【解决方案3】:

    Awk 会是一个更好的选择:

    awk '/^PATH/ { path=$0 } /^DESC/ && /English$/ { printf "%s\n%s",$0,path  }' file > newfile
    

    如果该行以“PATH”开头,则为该行设置一个可变路径 ($0)。然后当该行以“DESC”开头并且该行以“English”结尾时,将行和路径打印到newfile

    【讨论】:

    • 谢谢,但是写入的文件会是0字节;所以什么也没写。
    • 该解决方案使用发布的测试数据在本地工作。你有什么版本的 awk?
    • 我猜输入有 DOS 换行符,因此与 /English$/ 不匹配。尝试/English\r?$/ 或修复文件(无论如何它都不是有效的文本文件;不要使用 Windows 编辑器,或者根本不要使用 Windows)。
    • 那么它可以在行尾更正的情况下工作吗?
    • 它有效但不好:我有一个文件,其中包含多行以“PATH:”开头的文件,后跟一个“DESC:”行,而不仅仅是“DESC”中的“German”或“English”: '-line。它会找到(并将几乎所有行写入文件中,但每次第一个找到的带有 'PATH:' 的正确行都不会被写入。所以如果我有这个:PATH: /all movies/DE/0043.mkv DESC: Mon amour French PATH: /all movies/DE/0052.mkv DESC: Birds of Prey German. 它开始写入使用 'DESC: Birds of Prey German' 而不是之前的 'PATH:'-line
    猜你喜欢
    • 2013-09-03
    • 1970-01-01
    • 2021-07-27
    • 2013-06-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多