Функция замены строки Bash похожа на Javascript String.prototype.replace () - PullRequest
0 голосов
/ 11 ноября 2018

В JS я могу использовать функцию String.prototype.replace () при замене подстроки в регулярном выражении. Например:

var x = 'a1b2c3'.replace(/(\d+)/g, (num) => {
    return num*num+1
})
console.log(x)
// 'a2b5c10'

Я пытался использовать sed, но кажется, что вызов оператора $(()) внутри замены невозможен.

$ echo "a1b2c3" | sed 's/\([^0-9]*\)\([0-9]\)\([^0-9]*\)/\1$((\2*\2+1))\3/g'
# Output: a$((1*1+1))b$((2*2+1))c$((3*3+1))

Есть ли в bash аналогичный инструмент или функция, которая имеет функциональность, аналогичную JS String.replace()?

Ответы [ 3 ]

0 голосов
/ 11 ноября 2018

Оболочка bash поддерживает собственный оператор регулярных выражений, который можно включить с флагом ~. Все, что вам нужно сделать, это определить регулярное выражение, взять захваченные группы и заменить их измененными значениями

str='a1b2c3'
re='^.*([0-9]+).*([0-9]+).*([0-9]+).*$'
if [[ $str =~ $re ]]; then
    for match in "${BASH_REMATCH[@]}"; do
        final="${str/$match/$((match*match+1))}"
    done
fi
printf '%s\n' "$final"

[[ $str =~ $re ]] сопоставляет регулярное выражение и обновляет массив захваченных групп ${BASH_REMATCH[@]}. Поэтому для каждого элемента в порядке их появления мы делаем оператор подстановки строки ${str/old/new}. Значение замены в вашем случае - это число, умноженное на себя и добавленное на 1.

Добавление дополнительных групп захвата в регулярное выражение .*([0-9]+) для последующих совпадений.


Если бы не чистое решение bash, описанное выше, используя внешнюю утилиту, такую ​​как perl, можно было бы сделать это как

perl -pe 's/\d+/$&*$&+1/ge' <<<"$str"

$& относится к захваченной цифре в строке, а флаг e позволяет выполнять арифметические операции над захваченными группами.

0 голосов
/ 11 ноября 2018

Я не уверен, что это мой любимый ответ, но просто хочу сообщить, что у GNU sed есть возможности внешних команд:

echo "a1b2c3" | sed 's/\([^0-9]*\)\([0-9]\)\([^0-9]*\)/echo \1$((\2*2+1))\3/ge' | sed 's/echo //g'

e - это способ передать результат извне.

Самая раздражающая вещь - echo добавляется, когда флаги g и e объединяются с группами подстановки, следующими за первой, поэтому второй sed избавляется от них. Если кто-то знает, есть ли что-то встроенное, это было бы здорово.

К сожалению

echo "a1b2c3" | sed 's/\([^0-9]*\)\([0-9]\)\([^0-9]*\)/\1$((\2*2+1))\3/ge'

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

0 голосов
/ 11 ноября 2018

Вы можете реализовать это в gawk, используя match() и substr().

echo "a1b2c3" | awk '{
    head = ""
    tail = $0

    while (match(tail, /[0-9]+/)) {
        num = substr(tail, RSTART, RLENGTH)
        num = num * num + 1
        head = head substr(tail, 1, RSTART-1) num
        tail = substr(tail, RSTART + RLENGTH)
    }

    print head tail
}'

выход

a2b5c10
...