LINUX.ORG.RU

Как в bash заэскейпить строку, чтобы ее подставить в регулярное выражение?

 , , ,


0

1

Есть такой кусок кода:

insertAfter='#!/bin/bash'
fileName='./file.txt'

insertAfterEsc=`printf "%q" "$insertAfter"` 

cmd="sed '/"$insertAfterEsc"/,$ d' < "$fileName" > /tmp/sedHead.txt"
echo "$cmd" | bash


Переменная insertAfter - это строка, может быть с любыми символами, и ее содержимое надо подставить в sed для поиска. Поэтому для подстановки в sed используется переменная insertAfterEsc.

И мне нужно найти нормальный метод экранирования. Метод:
insertAfterEsc=`printf "%q" "$insertAfter"` 

Не работает как надо, потому что, например, не экранирует прямые слеши «/».

Вопрос: как экранировать строку, чтобы ее можно было подставить в sed, и все символы ее считались просто символами?

★★★★★

Ответ на: комментарий от aalst

В совсем общем случае никак

Нравится мне язык, в котором элементарную вещь вообще невозможно сделать. У меня скоро нервный срыв будет.

Xintrea ★★★★★
() автор топика

man sed

       \cregexpc
              Match lines matching the regular expression regexp.  The c  may  be
              any character.
anonymous
()
Ответ на: комментарий от Xintrea

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

aalst
()
Ответ на: комментарий от Xintrea

Не используй обратные кавычки ``. Вместо них используй "$( )".
И вот эта конструкция странная: echo "$cmd" | bash
А еще в bash принято называть переменные большими буквами.

По вопросу:
https://stackoverflow.com/questions/407523/escape-a-string-for-a-sed-replace-...

Updated

insertAfter='#!/bin/bash'
fileName='./file.txt'

insertAfterEsc="$(printf '%s\n' "$insertAfter" | sed -e 's/[]\/$*.^[]/\\&/g')"

cmd="sed \"/$insertAfterEsc/,$ d\" \"$fileName\" "
eval "$cmd" > /tmp/sedHead.txt

Kroz ★★★★★
()
Последнее исправление: Kroz (всего исправлений: 1)
Ответ на: комментарий от aalst

Конечно, bash не предназначен для этой задачи. Да и вообще задача уже решена на Ansible. Но руководитель проекта решил что Ansible - это сложно, и надо сделать на баше, чтобы неподготовленные пользователи могли копировать скрипты на конечную машину и могли их тупо запускать. Получается кусок говна.

Xintrea ★★★★★
() автор топика
Ответ на: комментарий от Xintrea

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

anonymous
()
Ответ на: комментарий от zolden

Задача - развертывание настроек постгреса на хостах. С настройками ядра, конфигов постгреса, созданием пользователей, баз данных, распределением прав доступа.

Если бы ты работал с Ansible, то название переменной insertAfter, при работе с содержимым файла, тебе бы много о чем сказало.

Xintrea ★★★★★
() автор топика
Ответ на: комментарий от Kroz

На самом деле это плохое решение. Более-менее надёжно для однострочных литералов

  MkLiteral () { # <string>
    #   Make pattern for literal match of (one-line) <string>
    # in 'grep' or 'sed' regular expressions.
    printf '%s' "${1}" | sed 's/./[&]/g ; s/\^/[.^.]/g'
  } # MkLiteral
Это по крайней мере не сломается вне зависимости от того, BRE там или ERE, и есть ли какие-либо расширения.

ABW ★★★★★
()
Ответ на: комментарий от ABW

Хорошее решение, признаю.

Note: В моем предыдущем есть ошибка.

Вот рабочее решение:

$ cat delme.txt
10
11
---
20
21
---#!/bi\n/b'a"s$.*[]h
30
31
---
40
41

Вариант 1 - мой, откорректированный, не универсальный
A='#!/bi\n'"/b'a\"s$.*[]h"
B="$( echo "$A" | sed -e 's/[]\/$*.^[]/\\&/g' )"
CMD="sed -e '/${B//\'/\'\"\'\"\'}/,$ d' 'delme.txt'"

cat delme.txt
echo "============="
echo "$A"
echo "$B"
echo "$CMD"
echo "============="

eval "$CMD"

Вариант 2 - ABW, универсальный, более правильный
MkLiteral () { # <string>
  #   Make pattern for literal match of (one-line) <string>
  # in 'grep' or 'sed' regular expressions.
  printf '%s' "${1}" | sed 's/./[&]/g ; s/\^/[.^.]/g'
} # MkLiteral

A='#!/bi\n'"/b'a\"s$.*[]h"
B="$( MkLiteral "$A" )"
CMD="sed -e '/${B//\'/\'\"\'\"\'}/,$ d' 'delme.txt'"

cat delme.txt
echo "============="
echo "$A"
echo "$B"
echo "$CMD"
echo "============="

eval "$CMD"

Kroz ★★★★★
()
Ответ на: комментарий от Xintrea

В ТЗ ничего не сказано об использовании sed, регулярок, и тем более об их экранировании.

Если сед это часть баша, то и питон тоже (и любая программа, особенно, предустановленная)

dsxl
()
Ответ на: комментарий от Kroz

Вариант 2 - ABW, универсальный, более правильный

Ну раз уж bash, то более универсальный и правильный это таки printf -v, и вызывать удобнее.

Но самое смешное, что так задачу и не знаем. Возможно там окажется, что никакого sed не надо и хватит и

[[ $line =~ ...regex..."$var"...regex... ]]
где никакого извращение с эскейпингом не надо и работать быстрее будет.

vodz ★★★★★
()
Последнее исправление: vodz (всего исправлений: 1)
Ответ на: комментарий от aalst

Когда начинаешь бодаться с кавычками и слэшами - это явный признак, что ты делаешь что-то не то

Например юзаешь не приспособленный для этого язык.

и задачу надо решать по-другому.

Write in C. (c)

dimgel ★★★★★
()

cmd=«sed '/»$insertAfterEsc"/,$ d' < «$fileName» > /tmp/sedHead.txt"
echo «$cmd» | bash

Тебе надо удалять из файла строки, содержащие определённую подстроку, а также последнюю строку файла? А нельзя ли тогда это сделать как-то так:

insertAfter='#!/bin/bash'
fileName='./file.txt'

rm -f /tmp/sedHead.txt
touch /tmp/sedHead.txt
do_print_line=""
prev_line=""
while read; do
    if [[ "$do_print_line" ]]; then
        echo "$prev_line" >> /tmp/sedHead.txt
    fi

    if [[ "$REPLY" != *"$insertAfter"* ]]; then
        prev_line="$REPLY"
        do_print_line=y
    else
        do_print_line=""
    fi
done < "$fileName"

proud_anon ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.