2016-05-08 3 views
0

Я ищу, чтобы сбежать от какого-либо персонажа ( и ), их соответствующие коды эвакуации (40) и (41).Как можно заменить sed, но пропустить замененные символы в конвейере?

echo 'Hello (world)' | sed 's/(/(40)/g;s/)/(41)/g' 

Этот код не с Hello (40(41)world(41), потому что он также будет обрабатывать выход из первой замены. Есть ли способ пропустить заменяющие символы или сделать условные ветви здесь. Я не хочу использовать временную (поскольку входная последовательность может содержать что угодно).

+0

заменить на '[40]', то есть окончательный проход, который заменяет все '[' с '(', и т.д. Удачи. – shellter

+0

Как смущающе, почему я не подумал об этом ?! Я убегаю [], так что это хорошее решение.Изменить: нет, подождите, пока это не сработает, потому что [] экранированы ... –

+0

@shellter: Вы можете рассмотреть вопрос об отправке ответа на этот вопрос: D – sjsam

ответ

1

Все, что вам нужно:

$ echo 'Hello (world)' | sed 's/(/(40\n/g; s/)/(41)/g; s/\n/)/g' 
Hello (40)world(41) 

выше безопасен, потому что \n не может присутствовать на входе, так как СЭД читает по одной строке за раз. С некоторыми семенами вам может потребоваться использовать обратную косую черту, за которой следует буквальная новая строка или $'\n', а не только \n.

Учитывая the answer you posted, хотя, это может быть то, что вы действительно хотите (использует GNU AWK для ord(), мульти-символ RS и RT):

$ cat tst.awk 
@load "ordchr" 
BEGIN { RS = "[][(){}]"; ORS="" } 
{ print $0 (RT=="" ? "" : "(" ord(RT) ")") } 

$ echo 'Hello (world) foo [bar] other {stuff} etc.' | awk -f tst.awk 
Hello (40)world(41) foo (91)bar(93) other (123)stuff(125) etc. 

Если у вас есть старый Gawk, который не поддерживает @load, чем получить новый, но если это невозможно по каким-то причинам, то просто создать массив значений, например:

$ cat tst.awk 
BEGIN { 
    RS = "[][(){}]" 
    ORS = "" 
    for (i=0;i<=255;i++) { 
     char = sprintf("%c",i) 
     map[char] = "(" i ")" 
    } 
} 
{ print $0 (RT=="" ? "" : map[RT]) } 

$ echo 'Hello (world) foo [bar] other {stuff} etc.' | awk -f tst.awk 
Hello (40)world(41) foo (91)bar(93) other (123)stuff(125) etc. 

EDIT: выбор времени Данные

Учитывая файл, который имеет следующие 10 строк:

$ head -10 file1m 
When (chapman) billies leave [the] street, And drouthy {neibors}, neibors, meet; 
As market days are wearing late, And folk begin to [tak] the gate, 
While (we) sit bousing {at} the nappy, An' getting [fou] and unco happy, 
We think na on the [lang] Scots (miles), The mosses, {waters}, slaps and stiles, 
That lie between us and our hame, Where sits our sulky, sullen dame, 
Gathering her [brows] like gathering storm, (Nursing) her wrath to keep it warm. 
This truth fand honest Tam o' Shanter, 
As he frae Ayr ae night did canter: 
(Auld Ayr, wham ne'er a town surpasses, 
For honest men and bonie lasses). 

повторяющимися в общей сложности 1 млн линий, 10,5 миллиона символов, 60,4 миллионов байт:

$ wc file1m 
1000000 10500000 60400000 file1m 

3-й перспективе статистика времени для сценария sed и оба сценария awk выше:

$ time sed 's/(/(40\n/g; s/)/(41)/g; s/\n/)/g; s/\[/(91)/g; s/\]/(93)/g; s/{/(123)/g; s/}/(125)/g;' file1m > sed.out 
real 0m7.488s 
user 0m7.378s 
sys  0m0.093s 

$ cat function.awk 
@load "ordchr" 
BEGIN { RS = "[][(){}]"; ORS="" } 
{ print $0 (RT=="" ? "" : "(" ord(RT) ")") } 

$ time awk -f function.awk file1m > awk_function.out 
real 0m7.426s 
user 0m7.269s 
sys  0m0.155s 

$ cat array.awk 
BEGIN { 
    RS = "[][(){}]" 
    ORS = "" 
    for (i=0;i<=255;i++) { 
     char = sprintf("%c",i) 
     map[char] = "(" i ")" 
    } 
} 
{ print $0 (RT=="" ? "" : map[RT]) } 

$ time awk -f array.awk file1m > awk_array.out 
real 0m4.758s 
user 0m4.648s 
sys  0m0.092s 

Я проверил, что все 3 скрипты производят один и тот же, успешно Измененный вывод:

$ head -10 sed.out 
When (40)chapman(41) billies leave (91)the(93) street, And drouthy (123)neibors(125), neibors, meet; 
As market days are wearing late, And folk begin to (91)tak(93) the gate, 
While (40)we(41) sit bousing (123)at(125) the nappy, An' getting (91)fou(93) and unco happy, 
We think na on the (91)lang(93) Scots (40)miles(41), The mosses, (123)waters(125), slaps and stiles, 
That lie between us and our hame, Where sits our sulky, sullen dame, 
Gathering her (91)brows(93) like gathering storm, (40)Nursing(41) her wrath to keep it warm. 
This truth fand honest Tam o' Shanter, 
As he frae Ayr ae night did canter: 
(40)Auld Ayr, wham ne'er a town surpasses, 
For honest men and bonie lasses(41). 
$ wc sed.out 
1000000 10500000 68800000 sed.out 
$ diff sed.out awk_function.out 
$ diff sed.out awk_array.out 
$ 
+0

Есть ли причина предпочесть ваш второй кусок кода над 'sed 's/(/ (40 \ n/g; s /)/(41)/g; s/\ n /)/g; с/\ [/ (91)/г; с/\]/(93)/г; с/{/ (123)/г; с /}/(125)/г; ''? –

+0

Да, это не требует, чтобы вы знали и жестко кодировали порядковые значения, и вам не нужно знать, какие символы должны быть экранированы в регулярном выражении, и если вы хотите добавить больше символов, вы просто добавляете их в список в 'RS'. Вероятно, это будет быстрее, чем сценарий sed, поскольку sed-скрипт анализирует каждую строку ввода 7 раз, в то время как awk-скрипт делает это только один раз. –

+0

Дает синтаксическую ошибку на '@ load', любые предложения? –

0

Проблема решена путем создания функции ord в awk. Это не похоже, что у sed есть эта функция.

#! /bin/sh 

awk ' 
    BEGIN { _ord_init() } 

    function _ord_init(low, high, i, t) { 
     low = sprintf("%c", 7) # BEL is ascii 7 
     if (low == "\a") { 
      low = 0; 
      high = 127; 
     } else if (sprintf("%c", 128 + 7) == "\a") { 
      low = 128; 
      high = 255; 
     } else { 
      low = 0; 
      high = 255; 
     } 
     for (i = low; i <= high; i++) { 
      t = sprintf("%c", i); 
      _ord_[t] = i; 
     } 
    } 
    function ord(str, c) { 
     c = substr(str, 1, 1) 
     return _ord_[c] 
    } 

    // { 
     split($0, array, "\\[|\\]|\\(|\\)|\\{|\\}", separators); 
     len = length(array); 
     seplen = length(separators); 
     for (i = 1; i < len; ++i) { 
      printf "%s(%s)", array[i], ord(separators[i]); 
     } 
     printf "%s", array[len]; 
    } 
