Эхо "фу" |судо тройник - идемпотент - PullRequest
0 голосов
/ 06 декабря 2018

Я использую следующую команду оболочки для добавления строки в файл:

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

Однако это не идемпотент, т. Е. Если строка уже существует в /etc/my-app.conf, она добавляется несколько раз каждый разкоманда вызывается.Можно ли добавить, только если он не существует в умной однострочнике?

Ответы [ 2 ]

0 голосов
/ 24 апреля 2019

Вы также можете использовать модуль Ansible inline , чтобы убедиться, что конкретная строка находится в файле.

0 голосов
/ 06 декабря 2018

Краткий ответ: просто, легко и неправильно

Если вас не волнует правильность в угловых случаях (многострочные входы, одновременные вызовы и т. Д.), Следующее будет сортировка сделайте работу:

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

Ответ, который больше касается правильности, чем краткости, читайте дальше.


Длинный ответ: оценка по линии с блокировкой

В качестве ответа, который не пытается быть кратким, но не обращает некоторое внимание на правильность (включая правильную работу, когда несколько экземпляров из нижеприведенного вызваны одновременно):

#!/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 "$@";
}

В качестве примера того, как вы можете использовать это для замены вашей старой команды, после source с помощью приведенного выше сценария:

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