Substitua vários espaços por um usando apenas 'tr'

71

Eu tenho um arquivo f1.txt:

ID     Name
1      a
2         b
3   g
6            f

O número de espaços não é fixo. Qual é a melhor maneira de substituir todos os espaços em branco por um espaço usando apenas tr?

Isto é o que eu tenho até agora:

cat f1.txt | tr -d " "

Mas a saída é:

IDName
1a
2b
3g
6f

Mas eu quero que fique assim:

ID Name
1 a
2 b
3 g
6 f

Por favor, tente e evite sed.

gkmohit
fonte
6
Por que é tão importante evitar sed? Use o que funcionar!
David Richerby
7
Porque eu sei como fazer isso sed. Queria saber outras maneiras:)
gkmohit

Respostas:

106

Com tr, use a opção de repetição do squeeze :

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

Ou você pode usar uma awksolução:

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

Quando você altera um campo no registro, awkreconstrói $0, pega todos os campos e os concatena, separados por OFS, que é um espaço por padrão.

Isso comprime seqüências de espaço e guias (e possivelmente outros caracteres em branco, dependendo do local e da implementação de awk) em um espaço, mas também remove os espaços em branco à esquerda e à direita de cada linha.

cuonglm
fonte
11
Esta é uma ótima solução também. . . Não sei qual escolher agora: / @Gnouc
gkmohit
Sinta-se livre para escolher qualquer solução que você gosta e ela funciona para você. Observe que minha solução é diferente com a resposta da @ polym.
21414 Cuyolm
11
:)) yay! A resposta de @Gnouc é realmente dinâmica, porque ele usa awk, ele pode fazer qualquer coisa. Você também pode aceitar a solução dele. Apenas uma coisa: o Gnouc pode explicar o que o formato awk no seu comando faz? Também é possível adicionar tabulações / espaços para que a saída esteja em conformidade com a saída esperada do Unknown?
polym 22/07
11
@ polym: Com a última edição de Unknown, ele parece querer apenas um espaço, não a saída como o column -tfaz. Adicione explicações para awk.
21814 cu cuklm
4
Há uma pequena diferença aqui. trsubstituirá dois espaços no final de uma linha por um único espaço. awkremoverá todos os espaços à direita.
Anne van Rossum
19

Basta usar column:

column -t inputFile

Resultado:

ID  Name
1   a
2   b
3   g
6   f
polym
fonte
Maravilhoso e uma resposta rápida :)
gkmohit
11
@ Desconhecido Ótimo estar ao serviço :)!
polym 22/07
11
@Gnouc uau legal, coluna também leva um arquivo como argumento. bom obrigado!
polym 22/07
Como posso obter a segunda coluna apenas se eu quiser? Eu tentei column -t f1.txt | cut -d " " -f2 , mas não foi uma solução que eu esperava
gkmohit
2
Use awk then: column -t file | awk '{print $2}'imprime apenas a segunda coluna
polym
8

Se você deseja espremer "espaço em branco", desejará usar os conjuntos de caracteres predefinidos do tr ": blank:" (guia e espaço em branco do espaço horizontal) ou ": space:" (espaço em branco vertical):

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

Exemplos foram executados no Red Hat 5 (GNU tr).

No meu caso, eu queria normalizar todo o espaço em branco em um único espaço para poder confiar no espaço como delmitter.

Como apontado pelo segundo comentário de dastrobu, perdi o texto na página de manual:

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

Isso nos permite eliminar o primeiro tr. Kudo deve procurar suas paciências em face da minha densidade.

Antes, analisando a porta da configuração do Redis. Arquivo:

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

Depois, com SET2 sendo especificado com o aperto:

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

Resultado:

6379

Para mais detalhes sobre as nuances do espaço em branco

Demonstre onde o aperto sozinho falha quando caracteres mistos sucessivos que se enquadram na classe de caracteres [: blank:] estão envolvidos:

 /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

Nota: Meus dois campos de sequência no formato printf são separados por 1 espaço, 1 guia, 1 espaço. Após o aperto, essa sequência ainda existe. Na saída do dump Octal, isso é representado pela sequência ascii 040 011 040.

user3183018
fonte
11
Você realmente precisa tr "[:blank:]" " " | tr -s "[:blank:]"? Eu acho que a primeira parte será suficiente, ou seja, tr "[:blank:]" " "uma vez que normaliza os espaços em branco e já faz a substituição. Na página do manual: "Aperte várias ocorrências dos caracteres [...] Isso ocorre depois que todas as exclusões e traduções são concluídas."
Dastrobu
2
então ´tr -s "[: blank:]" "" ´ deve fazê-lo, primeiro converte todos os espaços em branco em espaços e depois comprime os espaços. Não há necessidade de um segundo 'tr'.
Dastrobu 28/0318
11
Eu tentei printf 'ID \t Name\n' | tr -s "[:blank:]" " " | od -cb(como sugerido por @dastrobu) e recebi ID Name\n(com um espaço) como saída. Você realmente tentou, @ user3183018?
Scott
11
OK, deixe-me tentar dizer isso novamente. Eu fiz printf 'ID␣\t␣Name\n' | tr -s "[:blank:]" "␣"  (como sugerido por @dastrobu), onde representa um espaço, e recebi ID␣Name\n(com um espaço) como saída. É exatamente o mesmo que o seu exemplo de "Porta <ESPAÇO> <TAB> <ESPAÇO> 6379", exceto que eu usei as cadeias de título da pergunta. Gostaria de saber se você tentou  tr -s "[:blank:]"(sem o "␣"argumento final ).
Scott
11
Quando eu faço printf 'ID \t Name\n' | od -cb, mostra exatamente o que deveria: ID ⁠  \t ⁠  N a m e \n(ie,  ID 040 011 040 N a m e\n). Enquanto isso, por sua própria evidência, você está cometendo exatamente o erro que eu imaginei: você está executando tr -s "[:blank:]"(ou seja,  trcom uma opção e  um argumento), em vez do comando que @dastrobu e eu apresentamos quatro vezes agora: tr -s '[:blank:]' '␣'(ou seja,  trcom uma opção e  dois argumentos ).
Scott
5

Quem precisa de um programa (além do shell)?

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

Se você deseja alinhar os valores na segunda coluna, como na columnresposta da polym , use em printfvez de echo:

while read a b
do
    printf '%-2s %s\n' "$a" "$b"
done < f1.txt
Scott
fonte
11
Em primeiro lugar, quando comparado com tr - essa é uma sugestão extremamente fraca em termos de eficiência, a menos que a entrada seja muito pequena supera o pequeno custo da trinvocação - que não é mencionar quanto mais trabalho é necessário para escrever. Por fim, você não diria que este post não responde realmente à pergunta conforme solicitado? Qual é a melhor maneira de substituir todos os espaços em branco por um espaço usando apenas tr?
mikeserv
11
Além disso - você não poderia mais facilmente fazer algo com isso $IFS? Talvez como IFS=' <tab>' set -f ; echo $(cat <file):?
mikeserv
2

Esta é uma pergunta antiga e resolvida muitas vezes. Só para completar: eu tive um problema semelhante, mas queria passar linhas através de um canal para outro programa. Eu usei 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.

então cat f1.txt | xargs -L1parece produzir exatamente o que você deseja.

Martin Seitl
fonte