Как заменить пробелы и косую черту в строке в bash?

дав строку:

foo='Hello         
World!   
x

we are friends

here we are'

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

echo "$foo" | tr "[st]\[st]n[st]" " " | tr -s " "

возвращает:

Hello World! x we are friend here we are 

и результат мне нужен:

Hello World! x

we are friends

here we are

какая-то идея, совет или трюк, чтобы сделать это? Могу ли я получить желаемый результат только в команде?

9 ответов


следующая острота дает желаемый результат:

echo "$foo" | tr '\n' '\r' | sed 's,\s*\\s*, ,g' | tr '\r' '\n'
Hello World!

we are friends

here we are

объяснение:

tr '\n' '\r' удаляет новые строки из входных данных, чтобы избежать специального поведения sed для новых строк.

sed 's,\s*\\s*, ,g' преобразует пробелы со встроенным \ в одно пространство.

tr '\r' '\n' кладет неизменной строк.


попробовать, как показано ниже:

#!/bin/bash

foo="Hello     \
World!"

echo $foo | sed 's/[\s*,\]//g'

если вы просто хотите вывести на печать, и вам просто нужно:

foo='Hello     \
World!'
bar=$(tr -d '\' <<<"$foo")
echo $bar    # unquoted!
Hello World!

если вы хотите сжать пробелы, поскольку они хранятся в переменной, то один из:

bar=$(tr -d '\' <<<"$foo" | tr -s '[:space:]' " ")
bar=$(perl -0777 -pe 's/\$//mg; s/\s+/ /g' <<<"$foo")

преимущество версии perl заключается в том, что она удаляет только обратные косые черты продолжения строки (в конце строки).


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

$ foo="Hello    \
World"
$ echo "$foo"
Hello    World

Итак, на данный момент уже слишком поздно.

если вы используете одинарные кавычки, оболочка не будет интерпретировать продолжения строк и

$ foo='Hello     \
World!

here we are'
$ echo "$foo"
Hello     \
World!

here we are
$ echo "$foo" | perl -0777 -pe 's/(\s*\\s*\n\s*)/ /sg'
Hello World!

here we are

foo='Hello     \    
World! \  
x

we are friends

here we are'

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

я добавил обратную косую черту после World! для проверки нескольких строк обратной косой черты в строке.

sed -r ':s; s/( )? *\ *$//; Te; N; bs; :e; s/\n *//g' <<< "$foo"

выход:

Hello World! x

we are friends

here we are

что это делаешь? В псевдо-коде вы можете прочитать это как:

while (s/( )? *\ *$//) {  # While there's a backslash to remove, remove it...
    N                        # ...and concatenate the next line.
}

s/\n *//g                    # Remove all the newlines.

подробно, вот что это делает:

  1. :s это ветка с надписью s для "start".
  2. s/( )? *\ *$// заменяет обратную косую черту и окружающие ее пробелы. Он оставляет одно пространство, если оно было, захватив ( )?.
  3. если предыдущая подстановка не удалась,Te переход на метку e.
  4. N объединяет следующую строку, включая новую строку \n.
  5. bs возвращается к началу. Это чтобы мы могли справиться. несколько последовательных строк с обратными косыми чертами.
  6. :e это ветка с надписью e для "end".
  7. s/\n *//g удаляет все дополнительные новые строки из шага #4. Он также удаляет ведущие пробелы из следующей строки.

отметим, что T является расширением GNU. Если вам нужно это для работы в другой версии sed, вам нужно использовать . Это, вероятно, займет дополнительное b метка или два.


можно использовать read петли, чтобы получить желаемый результат.

arr=()
i=0
while read line; do
    ((i++))
    [ $i -le 3 ] && arr+=($line)
    if [ $i -eq 3 ]; then
        echo ${arr[@]}
    elif [ $i -gt 3 ]; then
        echo $line
    fi
done <<< "$foo"

С awk:

$ echo "$foo"
Hello     \
World! \
x

we are friends

here we are

С трейлингом newline:

$ echo "$foo" | awk '{gsub(/[[:space:]]*\[[:space:]]*/," ",)}1' RS= FS='\n' ORS='\n\n'
Hello World! x

we are friends

here we are
                                                                                              .

без трейлинга newline:

$ echo "$foo" | 
awk '{
  gsub(/[[:space:]]*\[[:space:]]*/," ",)
  a[++i] = 
}
END {
  for(;j<i;) printf "%s%s", a[++j], (ORS = (j < NR) ? "\n\n" : "\n")
}' RS= FS='\n' 
Hello World! x

we are friends

here we are

sed-отличный инструмент для простых подмножеств на одной строке, но для чего-либо еще просто используйте awk. Это использует GNU awk для multi-char RS (с другими awks RS='' будет работать для текстовых файлов, которые не содержат нуль символов):

$ echo "$foo" | awk -v RS='^$' -v ORS= '{gsub(/\s+\\s+/," ")}1'
Hello World! x

we are friends

here we are

С башизмами, такими как расширенные подстановка, расширения параметр etc...но это, вероятно, так же уродливо

foo='Hello     \    
World!'
shopt -s extglob
echo "${foo/+( )\*( )$'\n'/ }"
Hello World!

Как я понимаю, вы хотите просто удалить конечные пробелы, за которыми следует новая строка с обратной косой чертой?

в этом случае выполните поиск с помощью regex ( ) *\\n и заменить на