Замените несколько пробелов одним, используя только 'tr'

72

У меня есть файл f1.txt:

ID     Name
1      a
2         b
3   g
6            f

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

Это то, что я до сих пор:

cat f1.txt | tr -d " "

Но вывод:

IDName
1a
2b
3g
6f

Но я хочу, чтобы это выглядело так:

ID Name
1 a
2 b
3 g
6 f

Пожалуйста, попробуйте и избегайте sed.

gkmohit
источник
6
Почему так важно избегать sed? Используйте все, что работает!
Дэвид Ричерби
7
Потому что я знаю, как это сделать sed. Хотел узнать другие способы
:)

Ответы:

107

С trпомощью sопции повтора queeze :

$ tr -s " " < file
ID Name
1 a
2 b
3 g
6 f

Или вы можете использовать awkрешение:

$ awk '{$2=$2};1' file
ID Name
1 a
2 b
3 g
6 f

Когда вы изменяете поле в записи, awkперестраиваете $0, берете все поля и объединяете их вместе, разделяя их OFS, что по умолчанию является пробелом.

Это позволит сжать последовательности пробелов и табуляции (и, возможно, других пустых символов в зависимости от локали и реализации awk) в один пробел, но также удалит начальные и конечные пробелы с каждой строки.

cuonglm
источник
1
Это тоже отличное решение. , , Я не знаю, какой выбрать сейчас: / @Gnouc
gkmohit
Не стесняйтесь выбирать любое решение, которое вам нравится, и оно работает для вас. Обратите внимание, что мое решение отличается от ответа @ polym.
cuonglm
1
:)) ура! Ответ @Gnouc действительно динамичен, потому что он использует awk, он может сделать все, что угодно. Вы также можете принять его решение. Только одно: Gnouc, не могли бы вы объяснить, что делает формат awk в вашей команде? Также вы можете добавить табуляции / пробелы, чтобы вывод соответствовал ожидаемому выводу Неизвестного?
полим
1
@polym: с последним редактированием Unknown он, кажется, хочет только один пробел, а не вывод, как это column -tделает. Добавить объяснение для awk.
cuonglm
4
Здесь есть небольшая разница. trзаменит два пробела в конце строки одним пробелом. awkудалит все завершающие пробелы.
Анна Ван Россум,
19

Просто используйте column:

column -t inputFile

Выход:

ID  Name
1   a
2   b
3   g
6   f
Polym
источник
Замечательный и быстрый ответ :)
gkmohit
1
@ Неизвестно Здорово быть на службе :)!
полим
1
@ Gnouc вау круто, колонка также принимает файл в качестве аргумента. приятно спасибо!
полим
Как я могу получить второй столбец, только если я хочу? Я пытался, column -t f1.txt | cut -d " " -f2 но это не было решением, которого я ожидал
gkmohit
2
Тогда используйте awk: column -t file | awk '{print $2}'печатает только второй столбец
polym
8

Если вы хотите сжать «пробел», вам нужно использовать предопределенные наборы символов tr «: blank:» (горизонтальная табуляция и пробел) или «: space:» (вертикальный пробел):

/bin/echo -e  "val1\t\tval2   val3" | tr -s "[:blank:]"

Примеры были запущены на Red Hat 5 (GNU tr).

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

Как указано во втором комментарии Дастробу, я пропустил формулировку на странице руководства:

 -s uses the last specified SET, and occurs after translation or deletion.

Это позволяет нам исключить первый тр. Кудо Скотту за терпение перед лицом моей глупости.

До этого разбирал порт из конфига Redis. файл:

grep "^port" $redisconf | tr "[:blank:]" " " | tr -s "[:blank:]"  | cut -d" " -f2

После, с SET2, указанным с помощью squeeze:

grep "^port" $redisconf | tr -s "[:blank:]" " " | cut -d" " -f2

Выход:

6379

Для более подробной информации о нюансах пробелов

