Я каждый раз пытаюсь заменить один пробел двумя пробелами в Unix. Мы просто читаем со стандартного ввода и пишем на стандартный вывод. Я также должен избегать использования функций awk и perl. Например, если я читаю что-то вроде San Diego
, должно быть напечатано San Diego
. Если уже есть несколько пробелов, их следует просто оставить в покое.
Замена одного пробела двумя пробелами в Unix
Ответы (4)
Вы должны быть немного осторожны, чтобы не забыть пробелы в начале или конце. Я представляю три решения для образовательных целей:
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
Как насчет только 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
заменить только 1 пробел между 2 символами, не являющимися пробелами, с 2 пробелами
`sed 's/\([^ ]\) \([^ ]\)/\1 \2/g' file`
1)
[^ ]
- не пробел
2)
\1 \2
- первое выражение в скобках, 2 пробела, второе истечение скобок
3)
sed, используемый с s///g
, заменяет регулярное выражение в первом //
значением во втором //
echo "this is a test" | sed s/' '/' '/g
Почему ограничение не использовать awk или perl?
Примечание. Это не имеет ничего общего с Unix. Здесь важна
bash
, и она ведет себя так же, даже если не в Unix (например, в Linux).