【问题标题】:Replace a string which is present on first line in UNIX file替换 UNIX 文件第一行中存在的字符串
【发布时间】:2014-07-23 04:16:42
【问题描述】:

我想替换第一行中存在的字符串,尽管它也存在于文件的其余行中。我怎样才能通过 shell 脚本做到这一点?有人可以帮我解决这个问题。我的代码如下。我正在从文件中提取第一行,之后我不知道如何进行替换。任何帮助,将不胜感激。谢谢。

伙计们-我想替换 $line 中存在的字符串并将新行写入同一位置的同一文件中。

代码:

while read line
do
        if [[ $v_counter == 0 ]] then
                echo "$line"

                v_counter=$(($v_counter + 1));
        fi
done < "$v_Full_File_Nm"

样本数据:

输入

    BUXT_CMPID|MEDICAL_RECORD_NUM|FACILITY_ID|PATIENT_LAST_NAME|PATIENT_FIRST_NAME|HOME_ADDRESS_LINE_1|HOME_ADDRESS_LINE_2|HOME_CITY|HOME_STATE|HOME_ZIP|MOSAIC_CODE|MOSAIC_DESC|DRIVE_TIME| buxt_pt_apnd_20140624_head_5records.txt
100106086|5000120878|7141|HARRIS|NEDRA|6246 PARALLEL PKWY||KANSAS CITY|KS|66102|S71|Tough Times|2|buxt_pt_apnd_20140624_head_5records.txt

输出

BUXT_CMPID|MEDICAL_RECORD_NUM|FACILITY_ID|PATIENT_LAST_NAME|PATIENT_FIRST_NAME|HOME_ADDRESS_LINE_1|HOME_ADDRESS_LINE_2|HOME_CITY|HOME_STATE|HOME_ZIP|MOSAIC_CODE|MOSAIC_DESC|DRIVE_TIME| SRC_FILE_NM
100106086|5000120878|7141|HARRIS|NEDRA|6246 PARALLEL PKWY||KANSAS CITY|KS|66102|S71|Tough Times|2|buxt_pt_apnd_20140624_head_5records.txt

从上面的示例数据中,我需要将 buxt_pt_apnd_20140624_head_5records.txt 替换为 SRC_FILE_NAME 字符串。

【问题讨论】:

  • UNIX shell 是一个调用工具的环境。处理文本文件的 UNIX 工具是 awk。因此,编写一个 awk 脚本并从 shell 调用它。
  • 您发布的示例数据仅包含 1 行,但它在您的文本中听起来像是在后续行中不替换字符串的解决方案很重要,因此您应该发布一些示例输入/输出以证明该方面您的要求。 ALso - 如果目标字符串在您的第一行出现多次怎么办?如果它作为另一个字符串的一部分出现怎么办?

标签: unix awk sed grep


【解决方案1】:

为什么不使用sed

sed -e '1s/fred/frog/' yourfile

将在第 1 行将 fred 替换为 frog

如果你的“字符串”是一个变量,你可以这样做来扩展变量:

sed -e "1s/$varA/$varB/" yourfile

如果您想就地执行此操作并更改文件,请在 -e 之前添加 -i

【讨论】:

  • 这不是替换字符串,而是替换正则表达式,这是一种非常不同的情况。
  • @EdMorton - 虽然您严格来说是正确的,但我认为就这个问题而言,sed 可以作为一种工具。好像比awk简单。
  • 我认为我们不应该猜测 OP 字符串可能包含或不包含哪些字符。到目前为止发布的一个示例包含一个 RE 元字符。与 awk 中的上述 sed 脚本等效的是 awk 'NR==1{sub(/fred/,"frog")}1' yourfile,它并不比 sed 解决方案复杂,您只是不会在 awk 中编写它,因为与不支持字符串的 sed 不同,您可以在 awk 中选择实际工作在字符串上,因此不依赖于输入数据永远不会包含 RE 元字符的希望。如果有更简短的语法但没有,那就太好了。
  • @EdMorton 我理解一个字符串是 a bunch of characters/digitsfred 在我看来,至少,是一堆字符/数字。
  • ...请注意,除了各种 RE 元字符(.、*、+、?、(、)、[、]、|、\ 在某些位置等),你的“字符串”现在也不能包含“/”。
