?

Log in

No account? Create an account
nyaload

Журнал Пушыстого

Журнал Пушыстого

Previous Entry Share Flag Next Entry
frep (find/replace)
nyaload
_winnie
Одну вещь я так и не научился делать из командной строки. Я не научился делать replace в файлах и пайпах, не выходя из рабочего потока. Обычно для find/replace используется sed. Но всякая попытка использовать sed натыкается на то, что надо делать escape для строк. Сначала для непривычного диалекта регулярных выражений, затем для шелла. В notepad если я хочу заменить кусок текста на другой кусок текста, я могу использовать копипаст. Тупо взять кусок текста со всеми кавычками, скобками, слешами, скопировать его в диалог find/replace, выключить если надо галочку "use regular expression", и нажать "Replace all".

В sed же если я хочу заменить "result: \"%g\"\n" %((x*y)*0.5) на 'Result: "%g"\n' %((x*y)/2) мне надо несколько минут вместо секунд, что бы отладить escape-строку "s/\"result: \\\\\"%g\\\\\"\\\\n\" %((x\\*y)\\*0\\.5)/'Result: \"%g\"\\\\n' %((x\\*y)\\/2)/". Сначала эскейп регулярных выражений sed, затем bash. Вместо одного умственного усилия "заменить" приходится вспоминать список из десятка "особенных" символов для двух скриптовых языков: + можно оставить как +, * превращается в \* и затем \\*. Про % сходу не вспоминается, попробуем оставить так как есть. Так, ' остаётся как есть, а вот " сначала остаётся ", а затем превращается в \".

Кстати, обратите внимание, что для ЖЖ поста я использую вот такое выделение. Так как попытка выделить код при помощи спецзнака "кавычка" натыкается на "а что если в тексте уже есть этот спецзнак".


  • 1
А зачем делать все из командной строки?

Делать всё - совсем не обязательно. ЖЖ читать удобней в FireFox. Но иногда проще написать wget http://www.example.com чем сохранять через браузер. А если надо скачать 500 файлов ( как в предыщуем посте ), то вообще нет альтернативы.

Так же и sed. Если надо заменить строки в одном уже открытом файле - то удобней это сделать прямо в IDE. Если надо в сотне файлов по хитрому отфильтрованному списку - приходится это делать из командной строки. Но sed настолько неудобен, что иногда для 10 файлов проще заменить вручную. Или скопипасть вывод консоли в новый файл, там заменить, и скопировать в консоль обратно, чем писать foo | sed a b | bar.
Хотя wget иногда выигрывает в удобстве даже для одного интерактивного действия.


Edited at 2010-10-15 02:30 pm (UTC)

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



Edited at 2010-10-15 02:18 pm (UTC)

Не, не пропадает. Нужно дополнительное правило, что если в тексте уже есть одинарные кавычки - то надо перейти в шелле на двойные. Напр. когда заменяем "сan't на "Can't

Edited at 2010-10-15 02:29 pm (UTC)

О да.

Я генерировал документацию при помощи docbook, для этого нужен xsltproc. Надо было передать в него путь к файлу, путь - так получилось - был полным, т.е. в нем был символ :.

Все пути в xsltproc - URL-ы, в них нельзя D:\ сказать, поэтому : надо эксейпить - по правилам URL, т.е. D%3A\.

xsltproc запускается через cmd.exe, который %3 воспринимает как аргумент командной строки, поэтому передавать надо %%3A.

xsltproc запускается из системы сборки (jamplus), в которой есть команда subst. Команда subst работает так же, как string.gsub в lua - а там спец символ для regexp-like замены это... %.

В итоге:
local XSLDIR = [ Subst $(QUICKBOOK_PATH) : ":" : "%%%%3A" ] ;

:)))
И как при помощи tr заменить "result: %g\n" %((x*y)*0.5) на 'Result: %g\n' %((x*y)/2) ?

например так:
max:~$ echo 'aaaaaaaaaa "result: %g\n" %((x*y)*0.5) aaaaaaa' | tr '"result: %g\n" %((x*y)*0.5)' "'Result: %g\n' %((x*y)/2)"
aaaaaaaaaa 'Result: %g\n' %((x/y)/2))) aaaaaaa

1) Вы вкурсе, что tr может только по одному символу заменять?

2) первый и второй параметр tr - это таблицы перекодировки. Типа, tr "АБВГДE" "ABVGDE". То, что здесь 90% символов совпали - это большая удача.

3) 10% символов таки не совпали ;) (см. скобки на конце). Если бы сдвиг был в начале, а не в конце, был бы полный фейл.

да, лопухнулся, пойду почитаю ман.

А вот для этого у меня в environment есть чудесные функции shell_quote и regex_excape/eregex_escape.

Это какой-то свой макрос для vim? Удобная штука наверное.
Это как в моём посте про Java, работает для написания кода, но не для чтения :/

Не, для шелла, в .zshrc впихал.

Собственно, писались они для того, чтобы санитайзить параметры при использовании их в sed/ssh в скриптах.

shell_quote_stdin()
{
        echo -n "'"; sed "s/'/'\\\\''/"; echo -n "'"
}

# $1 - string to process
# stdout - escaped string
# exit code - none
regex_escape()
{
        echo -n "$1" | regex_escape_stdin
}

# stdin - input string
# stdout - escaped string
# exit code - none
regex_escape_stdin()
{
        sed 's/\([\/\\.*^$]\|\[\|\]\)/\\\1/g'
}

# $1 - string to process
# stdout - escaped string
# exit code - none
eregex_escape()
{
        echo -n "$1" | eregex_escape_stdin
}

# stdin - input string
# stdout - escaped string
# exit code - none
eregex_escape_stdin()
{
        sed 's/\([\(\)\/\\.*+?^${}]\|\[\|\]\)/\\\1/g'
        return 0
}

shell_quote()
{
        echo -n "$1" | shell_quote_stdin
        return 0
}

# $1 - string to be escaped
# $2 - is single-quote quotation needed
# $3 - number of iterations (default is 1)
# stdout - escaped string
exec_escape()
{
        local res
        local iter
        local iters

        res="`echo -n \"$1\"`"
        iters=${3:-1}

        for iter in $(seq 1 "$iters")
        do
                res=$(echo -n "$res" | sed 's/\\/\\\\/g' | ( [ "$2" != "1" ] && sed "s/'/\\\\\\\\'/g" || sed 's/"/\\"/g
s/\$/\\$/g
s/`/\\`/g
s/!/\\!/g'
                ))
        done

        echo -n "$res"
}


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

Тут ещё придумали переводить всё в hex, в hex заменять sedом, и обратно перегонять hex в текст/бинарник.
http://www.linux.org.ru/forum/desktop/4677944#comment-4678469

Чотто уже как-то перректально.

Ыыы, пациента таки отскребли от стенок :)

Я кстати в одном сложном случае сделал питоний скрипт, который делает replace по простому

python -c 'import sys; print sys.stdin.read().replace(sys.argv[1], sys.argv[2])', что ли?

  • 1