【问题标题】:Bash script that edits itself自行编辑的 Bash 脚本
【发布时间】:2010-07-02 18:56:37
【问题描述】:

我正在尝试编写一个可以自行编辑的 bash 脚本。我尝试使用的代码行是:

sed "s/$STRING_TO_REPLACE/$NEW_STRING/" < $0 > $0

但在运行脚本后,我得到了一个空文件。有什么建议吗?

我正在创建一个脚本,其中包含两个包含默认值的变量。唯一的问题是,这些默认值对于将要使用的许多站点中的每一个都不同。我宁愿运行这些站的人都不必打开脚本并自己编辑默认值,所以我想在脚本中包含更改默认值的能力。我的要求之一是脚本必须是自包含的;脚本没有其他文件,也没有环境变量。这需要脚本能够自行编辑。

【问题讨论】:

  • 我很好奇,有什么用,尝试多态脚本,或者你真的认为这会有用吗。
  • 这可能会有所帮助:stackoverflow.com/questions/3055005/…
  • 考虑拥有 2 个脚本。 1 是动态创建另一个然后执行它的控制器。

标签: bash


【解决方案1】:

如果您想使用 sed 进行就地替换,请使用 -i 开关。

我不建议即时重写当前正在执行的脚本。我不确定这是否可能,但如果是的话,维护代码肯定会很有趣(阅读:困难):-)

【讨论】:

  • 我知道这很丑。我得想办法做到这一点。
  • 他不需要在执行过程中编辑脚本。他可以简单地在满足特定条件时强制脚本退出,例如布尔值真或假,然后让从节点(外部脚本)编辑主节点(主脚本)并根据需要更改适当的行。
  • FWIW -i 开关似乎移动了原始文件并在其位置写入了一个新文件,因此 Bash 不会接受更改(至少在我刚刚运行的实验中不会)。要实际编辑正在运行的 shell 脚本并让解释器做出相应的响应,可能需要将整个脚本读入变量或临时文件,用sed 处理它,然后将sed 的输出发送到$0使用 shell 重定向而不是 -i 选项。 (这可能取决于系统。)
【解决方案2】:

您正在尝试读取和写入同一个流,这是一种不好的编程习惯,通常会导致错误。

这是你应该做的:

  1. 写入临时文件
  2. 删除原文件
  3. 将 temp 重命名为原始文件名

    sed "s/$STRING_TO_REPLACE/$NEW_STRING/" temp

    rm $0

    mv 温度 $1

我暂时没有编写 bash 脚本,所以请确认我有正确的命令。

【讨论】:

  • rm 不是必需的,它应该是“mv temp $0”,我必须在最后执行 chmod 以使新文件可执行。除此之外它还有效:) 我不知道我可以在脚本运行时删除它。
【解决方案3】:

首先,这是一个非常糟糕的主意,我无法想象它的任何用例都不能被除此之外的东西所覆盖。

发生这种情况的原因是,当您执行 >file 时,它​​会以读取模式打开文件并将其截断为 0 长度,并且输入没有可读取的内容。您需要使用临时文件。您可以查看更多信息here

【讨论】:

  • 他可以简单地强制脚本在满足某个条件时退出,例如布尔值真或假,然后让一个从节点(外部脚本)编辑主节点(主脚本)并更改他/她想要的适当的线条。或者他甚至可以使用配置文件来保存动态数据更改,让脚本退出,然后使用新的配置更改重新运行。我认为他可能正在测试小型单工人工智能。理论。 (即自更正代码)。 Bash 非常适合测试诸如此类的小型单纯形理论。
【解决方案4】:

我正在做的,也是我推荐的,是拥有一个配置文件 ($HOME/.myscript.cfg) 并让您的脚本检测它的存在(和版本)并相应地创建/更新它。这样,第一次运行就可以提出问题,随后的运行就可以完成他们的工作,而不会给操作员带来重复的问题。

