Como fazer o comando ‘cut’ tratar os mesmos delimitadores sequenciais como um?

Eu estou tentando extrair um certo (o quarto) campo do stream de texto com base em coluna, ‘espaço’. Estou tentando usar o comando cut da seguinte maneira:

cat text.txt | cut -d " " -f 4

Infelizmente, o cut não trata vários espaços como um delimitador. Eu poderia ter canalizado através do awk

awk '{ printf $4; }'

ou sed

sed -E "s/[[:space:]]+/ /g"

para recolher os espaços, mas eu gostaria de saber se existe alguma maneira de lidar com cut e vários delimitadores nativamente?

Experimentar:

 tr -s ' '  

Da página man tr :

 -s, --squeeze-repeats substituem cada seqüência de input de um caractere repetido
                         que está listado no SET1 com uma única ocorrência
                         desse personagem

Como você comenta em sua pergunta, o awk é realmente o caminho a percorrer. Usar o cut é possível junto com o tr -s para espremer espaços, como mostra a resposta de kev .

Deixe-me no entanto passar por todas as combinações possíveis para futuros leitores. Explicações estão na seção Teste.

tr | cortar

 tr -s ' ' < file | cut -d' ' -f4 

awk

 awk '{print $4}' file 

festança

 while read -r _ _ _ myfield _ do echo "forth field: $myfield" done < file 

sed

 sed -r 's/^([^ ]*[ ]*){3}([^ ]*).*/\2/' file 

Testes

Dado este arquivo, vamos testar os comandos:

 $ cat a this is line 1 more text this is line 2 more text this is line 3 more text this is line 4 more text 

tr | cortar

 $ cut -d' ' -f4 a is # it does not show what we want! $ tr -s ' ' < a | cut -d' ' -f4 1 2 # this makes it! 3 4 $ 

awk

 $ awk '{print $4}' a 1 2 3 4 

festança

Isto lê os campos sequencialmente. Ao usar _ , indicamos que essa é uma variável descartável como uma "variável de lixo" para ignorar esses campos. Dessa forma, armazenamos $myfield como o quarto campo no arquivo, não importando os espaços entre eles.

 $ while read -r _ _ _ a _; do echo "4th field: $a"; done < a 4th field: 1 4th field: 2 4th field: 3 4th field: 4 

sed

Isso captura três grupos de espaços e nenhum espaço com ([^ ]*[ ]*){3} . Então, pega o que vem até um espaço como o quarto campo, que é finalmente impresso com \1 .

 $ sed -r 's/^([^ ]*[ ]*){3}([^ ]*).*/\2/' a 1 2 3 4 

solução mais curta / mais amigável

Depois de ficar frustrado com as muitas limitações de cut , eu escrevi meu próprio substituto, que eu chamei de cuts para “cortar com esteróides”.

cortes fornece o que é provavelmente a solução mais minimalista para este e muitos outros problemas de corte / colagem relacionados.

Um exemplo, dentre muitos, abordando essa questão específica:

 $ cat text.txt 0 1 2 3 0 1 2 3 4 $ cuts 2 text.txt 2 2 

cuts suporta:

  • detecção automática dos delimitadores de campo mais comuns em arquivos (+ capacidade de replace padrões)
  • delimitadores combinados multi-char, mixed-char e regex
  • extraindo colunas de vários arquivos com delimitadores mistos
  • compensações do fim de linha (usando números negativos) além do início da linha
  • colagem automática lado a lado de colunas (não é necessário invocar paste separadamente)
  • suporte para reordenação de campo
  • um arquivo de configuração onde os usuários podem alterar suas preferências pessoais
  • grande ênfase na facilidade de utilização e minimalista necessária digitação

e muito mais. Nenhum dos quais é fornecido pelo cut padrão.

Veja também: https://stackoverflow.com/a/24543231/1296044

Fonte e documentação (software livre): http://arielf.github.io/cuts/

Este one-liner de Perl mostra o quanto o Perl está relacionado ao awk:

 perl -lane 'print $F[3]' text.txt 

No entanto, o array autosplit @F começa no índice $F[0] enquanto os campos awk começam com $1

Com versões de cut eu sei, não, isso não é possível. cut é útil principalmente para analisar arquivos em que o separador não é espaço em branco (por exemplo, /etc/passwd ) e tem um número fixo de campos. Dois separadores em uma linha significam um campo vazio, e isso também vale para os espaços em branco.