Obtém facilmente uma coluna específica da saída sem sed ou awk

4

Existe uma maneira mais rápida de obter algumas colunas de valores do que usar sed e awk ?

Por exemplo, se eu tiver a saída de ls -hal / e quiser apenas obter os nomes e tamanhos de arquivos e diretórios, como posso facilmente e rapidamente fazer isso, sem ter que gastar vários minutos aprimorando meu comando.

total 16078
drwxr-xr-x    33 root  wheel   1.2K Aug 13 16:57 .
drwxr-xr-x    33 root  wheel   1.2K Aug 13 16:57 ..
-rw-rw-r--     1 root  admin    15K Aug 14 00:41 .DS_Store
d--x--x--x     8 root  wheel   272B Jun 20 16:40 .DocumentRevisions-V100
drwxr-xr-x+    3 root  wheel   102B Mar 27 12:26 .MobileBackups
drwx------     5 root  wheel   170B Jun 20 15:56 .Spotlight-V100
d-wx-wx-wt     2 root  wheel    68B Mar 27 12:26 .Trashes
drwxrwxrwx     4 root  wheel   136B Mar 30 20:00 .bzvol
srwxrwxrwx     1 root  wheel     0B Aug 13 16:57 .dbfseventsd
----------     1 root  admin     0B Aug 16  2012 .file
drwx------  1275 root  wheel    42K Aug 14 00:05 .fseventsd
drwxr-xr-x@    2 root  wheel    68B Jun 20  2012 .vol
drwxrwxr-x+  289 root  admin   9.6K Aug 13 10:29 Applications
drwxrwxr-x     7 root  admin   238B Mar  5 20:47 Developer
drwxr-xr-x+   69 root  wheel   2.3K Aug 12 21:36 Library
drwxr-xr-x@    2 root  wheel    68B Aug 16  2012 Network
drwxr-xr-x+    4 root  wheel   136B Mar 27 12:17 System
drwxr-xr-x     6 root  admin   204B Mar 27 12:22 Users
drwxrwxrwt@    6 root  admin   204B Aug 13 23:57 Volumes
drwxr-xr-x@   39 root  wheel   1.3K Jun 20 15:54 bin
drwxrwxr-t@    2 root  admin    68B Aug 16  2012 cores
dr-xr-xr-x     3 root  wheel   4.8K Jul  6 13:08 dev
lrwxr-xr-x@    1 root  wheel    11B Mar 27 12:09 etc -> private/etc
dr-xr-xr-x     2 root  wheel     1B Aug 12 21:41 home
-rw-r--r--@    1 root  wheel   7.8M May  1 20:57 mach_kernel
dr-xr-xr-x     2 root  wheel     1B Aug 12 21:41 net
drwxr-xr-x@    6 root  wheel   204B Mar 27 12:22 private
drwxr-xr-x@   68 root  wheel   2.3K Jun 20 15:54 sbin
lrwxr-xr-x@    1 root  wheel    11B Mar 27 12:09 tmp -> private/tmp
drwxr-xr-x@   13 root  wheel   442B Mar 29 23:32 usr
lrwxr-xr-x@    1 root  wheel    11B Mar 27 12:09 var -> private/var

Eu percebo que há um bazillion opções para ls e eu provavelmente poderia fazê-lo para esse exemplo em particular dessa forma, mas esse é um problema geral e eu gostaria de uma solução geral para obter colunas específicas com facilidade e rapidez.

cut não corta porque não tem uma expressão regular, e eu praticamente nunca tenho a situação em que há um único espaço delimitando colunas. Isso seria perfeito se funcionasse:

ls -hal / | cut -d'\s' -f5,9

awk e sed são mais gerais do que eu quero, basicamente, idiomas inteiros em si mesmos. Eu não tenho nada contra eles, é só que a menos que eu tenha feito muito com eles, isso requer uma mudança mental considerável para começar a pensar em seus termos e escrever algo que funcione. Geralmente estou pensando em algum outro problema que estou tentando resolver e, de repente, ter que resolver um problema sed / awk libera meu foco.

Existe um atalho flexível para alcançar o que eu quero?

    
por iconoclast 14.08.2013 / 07:02

3 respostas

9

Não sei por que

ls -hal / | awk '{print $5, $9}'

