【问题标题】:Edit CSV rows in two different ways以两种不同的方式编辑 CSV 行
【发布时间】:2019-06-18 04:45:20
【问题描述】:

我有一个输出两个 CSV 列的 bash 脚本。我需要在第二列包含它们的行的三位数字前面加上“f.”,并保持其余行不变。到目前为止,我尝试了不同的方法,但每种方法都以某种方式失败了。

我尝试的主要是使用正则表达式与第一列或第二列将所需的行与其余行分开,但我不能在不取消或弄乱的情况下同时分开和前置以某种方式处理。到目前为止,我使用的一些命令是:$ sed $ cut 以及(嵌套的)for 循环、read-while 循环、if/else 和 if/else/elif 语句等。下面就是这样的一个(失败)解决方案:

for var1 in "^.*_[^f]_.*"
do
    sed -i "" "s:$MSname::" $pathToCSV"_final.csv"
    for var2 in "^.*_f_.*"
    do
        sed -i "" "s:$MSname:f.:" $pathToCSV"_final.csv"
    done
done

这些是一些示例行:

abc_deg0014_0001_a_1.tif,British Library 1 Front Board Outside
abc_deg0014_0002_b_000.tif,British Library 1 Front Board Inside
abc_deg0014_0003_f_001r.tif,British Library 1 001r
abc_deg0014_0004_f_001v.tif,British Library 1 001v
…
abc_deg0014_0267_f_132r.tif,British Library 1 132r
abc_deg0014_0268_f_132v.tif,British Library 1 132v
abc_deg0014_0269_y_999.tif,British Library 1 Back Board Inside
abc_deg0014_0270_z_1.tif,British Library 1 Back Board Outside

这里 $MSname = British Library 1(由于使用不同的 CSV,“British Library 1”部分可以更改为我需要删除/替换的其他词​​,这就是我使用参数扩展的原因)。

想要的结果

abc_deg0014_0002_b_000.tif,Front Board Inside
abc_deg0014_0003_f_001r.tif,f. 001r
…
abc_deg0014_0268_f_132v.tif,f. 132v
abc_deg0014_0269_y_999.tif,Back Board Inside

