Скрипты Bash: обновить файл свойств
propertyOne=1
propertyTwo=a/b
propertyThree=three
Как изменить содержимое файла свойств на следующий шаблон?
- propertyOne добавит строку перед исходным значением
- propertyTwo добавит строку в середине
propertyThree добавит строку в конце
propertyOne=apple/1 propertyTwo=a/and/b propertyThree=three/end
Я пытался с помощью sed -i -e
но я успешен, только если я жестко запрограммирую изменения для каждой строки; какие-либо предложения по улучшению кода?
sed -i -e '/propertyTwo=/ s=.*/=one/2/two' path/to/file
3 ответа
В этом случае чистое решение Bash предлагает как гибкость, так и надежность (но см. Ниже для более быстрого awk
решение).
В то время как решения Bash, которые читают файлы построчно, как правило, медленные, это, вероятно, не будет проблемой для файлов свойств, которые, как правило, небольшие.
#!/usr/bin/env bash
while IFS='=' read -r prop val; do
case $prop in
propertyOne)
val="apple/$val"
;;
propertyTwo)
val="${val/\///and/}"
;;
propertyThree)
val="$val/end"
;;
esac
printf '%s\n' "$prop=$val"
done < file > file.tmp && mv file.tmp file
Встроенный Bash read
удобно предлагает логику покоя в строке: указав только 2 переменные в IFS='-' read -r prop value
2-я переменная value
получает все после первого =
что бы это ни было, даже если оно содержит дополнительные =
экземпляров.
< file > file.tmp && mv file.tmp file
это общая идиома для (свободно говоря) обновления файла на месте. Технически, измененный контент записывается в темп. файл, и этот темп. Затем файл заменяет оригинал.
Замечания:
* Этот косвенный способ обновления необходим, потому что оболочка не поддерживает чтение и вывод в один и тот же файл одной и той же командой.
* Этот простой подход может быть проблематичным, так как если входной файл был символической ссылкой, он заменяется обычным файлом, права доступа к новому файлу могут быть другими, ...
awk
Как показано в ответе Каракфы, это, безусловно, более быстрый выбор, но он идет с оговоркой, которая может быть или не быть проблемой для вас:
Концептуально, файл свойств не строго основан на поле, потому что значение свойства может содержать значение internal =
экземпляров.
Если вы разделите ввод на поля =
, тогда обработка общего значения может быть проблематичной, потому что у вас не будет единственной переменной, ссылающейся на значение в целом.
Быстрый пример: скажем, у вас есть строка ввода foo=bar=baz
, и вы хотите добавить строку @
к существующей стоимости, bar=baz
без необходимости заранее знать, было ли существующее значение =
символы.
Если вы используете вслепую $2 = $2 "@"
для добавления полученное значение будет просто bar@
- другими словами: вы потеряли данные.
Решение этой проблемы требует немного больше работы; вот awk
Решение, адаптированное от Каракфы, которое предоставляет все значение в одной переменной val
:
awk -F= '
# Capture the entire value (everything to the right of "=") in variable "val".
{ val= $0; sub("^[^=]+=", "", val) }
$1 == "propertyOne" { val = "apple/" val }
$1 == "propertyTwo" { sub(/\//, "/and/", val) }
$1 == "propertyThree" { val = val "/end" }
{ print $1 "=" val }
' file > file.tmp && mv file.tmp file
Примечание: если вы используете GNU awk
и номер версии>= 4.1, вы можете использовать -i inplace
вместо > file.tmp && mv file.tmp file
обновить входной файл на месте (грубо говоря). Помимо того, что более удобный, чем последний подход, -i inplace
также сохраняет права исходного файла, но основной подход тот же: файл заменяется, что сопряжено с риском замены символической ссылки обычным файлом.
sed
не является хорошим выбором, потому что трудно ограничить замены частью строки в общем виде.
awk
в помощь!
$ awk -F'[=/]' '/propertyOne/{$2="apple/"$2}
/propertyTwo/{$2=$2"/and/"$3}
/propertyThree/{$2=$2"/end"}
{print $1 "=" $2}' file
propertyOne=apple/1
propertyTwo=a/and/b
propertyThree=three/end
определить два разделителя полей, чтобы иметь возможность ссылаться на компоненты. Установите новое второе поле на основе ваших правил и распечатайте.
Примечание Очевидно, что этот подход не будет работать, если у вас есть /
или же =
как часть вашего контента. Вы можете обобщить первое и последнее правило, чтобы добавить или добавить к значащей части уравнения, но второго варианта нет.
Кроме того, чтобы лучше контролировать правила, которые вы можете изменить /../
в $1=="..."
для каждого случая.
Это еще одна альтернатива,
$ awk -F= -v OFS='=' '$1=="propertyOne"{$2="apple/"$2}
$1=="propertyTwo"{sub(/\//,"/and/")}
$1=="propertyThree"{$NF=$NF"/end"}1' file
тестирование с крайними случаями
$ awk -F= ... << EOF
> propertyOne=a==b
> propertyTwo=a==b/c==d
> propertyThree=x/y==z
> Not_propertyOne=no change
> EOF
доходность
propertyOne=apple/a==b
propertyTwo=a==b/and/c==d
propertyThree=x/y==z/end
Not_propertyOne=no change
Вышеуказанные решения являются хорошими. Однако есть еще один способ сделать это на языке Java, который использует тот же класс свойств, который используется в самой Java, и который также можно легко адаптировать для редактирования свойств формата XML.
Давайте воспользуемся новой функцией безымянного класса Java 21 и новой функцией собственного образа GraalVM, чтобы создать команду, которая сделает именно это за нас. Создайте файл с именемSetproperty.java
.
Обратите внимание, что здесь нет объявления пакета и объявления класса! Код, показанный ниже, представляет собой полный файл.
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import static java.lang.System.out;
import java.util.Date;
import java.util.Properties;
void main(String[] args) throws IOException {
if(args.length < 2) {
out.println("Usage: setproperty filename propertyname [value]");
return;
}
final Properties p = new Properties();
p.load(new FileInputStream(args[0]));
if(args.length < 3) {
out.println(p.get(args[1]));
return;
}
p.setProperty(args[1], args[2]);
p.store(new FileOutputStream(args[0]), "updated " + new Date());
}
Скомпилировать с помощью:
javac --release 21 --enable-preview Setproperty.java
graalvm-jdk-21.0.1+12.1/bin/native-image --enable-preview -O1 Setproperty
rm Setproperty.class
Теперь вы можете использовать setproperty в своем скрипте:
./setproperty foo.properties SomeKey SomeValue
Он работает так же быстро, как и любой другой скомпилированный код — без задержки при запуске JVM. Это отличный пример использования новых функций Java 21 и Native-Image.