【解决方案2】:
awk -v old="string1" -v new="string2" '
NR==1 && (idx=index($0,old)) {
    $0 = substr($0,1,idx-1) new substr($0,idx+length(old))
}
1' file > /usr/tmp/tmp$$ && mv /usr/tmp/tmp$$ file

只有在file的第一行出现时,上面才会将string1替换为string2

发布的任何使用 awk 但不使用 index 的解决方案通常都不起作用。对于发布的任何使用 sed 的解决方案都是如此。原因是这些适用于 RE,而不是字符串,因此根据 string1 中存在的字符,字符串替换的行为是不受欢迎的。

看起来 OP 使用 sed RE-replacement 解决方案,因此这仅适用于希望替换字符串的任何其他人:如果您不想内联字符串替换函数,则如下所示:

awk -v old="string1" -v new="string2" '
function strsub(old,new,tgt,     idx) {
    if ( idx = index(tgt,old) ) {
        tgt = substr(tgt,1,idx-1) new substr(tgt,idx+length(old))
    }
    return tgt
}
NR==1 { $0 = strsub(old,new,$0) }
1' file

【讨论】:

  • awk 是否有一个 -i 类型选项,就像 sed 一样?
  • 是的,较新版本的 GNU awk 有 -i inplace,但是,就像 sed 一样,它只是隐藏了您正在使用 tmp 文件的事实,它不会改变您正在使用的事实。
  • @SOaddict 谢谢。 sed “解决方案”当然更简洁,但老实说,它是一个千篇一律的工具,因为在搜索和替换“字符串”中存在各种组合中的各种字符,它们会以奇怪的方式完全弄乱你的文件。如果你很幸运,它们会产生语法错误,但如果不是,它们只会悄悄地替换错误的字符串和/或用错误的替换文本替换正确的字符串。如果这是一个学校项目,那么最坏的情况是你的老师会叮你,但如果这是一个真正的商业应用程序,我会避免这个问题的 sed 解决方案。
【解决方案3】:

bash 解决方案:

file="afile.txt"
str="hello"
repl="goodbye"

IFS= read -r line < "$file"
line=${line/$str/$repl}

tmpfile="/usr/tmp/$file.$$.tmp"

{
  echo "$line"
  tail -n+2 "$file"
} > "$tmpfile" && mv "$tmpfile" "$file"

请注意,上面的 $str 将被解释为“模式”(一种简单的正则表达式),其中 * 匹配任意数量的任意字符,? 匹配任意单个字符,[abc] 匹配任意一个括号中的字符,并且[^abc](或[!abc])匹配括号中的任何一个字符not。见Pattern-Matching

【讨论】:

  • 这不是替换字符串,而是替换正则表达式,这是一种非常不同的情况。
  • @EdMorton 已更改为 bash 解决方案。谢谢指点。
  • 那不是仍然在 REs 而不是字符串上运行吗?它现在还有其他问题,例如,它将字符串 \t 替换为输入行上任何地方的文字制表符并去除任何前导/尾随空格,如果您无法创建 tmp 文件,您将删除内容您的原始文件。
  • @EdMorton 我不确定它将如何用标签替换 \t (例如,echo 仅使用 -e 开关,而 read 似乎没有完全这样做)。通过将IFS 设置为空字符串,可以强制read去除前导/尾随空格。阻止它破坏原始文件很容易(我认为)。但是你是对的,replace 命令使用了一种正则表达式(其中 * 和 ? 和 [...] 是特殊的),所以这不好。我现在正在寻找替代品...
  • read 解释像\t 这样的转义序列,除非您为raw 添加-r 标志。每次使用read 时都应该写IFS= read -r ...,除非你有非常具体的理由不理解所有警告,有点像你应该总是引用你的变量。嗯,也许没有 -r read 只是去掉了反斜杠,我不记得了。无论如何,shell 中的默认行为似乎永远不会像您通常想要的那样只留下该死的文本,但这太糟糕了,但我认为比我更聪明的人有理由这样做......
猜你喜欢
  • 2012-11-19
  • 2021-04-18
  • 1970-01-01
  • 2012-11-06
  • 1970-01-01
  • 2015-11-26
  • 2013-02-20
  • 2013-02-19
  • 2020-09-29
相关资源
最近更新 更多