【讨论】:

    【解决方案5】:

    但在运行脚本后,我得到了一个空文件。有什么建议吗?


    有点迟来的反应...一种可能的解决方案是您的脚本可以在脚本的退出命令之后包含可修改的设置

    为避免对这些设置以外的任何内容进行意外更改,最好使用如下适当的前缀:

    @SETTINGS
    @setting-homepage=http://www.goatse.cx
    @setting-dns=4.4.2.2
    

    以下是修改两个变量的示例:一个指定默认浏览器主页(默认:www.goatse.cx,新:www.google.com),另一个用于更改 DNS(默认:4.4.2.2,新: 8.8.8.8)。



    警告词

    sed 用于修改包含所需设置的行号的内容。如果这里有错误,它会 spaz 出来并用 sed 相关的疯狂覆盖脚本中的每一行。一种解决方案可能是让脚本编写另一个脚本,然后修改原始脚本。另一种方法可能是包含一个条件语句,该语句仅在确认匹配存在后才执行 sed 替换。或者更简单地说,匹配所需的设置字符串,而不仅仅是行号。

    编辑:似乎尝试使用 sed 执行字符串匹配会导致一些奇怪的脚本自噬。我认为您需要指定退出命令下方的行号。



    #!/bin/bash
    
    # find settings and extract their values
    settingDefaultHomepage=$(grep ^@setting-homepage $0 |  sed 's/@setting-homepage=*//')
    settingDefaultDNS=$(grep ^@setting-dns $0 |  sed 's/@setting-dns=*//')
    
    
    echo "Default browser homepage = $settingDefaultHomepage"
    echo "Default DNS = $settingDefaultDNS"
    
    
    # find line number of setting, to be used when modifying the value
    settingLineHomepage=$(awk '/^@setting-homepage/ {print FNR}' $0)
    settingLineDNS=$(awk '/^@setting-dns/ {print FNR}' $0)
    
    echo "Line of setting for browser homepage = $settingLineHomepage"
    echo "Line of setting for DNS = $settingLineDNS"
    
    
    
    # modify the settings using line number above
    sed -i $settingLineHomepage's/.*/@setting-homepage=www.google.com/' $0
    sed -i $settingLineDNS's/.*/@setting-dns=8.8.8.8/' $0
    
    
    
    exit
    
    
    @SETTINGS
    @setting-homepage=www.goatse.cx
    @setting-dns=4.4.2.2
    



    【讨论】:

    • 迟到是轻描淡写。我获得了学位,并且在问这个问题和你的答案之间有 3 份工作:P
    • 大声笑,在这里我希望你被困在同一个死胡同的工作中。
    【解决方案6】:

    编辑:正如DwB 上面评论的那样,将您的代码分成两个代码:一个核心脚本执行您想要的操作,另一个修改、执行、并且可能会终止第一个。 这是您应该做的第一件事

    还请记住,您应该编辑正在运行的 shell 脚本。阅读my answer 到问题Edit shell script while it's running

    【讨论】:

    • 他不需要在执行过程中编辑脚本。他可以简单地在满足特定条件时强制脚本退出,例如布尔值真或假,然后让从节点(外部脚本)编辑主节点(主脚本)并根据需要更改适当的行。我认为他可能正在测试小型单工人工智能。理论。 (即自我更正代码)。
    【解决方案7】:

    Perl 处理就地编辑。试试这个:

    perl -npi -e "s/$STRING_TO_REPLACE/$NEW_STRING/g" < $0
    

    【讨论】:

    • 这不起作用,因为您在 STDIN 上提供文件,因此没有文件可以就地修改。删除 &lt; 以执行您正在尝试的操作。
    猜你喜欢
    • 1970-01-01
    • 2023-01-08
    • 2014-08-29
    • 2022-12-15
    • 2012-01-10
    • 2010-12-05
    • 1970-01-01
    • 1970-01-01
    • 2023-04-06
    相关资源
    最近更新 更多