【问题标题】:Correct syntax and usage of `cat` command?`cat` 命令的正确语法和用法?
【发布时间】:2019-01-08 03:07:50
【问题描述】:

(这个问题是this评论的后续,在关于git hooks的回答中)

我在 bash 方面(到目前为止)太不熟练了,无法完全理解这句话以及如何采取相应的行动。更具体地说,有人建议我避免以这种方式使用 bash 命令cat

echo "$current_branch" $(cat "$1") > "$1"

因为操作的顺序取决于特定的 shell,它最终可能会破坏传递参数的内容,所以如果我理解正确,提交消息本身?

另外,如何“在单独的步骤中保存内容”?

以下是否有意义?

tmp = "$1"
echo "$current_branch" $(cat $tmp) > "$1"

【问题讨论】:

    标签: bash cat


    【解决方案1】:

    提议的问题不是关于覆盖变量或参数,而是关于同时读取和写入文件通常是一个坏主意的事实。

    例如,此命令可能看起来只是将文件写入自身,但实际上会截断它:

    cat myfile > myfile   # Truncates the file to size 0
    

    但是,这在您的特定命令中不是问题。保证可以在符合 POSIX 的 shell 中工作,因为 order of operations specify 重定向将在 扩展之后发生:

    1. 不属于变量赋值或重定向的词应被扩展。如果扩展后仍有任何字段,则第一个字段应视为命令名称,其余字段是命令的参数。

    2. 应按照重定向中的说明执行重定向。

    双然而,它仍然有点脆弱,因为看似无害的修改可能会引发问题,例如如果您想在结果上运行sed。由于重定向 (> "$1") 和命令替换 $(cat "$1") 现在在单独的命令中,POSIX 定义不再为您节省:

    # Command may now randomly result in the original message being deleted
    echo "$current_branch $(cat "$1")" | sed -e 's/(c)/©/g' > "$1"
    

    同样,如果你把它重构为一个函数,它也会突然停止工作:

    # Command will now always delete the original message
    modify_message() {
      echo "$current_branch $(cat "$1")"
    }
    modify_message "$1" > "$1"
    

    您可以通过写入临时文件来避免这种情况,然后替换您的原始文件。

    tmp=$(mktemp) || exit
    echo "$current_branch $(cat "$1")" > "$tmp"
    mv "$tmp" "$1"
    

    【讨论】:

    • 出色的答案,非常感谢!后续问题:临时文件之后不会悬空吗?我的意思是,它不应该被 git 检测为未跟踪的文件或阻碍进程,所以我应该在进程中摆脱它,对吧?
    • @RomainValeri 在此示例中,mv 用于移动您的钩子的临时文件并替换 git 的临时文件,因此没有剩余文件。如果你做了例如cat "$tmp" > "$1" 那么你是对的:那需要一个单独的 rm "$tmp" 来清理临时文件。
    • 你不是那个其他的人,你是那个人;-)
    【解决方案2】:

    在我看来,最好保存到另一个文件。

    你可以试试

    echo "$current_branch" > tmp
    cat "$1" >> tmp # merge these into 
    # echo "$current_branch" $(cat "$1") > tmp
    # may both OK
    mv tmp "$1"
    

    但是我不确定我的理解是否正确,或者有一些更好的解决方案。

    这是我认为的核心问题。很难确定$() 块和> 的“优先级”。如果>“更早”执行,那么echo "$current_branch"将重写“$1”文件并drop“$1”的原始内容,这是一场灾难。如果$() 被“提前”执行,那么一切都会按预期进行。但是,存在风险,我们应该避免它。

    【讨论】:

    • 我现在好多了,谢谢你的解释。我会尝试一下并摆弄它。
    【解决方案3】:

    在这里,一个命令组比一个命令替换要好得多。请注意与 Geno Chen 的答案的相似之处。

    {
      echo "$current_branch"
      cat "$1"
    } > tmp && mv tmp "$1"
    

    【讨论】:

    • 请问哪方面比较好?恐怕我不确定您在这里所说的命令替换:-/
    • $(...) 是一个命令替换,它需要先将文件的全部内容读入内存。 (它还会删除任何尾随的换行符,这需要少量的摆弄才能解决。)这只是将echo 命令的输出,然后是cat 命令的输出写入临时文件。 (语义上和写后追加文件一样,但只需要打开一次tmp,语法上更干净。)
    猜你喜欢
    • 2020-10-12
    • 2015-11-13
    • 1970-01-01
    • 2019-11-20
    • 2017-07-04
    • 2019-06-10
    • 1970-01-01
    • 1970-01-01
    • 2018-05-07
    相关资源
    最近更新 更多