回声“foo”| sudo tee -a idempotent

问题描述 投票:2回答:2

我使用以下shell命令将字符串附加到文件:

echo "latest stable main" | sudo tee -a /etc/my-app.conf > /dev/null

然而,这不是幂等的,即如果字符串已经存在于/etc/my-app.conf中,则每次调用命令时都会附加多次。如果它不存在于聪明的单行中,是否可以仅附加?

linux bash shell tee
2个回答
2
投票

The Short Answer: Simple, Easy And Wrong

如果您不关心极端情况下的正确性(多行输入,并发调用等),以下将进行排序:

grep -Fxe 'latest stable main' /etc/my-app.conf || {
  sudo tee -a /etc/my-app.conf <<<"latest stable main"
}

对于一个更关心正确而不是简洁的答案,请继续阅读。


The Long Answer: Per-Line Evaluation With Locking

作为一个不试图简洁的答案,但确实注意正确性(包括同时调用下面的多个实例时的正确操作):

#!/bin/bash
# ^^^- shebang present as an editor hint; this file should be sourced, not executed.

case $BASH_VERSION in ''|[0-3].*|4.0.*) echo "ERROR: Bash 4.1 or newer required" >&2; return 1 >/dev/null 2>&1; exit 1;; esac

appendEachNewLine() {
  local file=$1 line out_fd
  local -A existingContents=( )   # associative array, to track lines that already exist

  # dynamically assign a file descriptor on which to both lock our file and write
  exec {out_fd}>>"$file"
  flock -x -n "$out_fd" || {
    echo "ERROR: Unable to lock destination file" >&2
    exec {out_fd}>&-
    return 1
  }

  # read existing lines once, through a new file descriptor, only after holding the lock
  while IFS= read -r line; do
    existingContents[$line]=1
  done <"$file"

  # then process our stdin, appending each line if not previously seen
  while IFS= read -r line; do
    if ! [[ ${existingContents[$line]} ]]; then
      printf '%s\n' "$line" >&"$out_fd"
    fi
  done

  # close the file, thus releasing the lock, when done.
  exec {out_fd}>&-
}

appendEachNewLineAsRoot() {
  sudo bash -c "$(declare -f appendEachNewLine)"'; appendEachNewLine "$@"' appendEachNewLine "$@";
}

sourceing上面的脚本之后,作为如何使用它来替换旧命令的示例:

echo "latest stable main" | appendEachNewLineAsRoot /etc/my-app.conf

0
投票

您还可以使用Ansible inline模块来确保特定行在文件中。

© www.soinside.com 2019 - 2024. All rights reserved.