Краткий ответ: просто, легко и неправильно
Если вас не волнует правильность в угловых случаях (многострочные входы, одновременные вызовы и т. Д.), Следующее будет сортировка сделайте работу:
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