' 
+0

Вы используете gawk в своем скрипте (только gawk поддерживает 4-го аргумента для split()), а gawk уже имеет функцию ord(), см. Http://stackoverflow.com/a/37104339/1745001 о том, как ее использовать. –

+0

btw - причина, по которой вам нужны все эти обратные косые черты в вашей команде split ('' \\ [| \\] | \\ (| \\) | \\ {| \\} "') - вы используете строку в контексте регулярного выражения ([динамическое регулярное выражение] (http://www.gnu.org/software/gawk/manual/gawk.html#Computed-Regexps)). Если у вас нет конкретной цели, мы e regexp delimiters ('/.../') (константа регулярного выражения) вместо разделителей строк ('' ... "') в любом месте, где вы используете регулярные выражения, например. '/ \ [| \] | \ (| \) | \ {\ \} /', чтобы избежать этой проблемы, но в этом случае вы должны просто использовать выражение в скобках, то есть '/ [] [() {} ]/'. Кроме того, 'seplen = length (separators);' ничего полезного не делает. –

0

Это сложно в sed, но легко на любом языке с ассоциативными массивами.

perl -pe 'BEGIN { %h = ("(" => "(40)", ")" => "(41)"); 
     $r = join("|", map { quotemeta } keys %h); } 
    s/($r)/$h{$1}/g' 
0

Вы можете сделать это в perl, который поддерживает однострочные и обратные в регулярных выражениях. Просто требуют крупный скобка не является частью существующего побега:

$ echo 'Hello (world)' | perl -pe 's/\(/(40)/g; s/(?<!\(40)\)/(41)/g' 
Hello (40)world(41) 
+0

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