parece ser muito mais perturbador para seus processos de pensamento do que

ls -hal / | cut -d'\s' -f5,9

teria sido, se tivesse funcionado. Você realmente teria que escrever isso? Leva apenas alguns awk linhas antes de adicionar o {} se torna automático. (Para mim, a questão mais difícil é lembrar qual número de campo corresponde a qual parte dos dados, mas talvez você não tenha esse problema.)

Você não precisa usar todos os recursos do awk; para simplesmente a saída de colunas específicas, você precisa saber muito pouco awk.

O problema mais irritante seria se você quisesse produzir o link simbólico, assim como o nome do arquivo, ou se os nomes dos seus arquivos pudessem ter espaços neles. (Ou, pior ainda, novas linhas). Com o corte hipotético regex-consciente, isso não é um problema (exceto para as novas linhas); você apenas substituiria -f5,9 por -f5,9- . No entanto, não há sintaxe awk para "campos de 9 até o final", e você fica com a necessidade de lembrar como escrever um loop for.

Aqui está um pequeno script de shell que transforma cut -style -f options em um programa awk e, em seguida, executa o programa awk. Precisa de uma verificação de erros muito melhor, mas parece funcionar. (Bônus adicionado: lida com a opção -d passando para o programa awk).

#!/bin/bash
prog=\{
while getopts f:d: opt; do
  case $opt in
    f) IFS=, read -ra fields <<<"$OPTARG"
       for field in "${fields[@]}"; do
         case $field in
           *-*) low=${field%-*}; high=${field#*-}
                if [[ -z $low  ]]; then low=1; fi
                if [[ -z $high ]]; then high=NF; fi
                ;;
            "") ;;
             *) low=$field; high=$field ;;
         esac
         if [[ $low == $high ]]; then
           prog+='printf "%s ", $'$low';'
         else
           prog+='for (i='$low';i<='$high';++i) printf "%s ", $i;'
         fi
       done
       prog+='printf "\n"}'
       ;;
    d) sep="-F$OPTARG";;
    *) exit 1;;
  esac
done
if [[ -n $sep ]]; then
  awk "$sep" "$prog"
else
  awk "$prog"
fi

Teste rápido:

$ ls -hal / | ./cut.sh -f5,9-
7.0K bin 
5.0K boot 
4.2K dev 
9.0K etc 
1.0K home 
8.0K host 
33 initrd.img -> /boot/initrd.img-3.2.0-51-generic 
33 initrd.img.old -> /boot/initrd.img-3.2.0-49-generic 
...
    
por 14.08.2013 / 07:24
4

Eu acredito que não há solução mais simples do que sed ou awk. Mas você pode escrever sua própria função.

Aqui está a função list (copiar colar no seu terminal):

function list() { ls -hal $1 | awk '{printf "%-10s%-30s\n", $5, $9}'; }

use a função de lista:

list /

list /etc
    
por 14.08.2013 / 08:09
2

Você não pode simplesmente falar sobre "colunas" sem também explicar o que é uma coluna!

Muito comum no processamento de texto unix é ter espaço em branco como separador de coluna (campo) e (naturalmente) nova linha como separador de linha ou registro. Então awk é uma excelente ferramenta, que também é muito legível:

# for words (columns) 5 and 9:
ls -lah | awk '{print $5 " " $9}'
# or this, for the fifth and the last word:
ls -lah | awk '{print $5 " " $NF}'

Se as colunas forem ordenadas em caracteres, talvez cut -c seja melhor.

ls -lah | cut -c 31-33,46-

Você pode dizer a awk para usar outros separadores de campo com a opção -F . Se você não usar -c (ou -b ) com cut , use -f para especificar quais colunas serão produzidas.

O truque é o conhecimento sobre a entrada

Em geral, nem sempre é uma boa ideia analisar a saída de ls , df , ps e ferramentas semelhantes com ferramentas de processamento de texto, pelo menos não se você deseja ser portátil / compatível. Nesses casos, tente forçar a saída em um formato definido por POSIX. Às vezes isso pode ser conseguido passando uma certa opção ( -P maybe) para o comando gerando a saída. Às vezes, definindo uma variável de ambiente como POSIXLY_CORRECT ou chamando um binário específico, como /usr/xpg4/bin/ls .

    
por 14.02.2014 / 16:37