【问题标题】:Regex to remove lines in file(s) that ending with same or defined letters正则表达式删除文件中以相同或定义的字母结尾的行
【发布时间】:2011-11-21 08:12:58
【问题描述】:

我需要一个 ma​​c osx 以这种方式工作的 bash 脚本:

./script.sh * folder/to/files/ 
#
# or #
#
./script.sh xx folder/to/files/

这个脚本

  • 读取文件列表
  • 打开每个文件并读取每一行
  • 如果行以相同的字母('*' 模式)或自定义字母('xx')结尾,则
    删除行和重新保存文件
  • 备份原文件

我的第一个方法是:

#!/bin/bash

# ck init params
if [ $# -le 0 ]
then
  echo "Usage: $0 <letters>"
  exit 0
fi

# list files in current dir
list=`ls BRUTE*` 
for i in $list 
do 

  # prepare regex    
  case $1 in
       "*") REGEXP="^.*(.)\1+$";;
       *) REGEXP="^.*[$1]$";;
  esac    
  FILE=$i

  # backup file
  cp $FILE $FILE.bak

  # removing line with same letters
  sed -Ee "s/$REGEXP//g" -i '' $FILE
  cat $FILE | grep -v "^$"

done

exit 0

但它并没有像我想要的那样工作......

怎么了?
我该如何修复这个脚本?


示例:

$cat BRUTE02.dat BRUTE03.dat
aa
ab
ac
ad
ee
ef
ff
hhh
$

如果我使用“*”,我希望所有以相同字母结尾的文件都是干净的。
如果我使用“ff”,我希望所有以“ff”结尾的文件都是干净的。


啊,它在 Mac OSx 上。请记住,sed 与经典的 linux sed 有点不同。

人 sed

 sed [-Ealn] command [file ...]
 sed [-Ealn] [-e command] [-f command_file] [-i extension] [file

...]

描述 sed 实用程序读取指定的文件或标准输入 如果没有指定文件,则修改列表指定的输入 的命令。这 然后将输入写入标准输出。

 A single command may be specified as the first argument to sed. 

可以使用 -e 或 -f 选项指定多个命令。全部 命令被应用 按照指定的顺序输入到输入中,而不管它们的 产地。

 The following options are available:

 -E      Interpret regular expressions as extended (modern)

正则表达式而不是基本正则表达式 (BRE)。 re_format(7) 手册页 全面描述了这两种格式。

 -a      The files listed as parameters for the ``w'' functions

默认情况下在任何处理开始之前创建(或截断)。 -a 选项导致 sed 延迟打开每个文件,直到包含的命令 相关的“w”函数应用于一行输入。

 -e command
         Append the editing commands specified by the command

命令列表的参数。

 -f command_file
         Append the editing commands found in the file

command_file 到命令列表。编辑命令应该 每个都列在单独的行上。

 -i extension
         Edit files in-place, saving backups with the specified

扩展名。如果给出零长度扩展名,则不会备份 保存。不推荐 修改为在就地时提供零长度扩展 编辑文件,因为在某些情况下,您可能会面临损坏或部分内容的风险 磁盘空间在哪里 筋疲力尽等

 -l      Make output line buffered.

 -n      By default, each line of input is echoed to the standard

在所有命令都应用到它之后输出。然后 选项抑制了这一点 行为。

 The form of a sed command is as follows:

       [address[,address]]function[arguments]

 Whitespace may be inserted before the first address and the

命令的功能部分。

 Normally, sed cyclically copies a line of input, not including

它的终止换行符,进入模式空间,(除非有 还剩下什么 在 ``D'' 函数之后),应用所有命令 选择该模式空间的地址,将模式空间复制到 标准输出,追加- ing 一个换行符,并删除模式空间。

 Some of the functions use a hold space to save all or part of the

用于后续检索的模式空间。

还有什么?
很清楚我的问题?

谢谢。