Продемонстрируйте, где одно только сжатие не срабатывает, когда задействованы последовательные смешанные символы, попадающие в класс символов [: blank:]:

 /usr/bin/printf '%s \t %s' id myname | tr -s "[:blank:]"  | od -cb
0000000   i   d      \t       m   y   n   a   m   e
        151 144 040 011 040 155 171 156 141 155 145
0000013

Примечание: два моих строковых поля в формате printf разделены 1 пробелом, 1 табуляцией, 1 пробелом. После сжатия эта последовательность все еще существует. На выходе октального дампа это представлено последовательностью ascii 040 011 040.

user3183018
источник
1
Вам действительно нужно tr "[:blank:]" " " | tr -s "[:blank:]"? Я думаю, первой части будет достаточно, т. Е. tr "[:blank:]" " "Поскольку она нормализует пробелы и уже выполняет подстановку. Со страницы руководства: «Сжатие нескольких вхождений символов [...] Это происходит после того, как все удаление и перевод завершены».
Дастробу
2
поэтому ´tr -s "[: blank:]" "" ´ должен сделать это, сначала он переводит все пробелы в пробелы, а затем сжимает пробелы. Нет необходимости в секунду ´tr´.
Дастробу
1
Я попытался printf 'ID \t Name\n' | tr -s "[:blank:]" " " | od -cb(как предложено @dastrobu), и я получил ID Name\nодним пробелом) в качестве вывода. Вы действительно пробовали это, @ user3183018?
Скотт
1
Хорошо, позвольте мне повторить это снова. Я сделал printf 'ID␣\t␣Name\n' | tr -s "[:blank:]" "␣"  (как предложено @dastrobu), где представляет пробел, и я получил ID␣Name\n(с одним пробелом) в качестве вывода. Это точно так же, как ваш пример «Порт <SPACE> <TAB> <SPACE> 6379», за исключением того, что я использовал строки заголовка из вопроса. Мне интересно, пытались ли вы  tr -s "[:blank:]"(без окончательного "␣"аргумента).
Скотт
1
Когда я это делаю printf 'ID \t Name\n' | od -cb, он показывает именно то, что он должен: ID ⁠  \t ⁠  N a m e \n(т.е.  ID 040 011 040 N a m e\n). Между тем, по вашим собственным данным, вы делаете именно ту ошибку, о которой я догадывался: вы выполняете tr -s "[:blank:]"(то есть  trс одним параметром и  одним аргументом) вместо команды, которую @dastrobu и я представили четыре раза: tr -s '[:blank:]' '␣'(т.е.  trс одним вариантом и  двумя аргументами ).
Скотт
5

Кому нужна программа (кроме оболочки)?

while read a b
do
    echo "$a $b"
done < f1.txt

Если вы хотите, чтобы значения во втором столбце выстраивались в линию, как в ответе polym column, используйте printfвместо echo:

while read a b
do
    printf '%-2s %s\n' "$a" "$b"
done < f1.txt
Скотт
источник
1
Во-первых, по сравнению с tr - это очень слабое предположение с точки зрения эффективности, если только ввод слишком мал или слишком велик, чтобы перевесить крошечную стоимость trвызова - что не говоря уже о том, сколько еще работы требуется, чтобы написать. И последнее, разве вы не сказали бы, что этот пост на самом деле не отвечает на заданный вопрос? Каков наилучший способ заменить все пробелы одним пробелом, используя только tr?
mikeserv
1
И кроме того - не могли бы вы легче сделать что-нибудь с $IFS? Может быть , как: IFS=' <tab>' set -f ; echo $(cat <file)?
mikeserv
2

Это старый вопрос, решаемый много раз. Просто для полноты: у меня была проблема с аналогами, но я хотел передать линии по трубе в другую программу. Я использовал Xargs .

-L max-lines
   Use at most max-lines nonblank input lines per command line.
   Trailing blanks cause an input line to be logically continued 
   on the next input line.  Implies -x.

так , cat f1.txt | xargs -L1кажется, выход именно то , что вы хотите.

Мартин Зейтл
источник