Bash脚本:仅在~/.bash_profile中的行不存在时才打印该行一次

3
我写了一个关于安装 git 的 bash 脚本bash git-install script。在最后阶段,我执行了以下操作:
echo "Edit ~/.bash_profile to load ~/.git-completioin.bash on Terminal launch"
echo "source ~/.git-completion.bash" >> ~/.bash_profile

问题在于,如果您运行脚本超过一次,就会将此行多次附加到~/.bash_profile。我如何使用bash脚本和grepsed(或您可能推荐的另一个选项)仅在文件中不存在该行时添加该行。另外,如果存在~/.profile文件且不存在~/.bash_profile文件,则要将该行添加到~/.profile文件中,否则只需将其添加到~/.bash_profile文件中。

在处理此类问题时要小心竞态条件 -- 如果可能同时运行两个脚本实例,则它们都可以向文件追加内容;为了避免这种情况,您需要使用flock来保护该部分。对于这个特定的用例来说可能不是很重要,但是当将相同的技术应用于其他目的时,这可能是一个重要的考虑因素。 - Charles Duffy
4个回答

11

可以使用类似以下的方法:

LINE_TO_ADD=". ~/.git-completion.bash"

check_if_line_exists()
{
    # grep wont care if one or both files dont exist.
    grep -qsFx "$LINE_TO_ADD" ~/.profile ~/.bash_profile
}

add_line_to_profile()
{
    profile=~/.profile
    [ -w "$profile" ] || profile=~/.bash_profile
    printf "%s\n" "$LINE_TO_ADD" >> "$profile"
}

check_if_line_exists || add_line_to_profile

一些注释:

  • 我使用.命令替代source,因为source是bashism,但.profile可能被非Bash shell使用。在.profile中使用source ...是错误的。
  • 我使用printf替代echo,因为它更具可移植性,而不会像Bash的echo那样破坏反斜杠转义字符。
  • 尝试对非明显的故障进行更加健壮的处理。在这种情况下,确保.profile存在并且可写,然后再尝试写入。
  • 我使用grep -Fx来搜索字符串。-F表示固定字符串,因此搜索字符串中没有特殊字符需要被转义,而-x表示仅匹配整行。 -qs是仅检查字符串是否存在且不显示的常见grep语法。
  • 这是一个概念验证。我没有实际运行过这个脚本。我的错,但今天早上是星期天,我想出去玩。

6
if [[ ! -s "$HOME/.bash_profile" && -s "$HOME/.profile" ]] ; then
  profile_file="$HOME/.profile"
else
  profile_file="$HOME/.bash_profile"
fi

if ! grep -q 'git-completion.bash' "${profile_file}" ; then
  echo "Editing ${profile_file} to load ~/.git-completioin.bash on Terminal launch"
  echo "source \"$HOME/.git-completion.bash\"" >> "${profile_file}"
fi

2

您觉得这样怎么样:

grep -q '^source ~/\.git-completion\.bash$' ~/.bash_profile || echo "source ~/.git-completion.bash" >> ~/.bash_profile

或者以更明确(且易读)的形式表达如下:
if ! grep -q '^source ~/\.git-completion\.bash$' ~/.bash_profile; then
    echo "Updating" ~/.bash_profile

    echo "source ~/.git-completion.bash" >> ~/.bash_profile
fi

编辑:

在你的一行命令之前,最好添加一个额外的换行符,以防万一~/.bash_profile没有以换行符结尾:

if ! grep -q '^source ~/\.git-completion\.bash$' ~/.bash_profile; then
    echo "Updating" ~/.bash_profile

    echo >> ~/.bash_profile   
    echo "source ~/.git-completion.bash" >> ~/.bash_profile
fi

编辑2:

这个稍微容易修改一些,并且更加便携:

LINE='source ~/.git-completion.bash'

if ! grep -Fx "$LINE" ~/.bash_profile >/dev/null 2>/dev/null; then
    echo "Updating" ~/.bash_profile

    echo >> ~/.bash_profile   
    echo "$LINE" >> ~/.bash_profile
fi

-F-x 选项由 POSIX 规定,并在其他几个答案和评论中提出建议。


我会使用 grep -F 而不是需要转义的标准正则表达式 grep 语句。 - Charles Duffy

1
# Decide which profile to add to
PROFILE=~/.bash_profile
if ! [ -e "$PROFILE" ] && [ -e ~/.profile ]; then
    PROFILE=~/.profile
fi

# Add to profile if it doesn't appear to be there already. Err on the side of
# not adding it, in case user has made edits to their profile.
if ! grep -s 'git-completion\.bash' "$PROFILE"; then
    echo "Editing $PROFILE to load ~/.git-completion.bash on Terminal launch"
    echo "source ~/.git-completion.bash" >> "$PROFILE"
fi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接