【问题讨论】:

  • sed cmd 中这对单引号的用途是什么? sed -Ee "s/$REGEXP//g" -i '' $FILE 。您正在尝试做的事情以及如何做是直接的 sed 用法,我希望它可以在任何地方工作。 OsX 手动引用没有帮助,或者您从手册页中明确引用您认为与众不同的原因。最后,你写道,“不像我想要的那样工作”。请展示 1 你想要什么, 2 你得到什么。祝你好运。
  • MAC OSX 上的 Sed 不使用直接输出。使用-i '' 绕过检查。
  • '不使用直接输出'。请注意您提供的手册页中的这句话:The input is then written to the standard output. 这就像任何其他 sed ;-),老实说!祝你好运!

标签: regex string macos bash sed


【解决方案1】:

我不太了解 bash shell,因此无法评估失败的原因。
这只是对正则表达式的理解(这可能是错误的)。

* 模式正则表达式看起来不错:
^.*(.)\1+$ that ended with same letters..

但字面量模式可能不会像你想的那样。
当前:^.*[$1]$ that ended with 'literal string'
这不应该使用字符类。

改成:^.*$1$

意识到 $1 中的字符串(在它进入正则表达式之前)应该被转义
以防其中包含任何正则表达式元字符。

否则,你打算有一个字符类吗?

【讨论】:

    【解决方案2】:
    perl -ne '
        BEGIN {$arg = shift; $re = $arg eq "*" ? qr/([[:alpha:]])\1$/ : qr/$arg$/}
        /$re/ && next || print
    '
    

    例子:

    echo "aa
    ab
    ac
    ad
    ee
    ef
    ff" | perl -ne '
        BEGIN {$arg = shift; $re = $arg eq "*" ? qr/([[:alpha:]])\1$/ : qr/$arg$/}
        /$re/ && next || print
    ' '*'
    

    生产

    ab
    ac
    ad
    ee
    ef
    

    【讨论】:

      【解决方案3】:

      一个可能的问题:

      • 当您在命令行中输入* 时,shell 会将其替换为您目录中所有文件的名称。您的 $1 永远不会等于 *

      还有一些提示:

      • 你可以替换替换:

      这个:

      # list files in current dir
      list=`ls BRUTE*` 
      for i in $list 
      

      与:

      for i in BRUTE*
      
      • 并且:

      这个:

      猫 $FILE | grep -v "^$"

      与:

      grep -v "^$" $FILE
      

      除了可能的问题之外,我看不到有任何事情发生在我身上。你是什​​么意思干净?你能举个例子说明文件前后的样子以及命令的样子吗?

      【讨论】:

      • 您不需要ls 来使用 for 循环列出文件。 for file in BRUTE* 够了
      • @bash-o-logist:您不需要先使用ls 命令。请记住,每个命令首先由 shell 解释。 shell 搜索它的元字符并将它们替换为匹配文件的名称。这将适用于任何命令。试试echo *(不带引号)与echo "*"(带引号),感受一下shell 的力量。使用外壳,卢克。
      • 我提醒您,如果您只想列出文件名,则不需要 ls 来使用 for 循环。你为什么要提醒我呢? :)。为了克服 shell globbing 的问题,在 bash 中,我们可以打开 nullglob。
      • @bash-o-logist:抱歉。我以为这是对我帖子的回复,我提到您可以使用for i in BRUTE* 而不是使用list=$(ls BRUTE*) 的原始发帖人。
      【解决方案4】:

      这就是问题所在!

      grep '\(.\)\1[^\r\n]$' *
      

      在 MAC OSX 上,( ) { } 等...必须引用!!!

      已解决,谢谢。

      【讨论】:

        猜你喜欢
        • 2023-02-02
        • 2018-08-21
        • 1970-01-01
        • 2017-07-09
        • 1970-01-01
        • 1970-01-01
        • 2011-10-02
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多