【问题标题】:bash script: write string with double quotes and blanks to filebash脚本:将带有双引号和空格的字符串写入文件
【发布时间】:2015-03-30 10:02:56
【问题描述】:

我尝试使用 sed 从 ASCII 文件中读取一行,对其进行解析并将其写入输出文件中的定义行号。

输入文件中的行格式如下:

linenumber:designator,"variable text content"

例如

3:string1,"this is text of string 1"

所以第 3 行的 outfile 应该如下所示:

string1,"this is text of string 1"

该行包括双引号和空格。所有旧行都下移了一行。

用户负责提供有关行顺序的正确输入文件,并且必须考虑输出文件中的行随着输入文件中的每一新行而下移。除了输入文件中给出的行号之外,脚本不知道任何顺序。

脚本应读取所有行并将这些行的内容放入给定行号的输出文件中

  • 包括双引号和空格
  • 没有行号部分和冒号

我在 shell 中成功使用的命令是 e.g.:

sed -i '3istring1,"this is text of string 1"' outfile

引号,双引号和空格没有问题。 使用 bash 脚本

while read line           
do           
    linenum=$(echo $line | cut -f1 -d:)
    linestr=$(echo $line | cut -f2 -d:)
    sedcmd="sed -i '"
    sedcmd=${sedcmd}${linenum}
    sedcmd=${sedcmd}i
    sedcmd=${sedcmd}${linestr}
    sedcmd=${sedcmd}"' outfile"
    echo "---> $sedcmd"
    $sedcmd
done < script/new_records.txt

显示与 echo 完全相同的 sed 命令,但返回:

sed: -e expression #1, char 1: unknown command: `''

显然,从 bash 脚本中执行 sed 命令与直接在 bash shell 中执行它是不同的。 我在引号、双引号和空格之前尝试了各种转义序列“\”……但相当随机,都没有成功。

我需要做什么才能将包含空格和双引号的字符串写入文本文件中的指定行?

【问题讨论】:

  • 请提供一些示例输出。例如,给定一个数据文件1\n2\n3\n4\n5\n6 和一个输入文件2:foo,"bar"\n4:baz,"qux",结果应该是什么?与4:baz,"qux"\n2:foo,"bar"相同转换的结果应该不同吗?
  • 我编辑了帖子以澄清问题。用户负责输入文件中的正确顺序。
  • 我很抱歉,但我的意思从编辑中并不清楚。我的意思是:4:baz,"qux" 是否意味着baz,"qux" 应该作为输出中的第四行出现,或者应该插入到输入中最初的第四行之前?当在该点之前已经插入一行时,这会有所不同。反过来也很重要——如果插入第四行,然后插入第二行,第四行将被向下推。这是想要的吗? (哦,应该插入到指定行之前还是之后?)

标签: bash shell sed


【解决方案1】:
# Assuming OutFile exist and have enough line
while read ThisLine           
 do
    LineNum=$(echo "${ThisLine}" | cut -f1 -d ":" )
    echo "${ThisLine##*:}" > /tmp/LineContent.txt

    sed -i -n "${LineNum} !{p;b;};r /tmp/LineContent.txt" OutFile
 done < script/new_records.txt

不是最好的事情,因为你假设很多问题,比如在 outfile 中有足够的行,读取行没有问题(引用字符串中的转义字符,...)可能会发生

【讨论】:

    【解决方案2】:

    好的,我试一试。如果我理解您正在尝试正确执行的操作,并且您确定代码输入文件没有格式错误,那么

    sed -i -f <(sed 's/:/i/' insertions.txt) datafile.txt
    

    是最直接的方法。这是有效的,因为输入规范为

    number:text
    

    只需将: 替换为i 即可获得一个sed 命令,该命令显示:“处理行number 时,插入text”。 &lt;() 位是 bash 样式的命令替换,它扩展为 FIFO 的名称,可以从中读取命令的输出。

    通过说类似的话来防止错误可能是谨慎的

    sed -i -f <(sed '/^[0-9]\+:/!d; s/:/i/' insertions.txt) datafile.txt
    

    这会删除 insertions.txt 中所有不以数字开头且后跟冒号的行,因为这些行显然已损坏。

    请注意,这种一次性方法将行号视为输入文件中的行号。也就是说,给定一个包含内容的插入文件

    2:foo,"bar "
    4:baz,"qux "
    

    baz,"qux " 将出现在输出的第 5 行(输入的第 4 行之前)。如果不希望这样做,则必须多次调用 sed 以单独处理每个插入,如

    while read insertion; do
      sed -i "${insertion/:/i}" datafile.txt
    done < insertions.txt
    

    ${insertion/:/i} 是另一种 bashism,它将 shell 变量中的第一个 : 替换为 i 并扩展为结果,即如果 insertion=1:2:3,则 ${insertion/:/i}1i2:3

    【讨论】:

    • 非常感谢。我需要循环多次调用sed。除了上一个示例中的 -f 需要文件而不是 shell 变量之外,这就是我所需要的。由于更改的文件受版本控制,并且可以轻松检查/恢复结果,因此我不使用任何检查。
    • 哦,对了。在我有了${//} 的想法并忘记删除-f 之前,我在那里有一个&lt;() 替换。现在已经修好了。
    • 至于检查:您的场景听起来好像hackery 相对不太可能,但请注意可以使 GNU sed 运行 shell 命令。如果插入文件来自不可靠的来源(例如办公室的那个人),则可能会发生恶作剧。如果我是你,为了安全起见,我可能会将 sed 命令放在 if [[ "$insertion" =~ ^[0-9]+: ]]; thenfi 之间。
    猜你喜欢
    • 2014-06-11
    • 1970-01-01
    • 1970-01-01
    • 2012-05-30
    • 1970-01-01
    • 2012-11-15
    • 2020-04-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多