Замена одного пробела двумя пробелами в Unix

avatar
Micki
8 апреля 2018 в 02:38
439
4
0

Я каждый раз пытаюсь заменить один пробел двумя пробелами в Unix. Мы просто читаем со стандартного ввода и пишем на стандартный вывод. Я также должен избегать использования функций awk и perl. Например, если я читаю что-то вроде San Diego, должно быть напечатано San Diego. Если уже есть несколько пробелов, их следует просто оставить в покое.

Источник
ivanivan
8 апреля 2018 в 02:48
0

echo "this is a test" | sed s/' '/' '/g

cup
8 апреля 2018 в 02:51
1

Почему ограничение не использовать awk или perl?

Ulrich Eckhardt
8 апреля 2018 в 09:45
0

Примечание. Это не имеет ничего общего с Unix. Здесь важна bash, и она ведет себя так же, даже если не в Unix (например, в Linux).

Ответы (4)

avatar
kvantour
8 апреля 2018 в 08:16
1

Вы должны быть немного осторожны, чтобы не забыть пробелы в начале или конце. Я представляю три решения для образовательных целей:

sed 's/\(^\|[^ ]\) \($\|[^ ]\)/\1  \2/g'    # solution 1
sed 's/\( \+\)/ \1/g;s/ \(  \+\)/\1/g'      # solution 2
sed 's/ \( \+\)/\1/g;s/\( \+\)/ \1/g'       # solution 3    

Все три решения используют подвыражения:

9.3.6 BRE, соответствующие нескольким символам

Подвыражение можно определить в BRE, заключив его между пары символов \( и \). Такое подвыражение должно соответствовать все, что совпало бы без \( и \), за исключением того, что привязка внутри подвыражений является необязательным поведением; см. BRE Привязка выражений. Подвыражения могут быть произвольно вложены.

Выражение обратной ссылки '\n' должно соответствовать тому же (возможно пустая) строка символов, совпадающая с заключенным подвыражением между "\(" и "\)", предшествующими '\n'. Символ n должен быть цифра от 1 до 9, указывающая n-е подвыражение (то, которое начинается с n-го \( от начала шаблона и заканчивается с соответствующей парой \) ). Выражение неверно, если менее чем n подвыражений предшествуют \n. Например, выражение ".∗\1$" соответствует строке, состоящей из двух соседних появления одной и той же строки, а выражение a*\1 не соответствует совпадение a. Когда ссылочное подвыражение соответствует более чем одному строка, выражение с обратной ссылкой должно относиться к последнему совпавшему нить. Если подвыражение, на которое ссылается обратная ссылка, совпадает более одной строки из-за звездочки (*) или интервала выражение (см. пункт (5)), обратная ссылка должна соответствовать последней (самая правая) из этих строк.

Решение 1: sed 's/\(^\|[^ ]\) \($\|[^ ]\)/\1 \2/g'

Здесь два подвыражения. Первое подвыражение \(^\|[^ ]\) соответствует началу строки (^) или (\|) непробельному символу ([^ ]). Второе подвыражение \($\|[^ ]\) аналогично, но с концом строки ($).

Решение 2: sed 's/\( \+\)/ \1/g;s/ \( \+\)/\1/g'

Это заменяет один или несколько пробелов таким же количеством пробелов и одним дополнительным. Затем мы исправляем те, что содержат 3 и более пробела, удаляя из них один пробел.

Решение 3: sed 's/ \( \+\)/\1/g;s/\( \+\)/ \1/g'

Это делает то же самое, что и решение 2, но инвертирует логику. Сначала удалите пробел из всех последовательностей, содержащих более одного пробела, а затем добавьте пробел. Этот однострочник всего на один символ короче решения 2.

Пример: на основе решения 1

Следующие команды представляют собой не что иное, как echo "string" | sed ..., но показывают пробелы, заключенные в оператор printf.

# default string
$ printf "|%s|" " foo bar  car "
| foo bar  car |
# spaces replaced
$ printf "|%s|" "$(echo " foo bar  car " | sed 's/\(^\|[^ ]\) \($\|[^ ]\)/\1  \2/g')"
|  foo  bar  car  |
# 3 spaces in front and back
$ printf "|%s|" "$(echo "   foo bar  car   " | sed 's/\(^\|[^ ]\) \($\|[^ ]\)/\1  \2/g')"
|   foo  bar  car   |

примечание: Если вы хотите заменить любую форму пробелов (пробелы и табуляции в любой кодировке) одним и тем же двойным пробелом, вы можете использовать :

sed 's/\(^\|[^[:blank:]]\)\([[:blank:]]\)\($\|[^[:blank:]]\)/\1\2\2\3/g'
sed 's/\(^\|[[:graph:]]\)\([[:blank:]]\)\($\|[[:graph:]]\)/\1\2\2\3/g
avatar
James Brown
8 апреля 2018 в 08:00
1

Как насчет только bash? Первый тест file:

$ cat file
 1
  2 3
    4  5
San Diego  NO

Затем:

$ cat file | 
while IFS= read line
do 
  while [[ "$line" =~ (^|.+[^ ])\ ([^ ].*) ]]
  do 
    line="${BASH_REMATCH[1]}  ${BASH_REMATCH[2]}"
  done
  echo "$line"
done
  1
  2  3
    4  5
San  Diego  NO
avatar
shaiki siegal
8 апреля 2018 в 06:22
0

заменить только 1 пробел между 2 символами, не являющимися пробелами, с 2 пробелами

  `sed 's/\([^ ]\) \([^ ]\)/\1  \2/g' file`

1) [^ ] - не пробел

2) \1 \2 - первое выражение в скобках, 2 пробела, второе истечение скобок

3) sed, используемый с s///g, заменяет регулярное выражение в первом // значением во втором //

avatar
Alex
8 апреля 2018 в 03:01
0

Что-то вроде

cat input.txt | sed 's,\([[:alnum:]]\) \([[:alnum:]]\),\1  \2,'

должен работать для этой цели.

shaiki siegal
8 апреля 2018 в 06:25
0

красиво, но... я думаю, что вы забыли прикрыть single white space