【问题标题】:BASH: Split strings without any delimiter and keep only first sub-stringBASH:不带任何分隔符拆分字符串,只保留第一个子字符串
【发布时间】:2017-03-13 12:57:02
【问题描述】:

我有一个包含 7 列的 CSV 文件,我有兴趣只修改第一列。事实上,在某些行中,行名以串联的方式出现 n 次,没有任何空格。我需要一个脚本来识别重复的开始位置并删除所有重复。

行名示例

行名 = EXAMPLE1.ABC_DEF.panel4EXAMPLE1.ABC_DEF.panel4EXAMPLE1.ABC_DEF.panel4

替换为:EXAMPLE1.ABC_DEF.panel4

在不同的行中:

  • n 可以变化

  • 行名的长度可以变化

  • 行名的结构可以变化(例如,_. 的数量),但它总是在没有任何空格的情况下进行排序

我尝试过的:

:%s/(.+)\1+/\1/

一步一步:

  • %s: 替换整个文件

  • (.+)\1+:第一个捕获组。 .+ 匹配任何字符(行终止符除外),+ 是量词——匹配一次到无限次,尽可能多次,根据需要返回。

  • \1+:匹配第一个捕获组最近匹配的相同文本

  • 替换为\1

但是,我收到以下错误:

  • E65:非法反向引用

  • E476:无效命令

【问题讨论】:

  • 产生这些错误的工具是什么?您发布的命令与 sed 一起使用时符合您的预期:sed -E 's/(.+)\1+/\1/' file
  • @oliv 看起来像 vi/Vim?
  • 你必须逃跑::%s/\(.\+\)\1\+/\1/
  • @BenjaminW。这是我的想法,并尝试过,但我只是没有收到这些错误......
  • @oliv 我得到了它们,未转义的(),然后是未转义的+,但这可能取决于您使用的特定版本。

标签: regex bash csv split


【解决方案1】:

据我了解,您只需要一行包含EXAMPLE1.ABC_DEF.panel4。在这种情况下,您可以执行以下操作:

首先删除一行中的重复项:

sed -i "s/EXAMPLE1.ABC_DEF.panel4.*/EXAMPLE1.ABC_DEF.panel4/g"

然后删除重复的行:

awk '!a[$0]++'

【讨论】:

  • 感谢@Nasr 的回答。然而,这并不是我想要的。 CSV 文件包含具有不同行名的行。所以我试图重命名名称以重复形式出现的所有行。我希望这次能解释得更好。
【解决方案2】:

如果你所有的行都是你在问题中给出的格式(如EXAMPLExyzEXAMPLExyz),那么这应该可以工作-

awk -F"EXAMPLE" '{print FS $2}' file

这将“EXAMPLE”作为字段分隔符并要求它仅打印第一个“列”。它将“EXAMPLE”添加到第一列(通过调用内置的awk 变量FS)。谢谢,@andlrc。

不是一个理想的解决方案,但对于此目的可能已经足够了。

【讨论】:

  • 我认为使用 multichar 作为字段分隔符是 GNU 扩展。可以使用变量FS避免重写EXAMPLE... print FS $2 ...
【解决方案3】:

这个脚本,第一个参数是要测试的字符串,可以检索最大的重复子字符串(即“totototo”完成“toto”,而不是“to”)

#!/usr/bin/env bash
row_name="$1"
#test duplicate from the longest to the smallest, by how many we need to split the string ?
for (( i=2; i<${#row_name}; i++ ))
do
  match="True"
  #continue test only if it's mathematically possible
  if (( ${#row_name} % i )); then
    continue
  fi
  #length of the potential duplicate substring
  len_sub=$(( ${#row_name} / i ))
  #test if the first substring is equal to each others
  for (( s=1; s<i; s++ ))
  do
    if ! [ "${row_name:0:${len_sub}}" = "${row_name:$((len_sub * s)):${len_sub}}" ]; then
      match="False"
      break
   fi
  done
  #each substring are equal, so return string without duplicate
  if [ $match = "True" ]; then
    row_name="${row_name:0:${len_sub}}"
    break
  fi
done
echo "$row_name"

【讨论】:

    猜你喜欢
    • 2016-12-27
    • 1970-01-01
    • 2018-12-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-11-26
    • 1970-01-01
    相关资源
    最近更新 更多