Tubulação em scripts awk

Tubulação em scripts awk

Estou tentando escrever um lswrapper que use awkpara analisar a saída de ls -lhF. No momento, dividi o programa em dois arquivos - my_ls.she my_ls.awk. my_ls.shO único propósito de é canalizar a saída ls -lhFpara my_ls.awk. Parece:

#!/bin/bash
ls -lhF "$@" | my_ls.awk

Eu queria saber se havia alguma maneira de ler a saída ls -lhFatravés do próprio script awk.

EDITAR:Meu objetivo principal é escrever um script que mostre o conteúdo do diretório atual na forma de uma bela árvore. Uma versão preliminar de my_ls.awkseria semelhante a:

#!/usr/bin/awk -f

( NF >= 9 ) {
    print "|-- [" $5 "] " $9
}

Esseé onde cheguei até agora.

Responder1

Vou me juntar ao outro conselho de que você não deve analisar a saída de ls, então este é um mau exemplo. Mas de uma forma mais geral, eu incluiria o script awk diretamente no script shell, passando-o como um argumento para awk.

#!/bin/bash
ls -lhF "$@" | awk '
    ( NF >= 9 ) {
        print "|-- [" $5 "] " $9
    }'

Observe que se o script awk precisar incluir o 'caractere (aspas simples), você precisará colocá-lo entre aspas: use '\''(fechar aspas simples, aspas simples literal, abrir aspas simples).

Para evitar ter que citar, você pode usar umaqui documentoem vez de. Mas é estranho porque você não pode usar a entrada padrão para alimentar o awk e para alimentar o script. Você precisa usar um descritor de arquivo adicional (vejaQuando você usaria um descritor de arquivo adicional? Descritores de arquivos e scripts de shell).

#!/bin/bash
ls -lhF "$@" | awk -f /dev/fd/3 3<<'EOF'
( NF >= 9 ) {
    print "|-- [" $5 "] " $9
}
EOF

Dentro do awk, você pode ler a entrada de outro comando usando a getlinefunção e a construção do pipe. Não é assim que o awk foi projetado principalmente para ser usado, mas pode ser feito para funcionar. Você precisa citar os argumentos do nome do arquivo para o shell subjacente, que é altamente sujeito a erros. E como o texto a ser processado não vem das fontes esperadas (entrada padrão ou dos arquivos nomeados na linha de comando), você acaba com todo o código no BEGINbloco.

#!/usr/bin/awk -f
BEGIN {
    command = "ls -lhF"
    for (i = 1; i <= ARGC; i++) {
        arg = ARGV[i];
        gsub("'", "'\\''", arg);
        command = command " '" arg "'";
    }
    ARGC = 0; for (i in ARGV) delete ARGV[i];
    while ((command | getline) > 0) {
        if (NF >= 9) { print "|-- [" $5 "] " $9 }
    }
}

Resumindo, use um shell para aquilo em que ele é bom (como encadear comandos juntos) e awk para aquilo em que ele é bom (como processamento de texto).

Responder2

Não tenho certeza do que você está tentando fazer, mas um problema que pode surgir é imprimir awko que lsconsidera ser o último campo, mas que awknão o considera (por meio de sua análise padrão). por exemplo.

-rw-r--r-- | 433k | filename-with-no-spaces      
-rw-r--r-- |   1k | link containing  spaces -> /home/user/filename-with-no-spaces

De alguma forma, você precisa isolar o último lscampo. A abordagem seguida abaixo é encontrar o comprimento de todos os campos anteriores e o delimitador. O resto é o campo do nome do arquivo (além de outras informações, como o destino de um link). 

O script abaixo determina a largura máxima da largura variáveltamanhocampo (necessário para formatação de saída). Existem várias maneiras de obter essa largura; por exemplo.(1)use awkpara processar cada linha de ls saída, no loop principal, adicionando cada linha a uma matriz para END{ }processamento subsequente. ou(2) grave a saída em lsum arquivo temporário e, em seguida, awkprocesse esse arquivo. O método mostrado abaixo usa(2).

Observe que a saída de lspode enviar algumas saídas talvez inesperadas e não simples para você, como no caso de a link, portanto, geralmente é mais seguro usar finde personalizar sua saída para melhor atender às suas necessidades de análise.

f=7               # the number of (multi-space) delimiters before the start of the filename  
myls="$(mktemp)"  # a temp file to hold  output from `ls`
w=$(ls --color=always -lFHk ~/ |tee "$myls" |awk '{print $5}' |wc -L) # max width of size field
h=k               # size unit
awk --re-interval -v"f=$f" -v"w=$w" -v"h=$h" '
  NF >= f {
    regex = "^([^ ]+ +){"f"}" 
    match( $0, regex )  # find start of name field
    printf( "%s | %"w"s%s | %s\n", $1, $5, h, substr( $0, RLENGTH ))
  }' "$myls"
rm "$myls"

Responder3

Eu recomendo evitar reinventar a roda e, em vez disso, usar tree, que apresenta arquivos/pastas e subdiretórios de arquivos/pastas de um diretório:

tree(1) - página de manual do Linux

Nome

árvore - lista o conteúdo dos diretórios em formato de árvore.

Sinopse

árvore [-adfghilnopqrstuvxACDFNS] [-L nível [-R]] [-H baseHREF] [-T título] [-o nome do arquivo] [--nolinks] [-P padrão] [-I padrão] [--inodes] [ --device] [--noreport] [--dirsfirst] [--version] [--help] [--filelimit #] [diretório ...]

Descrição

Tree é um programa recursivo de listagem de diretórios que produz uma listagem de arquivos com recuo profundo. A cor é suportada como dircolors se a variável de ambiente LS_COLORS estiver definida, a saída for um tty e o sinalizador -C for usado. Sem argumentos, tree lista os arquivos no diretório atual. Quando argumentos de diretório são fornecidos, a árvore lista todos os arquivos e/ou diretórios encontrados nos diretórios fornecidos, cada um por vez. Após a conclusão da listagem de todos os arquivos/diretórios encontrados, tree retorna o número total de arquivos e/ou diretórios listados.

Por padrão, quando um link simbólico é encontrado, o caminho ao qual o link simbólico se refere é impresso após o nome do link no formato:

nome -> caminho real

Se a opção '-l' for fornecida e o link simbólico se referir a um diretório real, então tree seguirá o caminho do link simbólico como se fosse um diretório real.

informação relacionada