Для надежного выполнения операции вам потребуются следующие подпрограммы обработки строк:
text_new=${text%%"${match}"*}${word}${text#*"${text%%"${match}"*}"}
# |------- A -------| |-B--| |------------ C -------------|
A
— это часть до -совпадения, то есть строка перед началом сопоставления. B
— данные для вставки. C
— это часть совпадения после -, то есть строка после окончания совпадения. bash
parameter expansion
. match_esc=$(printf '%s\n' "$match" | sed -e 's|[][^\/.*$]|\\&|g' | sed -e 'H;1h;$!d;g;s/\n/\\n/g')
word_esc=$(printf '%s\n' "$word" | sed -e 's|[\&/]|\\&|g;$!s/$/\\/')
printf '%s\n' "$text" | sed -e 'H;1h;$!d;g;'"s/$match_esc/$word_esc&/"''
sed
является более запутанным, так как мы должны убедиться, что все символы, имеющие значение для sed
и появляющиеся во входном тексте, должны быть отключены. Еще один уровень сложности заключается в том, что эти символы различны для левой и правой команд s/// команды sed. Это означает, что вам придется сохранить текущее состояние активации.
Самый простой способ сделать это — создать файл для одного из состояний (, возможно, для состояния «включено» или «активировано» ), и удалить этот файл при входе в другое состояние (. ] "выключено" или "деактивировано" ).
Создание пустого файла выполняется с помощью touch filename
, а проверка существования этого файла выполняется с помощью if [ -e filename ]; then...; fi
. Удалите файл с помощью rm filename
.
В приведенном ниже примере вы можете сохранить часть информации в этом состоянии, сохранив значение переменной в файле. В этом случае состояние передается постоянным файлом, который изменяется каждый раз, когда вы запускаете сценарий (, а не просто файлом, который либо создается, либо удаляется при каждом вызове вашего сценария ).
Если вы используетеbash
:
#!/bin/bash
statefile=$HOME/.script.state
if [ -f "$statefile" ]; then
. "$statefile"
fi
case $state in
on) state=off ;;
*) state=on
esac
printf 'Current state is "%s"\n' "$state"
declare -p state >"$statefile"
Тестирование:
$ bash script.sh
Current state is "on"
$ bash script.sh
Current state is "off"
$ bash script.sh
Current state is "on"
$ bash script.sh
Current state is "off"
Сценарий сохраняет переменную state
в $HOME/.script.state
с помощью declare -p state
в bash
в конце каждого запуска и считывает ее оттуда путем поиска этого файла в начале (, если файл существует ).
В конечном итоге этот файл будет выглядеть так:
declare -- state="off"
, который является результатом declare -p state
, если $state
является строкой off
.
С помощью /bin/sh
приведенный выше сценарий может быть записан как
#!/bin/bash
statefile=$HOME/.script.state
if [ -f "$statefile" ]; then
read state <"$statefile"
fi
case $state in
on) state=off ;;
*) state=on
esac
printf 'Current state is "%s"\n' "$state"
printf '%s\n' "$state" >"$statefile"
... просто заменив чтение и запись состояния на read
и printf
, что позволит сохранить только саму строку состояния в файле состояния.
Если вы хотите автоматически инвертировать состояние при каждом запуске, вы также можете воспользоваться числовой операцией «логического отрицания», которая полезна для инвертирования значения. То есть логическое отрицание 0 равно 1, и наоборот.
Оболочки POSIX способны выполнять наиболее распространенные числовые и побитовые операции посредством арифметического расширения и Bash не является коротким , предоставляя также массивы , которые может быть проиндексирован целым числом, таким как 0 или 1 (и более ).
На практике:
(
# our persistent file to save state into
file=./state
# array of two states, "off" is the first so that it will be 0, and "on" will be 1
states=(off on)
# import what's been saved in our state file, if present
[ -f "$file" ] && source "$file"
# within the arithmetic expansion syntax,
# we compute new state as the logical negation (the `!` operator) of the current state
state=$(( !(state) ))
printf 'state=%d\n' $state > "$file" # save new state to persistent file
# print current state from the array using the numeric state as index
printf 'currently %s\n' "${states[state]}"
)
В оболочке строго POSIX это немного сложнее, потому что нам нужно обойти отсутствие массивов:
(
file=./state
# here we use a simple string instead of an array
states='off on'
[ -f "$file" ] &&. "$file" # the `source` command POSIXly is just `.`
state=$(( !(state) ))
printf 'state=%d\n' $state > "$file"
# here we use the `cut` tool to select the piece of string based on numeric state
echo -n 'currently '; printf '%s\n' "$states" | cut -d ' ' -f $((state+1))
)