LINUX.ORG.RU

sed и два пробела

 


0

1

Есть код sed 's/\s[^$]/\\&/g'<<<"$VAR" — заменяет пробелы кроме пробела в конце строки на экранированные.
Проблема в том, что если где-то идёт 2 пробела, он один не экранирует. Как сделать, чтоб экранировал все?
И я даже не понимаю, почему он так делает.
Если без [^$] — то нормально. Но [^$] нужно.

★★★★★

Последнее исправление: teod0r (всего исправлений: 3)

[^$] матчится вторым пробелом, \s[^$] это как бы два символа: первый символ пробел, второй символ — любой, который не конец строки, в случае с двумя пробелами второй пробел.

anarquista ★★★★★
()

Вероятно не объединяет, а экранирует только один. Надеюсь подсказка поможет понять.

Elyas ★★★★★
()

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

sed 's/\s/\\&/g; s/\\((\s\)$/\1/g'

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

нет. именно объединяет. неэкранированного пробела не остаётся

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

потом разэкранировать последний

как вариант сойдёт. благодарю
но причину происходящего тоже понять хочется

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

Нет, & матчит именно два символа, пробел и идущий за ним любой символ, который не конец строки:

$ echo 'Hello world ' | LANG=C sed 's/\s[^$]/A/g'
HelloAorld

и у меня два пробела последовательных пробела не объединяются в один:

$ echo 'Hello   world ' | LANG=C sed 's/\s[^$]/\\&/g'
Hello\  \ world
anarquista ★★★★★
()
Ответ на: комментарий от anarquista

странно. только что у себя проверил — в скрипте объединяются, а не в скрипте — нет, но второй остаётся не экранированным. странности

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

а нет! действительно второй остаётся неэкранированным, просто в скрипте лишнее эхо было

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

ну и что делать? ведь если после пробела — не пробел, а какой-то другой символ, оно же нормально срабатывает. до конца не понятно же. то что дело в [^$] я и так понял

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

ты лучше рассказывай, ЧТО ты хочешь получить от [^$] ?

А то я этого не понимаю, прост о тупо «мне не нравится! А-А-А!!!». Это овуляшки.ру?

emulek
()
% cat wowSpaces
  q w edddascxzc  # <-- тут пробел в конце
% perl -i -lpe 's/\s(?<!$)/\\&/g' testSpaces
% cat testSpaces
\&\&q\&w\&edddascxzc
anonymous
()
Ответ на: комментарий от emulek

ну что от [^$] нужно получить — писал: заэкранировать пробелы, кроме пробела в конце строки

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

В первом cat «wowSpaces», но это был бэкапный файл.

anonymous
()

заменяет пробелы кроме пробела в конце строки на экранированные.

это POSIX регулярками сделать нельзя.

можно тремя:

echo "A B C " | sed 's/ $/\n/;s/ /☣/g;s/\n/¨/'

POSIX регулярки не умеют убирать критерии, только вставлять новые. Т.е. тебе надо что-то вроде " «AND(NOT$). Но есть только OR (|скобки|).

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

я всё-равно не понимаю, почему если после пробела — пробел, оно не обрабатывает оба пробела нормально. ведь если после пробела — не пробел, то пробел нормально экранируется

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

не знаю как на авке сделать

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

Поэтому и посвятил немного времени тому, как с perl заменить awk/sed. Всё равно, он есть почти на всех системах, что я использую (нету всякой макрухи с минимальными окружениями). Плюс в grep есть -P.
// Помимо всего прочего sed regex делают мне больно.

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

Команда s/regexp/replacement/g заменяет часть строки, которая соответствует regexp, строкой replacement и идёт дальше; та часть строки, в которой производилась замена, повторно не рассматривается.

Регулярное выражение \s[^$] всегда соответствует двум символам. Первый символ пробельный, второй — любой, кроме конца строки.

В том случае, если идут два пробела подряд, то они два соответствуют этому регулярному выражению, происходит замена: перед ним ставится \. Команда s идёт дальше, при этом команда считает, что второй пробел уже обработан, ведь он входил в ту часть строки, которая соответствовала регулярному выражению и была заменена.

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

я всё-равно не понимаю, почему если после пробела — пробел, оно не обрабатывает оба пробела нормально. ведь если после пробела — не пробел, то пробел нормально экранируется

Потому что для двух пробелов надо не вставить слеш перед найденным соответствием, а ещё и вставить слеш в середину. А обрабатывается именно соответствие из двух символов, а не единственный пробел

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

та часть строки, в которой производилась замена, повторно не рассматривается

теперь понятно

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

жаль, что sed не умеет 'look-behind' 'look-forward'.

это не баг, а фича.

Кстати, можешь скачать сырцы и сам собрать с какими-то параметрами. И будут у тебя PCRE в sed.

emulek
()
Ответ на: комментарий от anarquista

Первый символ пробельный, второй — любой, кроме конца строки.

нет

$ echo '123'|sed 's/./ /'|sed 's/[^$]/Z/g'
ZZZ

