【问题标题】:How to update a multichracter delimited field in a file in bash?如何在bash中更新文件中的多字符分隔字段?
【发布时间】:2022-01-06 15:10:31
【问题描述】:

我正在尝试匹配某个字段并从由多个字符分隔的文件中更新其数据。我正在使用它来模拟 SQL 的 UPDATE。这是使用 bash 创建迷你 DBMS 的更大项目的一部分。

我尝试了什么:

sed "s/\^\_\^/:/g" $file_path | cut -d: -f1 | grep -nw SAR | sed "s/\^\_\^/:/g" | cut -d: -f2 | sed -i "s/$match/$update/g"

我的问题是我无法使用 sed -i 仅更新找到的这些特定列,因为您无法通过管道输入它。

使用的分隔符是:^_^

数据文件示例:

'EGP'^_^'Egypt'
'SAR'^_^'Europe'
'SAR'^_^'Europe'
'SAR'^_^'Europe'
'SAR'^_^'Europe'
'Europe'^_^'SAR'
'SAR'^_^'Europe'
'MYR'^_^'Malaysia'
'MYR'^_^'Malasia'

例如,我的$match 可以是SAR$update 可以是USD

对数据文件的预期更改

'EGP'^_^'Egypt'
'USD'^_^'Europe'
'USD'^_^'Europe'
'USD'^_^'Europe'
'USD'^_^'Europe'
'Europe'^_^'SAR'
'USD'^_^'Europe'
'MYR'^_^'Malaysia'
'MYR'^_^'Malasia'

如果有更好的不同方法,我也很欢迎,因为我对 bash 脚本还很陌生。

【问题讨论】:

  • 如果您有字段,awk 可能是比sed 更好的选择。请参阅 awk 文档中的 awk FS 变量(字段分隔符)。

标签: bash awk sed grep


【解决方案1】:

最好在这里使用awk

awk -v s="'SAR'" -v q="'USD'" -F'\\^_\\^' -v OFS='^_^' '$1==s {$1=q} 1' file

'EGP'^_^'Egypt'
'USD'^_^'Europe'
'USD'^_^'Europe'
'USD'^_^'Europe'
'USD'^_^'Europe'
'Europe'^_^'SAR'
'USD'^_^'Europe'
'MYR'^_^'Malaysia'
'MYR'^_^'Malasia'

说明:

  • -v s="'SAR'" 设置命令行变量 s='SAR'
  • -v q="'USD'" 设置命令行变量 s='USD'
  • -F '\\^_\\^' 将输入字段分隔符设置为 ^_^。我们需要转义 ^,因为它是一个特殊的正则表达式元字符,我们需要对它进行双重转义,因为我们在稍后转换为正则表达式(字段分隔符)的字符串中使用它。
  • -v OFS='^_^' 将输出字段分隔符设置为 ^_^
  • $1 == s 将第一个字段与 'SAR' 进行比较
  • $1 = q$1 设置为变量 'USD'

【讨论】:

  • 简单一点,也许:awk -v s="'SAR'" -v q="'USD'" -F'\\^_\\^' '$1==s {$1=q} 1' file.
  • 谢谢,在-F 周围使用单引号可以减少转义。但是我们仍然需要设置OFS
  • 糟糕,是的,我忘记了,抱歉。
  • 相当优雅,非常感谢!最后一个问题,因为我对 awk 不是很熟悉。 awk 脚本末尾的 1 有什么作用?
  • 尾随1(或任何非零值)打印输入的每一行。
【解决方案2】:

使用 sed,您可以在字符串 ^ 的开头使用 $match,并将其更改为 $update

match="'SAR'"
update="'USD'"
sed "s/^$match\^_\^/$update^_^/" file

输出

'EGP'^_^'Egypt'
'USD'^_^'Europe'
'USD'^_^'Europe'
'USD'^_^'Europe'
'USD'^_^'Europe'
'Europe'^_^'SAR'
'USD'^_^'Europe'
'MYR'^_^'Malaysia'
'MYR'^_^'Malasia'

查看bash demo

【讨论】:

  • 简单一点,也许:match="'SAR'"; update="'USD'"; sed "s/^$match\\^_\\^/$update^_^/" file.
  • @RenaudPacalet 在变量中使用单引号您的意思是对吗?这确实使它更具可读性。
  • 这绝对有效,唯一的问题是如果我想更新第二个字段会发生什么? ^ 只匹配给定模式的开头。
  • @Stoon 如果要匹配第 n 个匹配项,可以使用量词和捕获组在匹配要替换的内容之前跳过 n 个字段。就像 sed "s/^\(\('[^']*'\^_\^\)\{2\}\)$match\^_\^/\1$update^_^/g" filethis example 但在这种情况下 awk 会更合适。
  • 我明白了,没有意识到你可以用 sed 做到这一点。有点复杂但有效。我想我会像你一样继续使用 awk 并建议接受的答案:)
【解决方案3】:

使用您展示的示例,请尝试关注awk 程序。

awk -v matchvalue="'SAR'" -v updatedvalue="'USD'" '
match($0,"^"matchvalue"\\^_\\^"){
  print updatedvalue"^_^" substr($0,RSTART+RLENGTH)
  next
}
1
'  Input_file

说明: 创建 2 个名为 matchvalueupdatedvalue 的变量,其中 OP 在示例中显示了 'SAR''USD' 值。然后在主程序中使用match 函数来匹配一行是否以'SAR'^_^ 开头,然后在其位置打印新值,然后打印该行的其余部分,如果该行不是从'SAR'^_^ 开始,则简单地打印它。

【讨论】:

    【解决方案4】:

    这是我最终做的:

    escaped_delm=$(echo $curr_delim | sed 's/[^^\\]/[&]/g; s/\^/\\^/g; s/\\/\\\\/g')
    awk -F"$escaped_delm" -v a_col_update=$update_field -v pick=$p_tmp_field -v a_del="$match" -v a_ins="$insert" -v OFS="$curr_delim" '$pick==a_del {$a_col_update=a_ins} 1' $t_path > tmp && mv tmp $t_path
    

    我面临的最大问题是我将分隔符 ^_^ 作为变量传递给 awk。我的脚本假定我不知道变量是什么,并且它在脚本的开头被传递给它。这会导致一个问题,就像第一个答案建议 awk-F 选项认为 ^ 是一个正则表达式参数一样。

    这就是我使用的sed 命令非常方便的地方!它通过转义任何正则表达式元字符来清理分隔符。感谢 Ed Morton 对 this question 的出色回答,我从那里得到了 sed 命令。

    希望其他人觉得这很有用!

    我的 awk 变量是:

    1. curr_delim="^_^"
    2. a_col_update=<column number to be updated>
    3. pick=<column used to pick the record/row>
    4. a_del=<field to be updated from picked record/row>
    5. a_ins=<new value for the picked field>
    6. OFS="$curr_delim"

    【讨论】:

      猜你喜欢
      • 2017-04-02
      • 2014-10-14
      • 1970-01-01
      • 2019-04-05
      • 2011-08-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-06-30
      相关资源
      最近更新 更多