如果您仔细观察,您会注意到这些行的第一列中的“f”也与其他行区分开来(前面不应有“f.”的行它们的第二列由“a”、“b”、“y”和“z”区分",分别在第一列)。

【问题讨论】:

  • 您将变量var1 设置为字符串^.*_[^f]_.*,类似地var2,但您从不使用这些变量。拥有它们有什么意义?
  • 一个for对单个字符串的循环相当于只是将字符串分配给循环变量,只是你可以break退出循环直接进入后面的行done(无论如何你都不会在这里使用)。
  • 我取消了 for 循环,但你是对的。

标签: regex bash csv replace


【解决方案1】:

您没有使用var1var2 做任何事情,即使您这样做了,循环变量并在同一个输出文件上重复运行sed -i 也是非常浪费的。理想情况下,您希望将所有修改写入单个 sed 脚本,并且只处理一次文件。

无法猜测除了"British Library 1" 之外还有哪些字符串以及这些字符串是否需要不同类型的操作,我会建议类似于

sed -i '/^[^,]*_f_[^,_]*,/s/,British Library 1 /,f. /
    s/,British Library 1 /,/' "${pathToCSV}_final.csv"

注意单引号中的sed 脚本如何被包裹在多条物理行上。第一行查找第一个逗号分隔列中下划线之间的最后一个字符为f 的任何行,并将",British Library 1 " 替换为",f. "。 (我在这里对间距做了一些调整——我希望它们对你有意义。)在下面的行中,我们只需用逗号替换任何(剩余的)",British Library 1 ";这个想法是,只有与前一行的正则表达式不匹配的行仍然包含这个字符串,所以我们不必再做一次正则表达式匹配。

这可以很容易地扩展到在同一个sed 脚本中覆盖更多模式,而不是重复循环文件并一次重写一个模式。例如,如果您的下一个任务是根据第一个字段中倒数第二个下划线分隔的子字段是否包含 a,将 Windsor Palace A 替换为 a. 或不替换,这应该很明显:

sed -i '/^[^,]*_f_[^,_]*,/s/,British Library 1 /,f. /
    s/,British Library 1 /,/
    /^[^,]*_a_[^,_]*,/s/,Windsor Palace A /,a. /
    s/,Windsor Palace A /,/' "${pathToCSV}_final.csv"

正则表达式更详细地说

^       beginning of line
[^,]*   any sequence of characters which are not a comma
_f_     literal characters underscore, f, underscore
[^,_]*  any sequence of characters which are not a comma or an underscore 
,       literal comma

您应该能够看到这将针对第一列中的最后一对下划线。重要的是永远不要跳过第一个逗号,并且在接近结尾时,在我们最终允许逗号列分隔符之前,不允许在我们专门针对的逗号之后出现任何下划线。

最后,还要注意我们总是在包含文件名的变量周围使用双引号。在某些情况下您可以避免这种情况,但您必须知道自己在做什么;简单直接的经验法则是始终在变量周围加上双引号。有关完整的独家新闻,请参阅When to wrap quotes around a shell variable?

【讨论】:

  • 太棒了!这非常有效。只有我必须将单引号修改为双引号(并且还将 CSV 标题中的另一个开头双引号移动到它自己的 $pathToCSV 之后),因为我在内部使用了参数扩展(即 $MSname 我在我的原始帖子),它负责变量字符串“British Library 1”:echo MS name as recorded in TIFF header to be removed: read MSname sed -i "" "/^[^,]*_f_[^,_]*,/s/,$MSname /,f. / s/,$MSname /,/" $pathToCSV"_final.csv" PS:由于我的 shell 的语法(in)兼容性,我还必须在-i 标志之后添加""
  • _final.csv 不必用引号括起来,但它前面的变量非常重要。为方便起见,我将所有内容都放在引号内;但是没有理智的方法可以将 that 部分移到引号之外。如果要在引号内使用变量,则将单引号切换为双引号是有意义的;但正确的解决方案是删除所有 shell 循环并构建一个 sed 脚本,其中包含您要执行的所有替换。
  • 无论哪种方式都可以(我尝试了两种方法,即只使用您在上面的评论中建议的引号内的变量,然后使用我最初使用的固定部分 _final.csv 在引号内)。当这种分隔(变量和固定部分之间)被删除时,它给了我undefined label 错误,这是有道理的。似乎重要的是分隔而不是插入引号的位置,所以只要我保持变量与 CSV 标题的固定部分不同,一种或另一种方式,就可以了。
  • 这就是为什么我在变量名周围加上大括号;没有它们,$pathToCSV_final 会查找名称为 pathToCSV_final 的变量。只要该值不包含空格或 shell 元字符,您就可以在不引用它的情况下逃脱,但是(正如我链接到的引用问题中更深入地解释的那样)当您实际尝试它时很容易产生错误-世界文件名可能包含两者,通常很难调试,特别是如果您不熟悉 shell 脚本。
【解决方案2】:

使用awk,您可以查看第一个字段以查看它是否与“3digits + 1 个字母”匹配,然后在这种情况下使用f. 打印,在另一种情况下只需删除字段 2,3 和 4。例如:

awk -F'[, ]' '{
   if($5 ~ /.?[[:digit:]]{3}[a-z]$/) {
      printf("%s,f. %s\n",$1,$5)} 
   else {
      printf("%s,%s %s %s\n",$1,$5,$6,$7)
   }
 }' test.txt

在您提供的示例中,它给出了:

abc_deg0014_0001_a_1.tif,外前板

abc_deg0014_0002_b_000.tif,前板内部

abc_deg0014_0003_f_001r.tif,f。 001r

abc_deg0014_0004_f_001v.tif,f. 001v

abc_deg0014_0267_f_132r.tif,f。 132r

abc_deg0014_0268_f_132v.tif,f. 132v

abc_deg0014_0269_y_999.tif,背板内侧

abc_deg0014_0270_z_1.tif,背板外侧

【讨论】:

  • 这对固定数量的空格分隔字段有一些潜在的不幸假设。
  • 确实如此。另一种方法是假设字符串British Library 1 是固定的并按照您的建议替换它。然而,您的解决方案可以被视为更具限制性,因为除了修复内容所需的字段数量之外。这真的取决于实际数据和需求。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-03-06
  • 2016-09-23
  • 1970-01-01
  • 2012-03-13
  • 2016-10-24
  • 1970-01-01
相关资源
最近更新 更多