[^$] это «не доллар$» один пробел у тебя не заменился т.к. пробел — не доллар, но надо два пробела(пробел и не доллар). У тебя там всего один.

emulek
()
Ответ на: комментарий от anonymous
% echo "wow" | sed -e 's/$/[I am the end]/'
wow[I am the end]
anonymous
()
Ответ на: комментарий от teod0r

я думал [^$] означает «следующий символ не является концом строки»

не означает. Внутри [скобок] свои правила.

emulek
()
Ответ на: комментарий от teod0r

моя понимает, причём по-моему.

и да, хватит уже

man 7 regex

Obsolete («basic») regular expressions differ in several respects. '|', '+', and '?' are ordinary characters and there is no equivalent for their functionality. The delimiters for bounds are «\{» and «\}», with '{' and '}' by themselves ordinary characters. The parentheses for nested subexpressions are «\(» and «\)», with '(' and ')' by themselves ordi‐ nary characters. '^' is an ordinary character except at the beginning of the RE or(!) the beginning of a parenthesized subexpression, '$' is an ordinary characterexcept at the end of the RE or(!) the end of a parenthesized subexpression, and '*' is an ordinary character if it appears at the beginning of the RE or the beginning of a parenthesized subexpres‐ sion (after a possible leading '^').

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

Короч, понял, что тупой прост. В данном случае [] — character class. В нём не работают в качестве $, ^ anchors.
А ступил из-за предплоложения, что исходный примере

echo ' ' | sed 's/[^$]/Z/g'

таки отработал и осоноваясь на версии teod0r решил объяснить ему.
И если следовать логике мой пример должен быть таким:

% echo '123' | sed 's/[$]/хуй а не конец строки/g'
123

Сорь.

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

В данном случае [] — character class.

угу, там ^ работает как отрицание (в начале класса).

вот-бы ещё замутили такую ерунду в подвыражениях, было-бы хорошо.

emulek
()
Ответ на: ok от wakuwaku
% echo '$$$$\n'|sed 's/[^$]/Z/g'
$$$$

 % sed --version
sed (GNU sed) 4.2.2
...
anonymous
()
Ответ на: комментарий от wakuwaku

неужели так сложно представить LF? точно такой же символ

во первых, у тебя _два_ символа: «\» и «n». Во вторых, \n это, по мнению sed, особый символ. В строках sed \n _никогда_ не бывает(если его ты сам не засадишь).

В третьих, что ты доказываешь? Что мне трудно представить LF?

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

_два_
особый символ

противоречишь себе.

_никогда_ не бывает

всегда есть Ox0A(EOL, почти, но LF у нас), кроме случаев, когда там только одна строка с виртуальным EOF на конце. EOF — место, EOL — вполне себе символ, либо же последовательность оных.

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

противоречишь себе.

читать не умеешь? Повторю: в строках sed \n _никогда_ не бывает(если его ты сам не засадишь).

всегда есть Ox0A(EOL, почти, но LF у нас), кроме случаев, когда там только одна строка с виртуальным EOF на конце. EOF — место, EOL — вполне себе символ, либо же последовательность оных.

ты неадекватен. Я же сказал «в строках sed».

И ты ответил: что ты доказываешь?

emulek
()
Ответ на: комментарий от wakuwaku

Sed получает строки из файлов, причём тут «строки sed»?

при том, что sed всегда грузит строку до EOL, потом обрабатывает, а потом выгружает.

Потому якорь $, это граница между последним символом и EOL. Сам EOL в строку сам никогда не попадает, потому что sed, как его встретит, так сразу начинает обрабатывать.

У меня

$ echo '$$$$\n'|sed 's/[^$]/Z/g'
$$$$ZZ
и тут нет ничего удивительного:

$ не матчится с [^$]

символы «\» и n матчатся, потому каждый символ меняется на Z. Получается $$$$ZZ.

И что?

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

Хм, у меня zsh специфика была.

$ echo '$$$$\n'|sed 's/[^$]/Z/g'
$$$$ZZ

Понаставят всяких shell'ов, а потом толкуй с ними.

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

У меня так:

~ % echo '$$$$\r\n'|sed 's/[^$]/Z/g'                                                                                                                                                                           [0]
$$$$Z

~ % echo '$$$$\n'|sed 's/[^$]/Z/g'                                                                                                                                                                             [0]
$$$$

~ % echo '$$$$\r'|sed 's/[^$]/Z/g'                                                                                                                                                                             [0]
$$$$Z
~ %                                                                                                                                                                                                            [0]

по кажется это фича zsh, он пытается быть eye candy

~ % echo -n '$$$$\r\n'>/tmp/tmp0002.tmp;sed 's/[^$]/Z/g' /tmp/tmp0002.tmp                                                                                                                                      [0]
$$$$Z
~ % 
~ $ echo -n '$$$$\r\n'>/tmp/tmp0002.tmp;sed 's/[^$]/Z/g' /tmp/tmp0002.tmp
$$$$ZZZZ~ $ 
wakuwaku ★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.