Piping em scripts do awk

3

Estou tentando gravar um wrapper ls que usa awk para analisar a saída de ls -lhF . Agora eu dividi o programa em dois arquivos - my_ls.sh e my_ls.awk . A única finalidade do my_ls.sh é canalizar a saída de ls -lhF para my_ls.awk . Parece:

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

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

EDIT: Meu objetivo principal é escrever um script que mostre o conteúdo atual do diretório na forma de uma boa árvore. Uma versão de rascunho de my_ls.awk seria semelhante a:

#!/usr/bin/awk -f

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

Este é onde eu cheguei até agora.

    
por westeros91 16.07.2012 / 03:30

3 respostas

2

Vou me juntar ao outro conselho de que você não deve analisar a saída de ls , então esse é um mau exemplo. Mas como uma questão mais geral, eu incluiria o script awk diretamente no shell script 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), será necessário citá-lo: use '\'' (feche aspas simples, aspas simples literais, aspas simples abertas).

Para evitar a cotação, você pode usar um documento aqui . Mas é estranho porque você não pode usar a entrada padrão para alimentar a entrada para o awk e para alimentar o script. Você precisa usar um descritor de arquivo adicional (consulte Quando você usaria um descritor de arquivo adicional? Descritores de arquivo e script 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 função getline e a construção do pipe. Não é como o awk é 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 propenso a erros. E como o texto a ser processado não vem das fontes esperadas (entrada padrão ou arquivos nomeados na linha de comando), você acaba com todo o código no bloco BEGIN .

#!/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 }
    }
}

Em suma, use um shell para o que é bom (como comandos de piping juntos) e awk para o que é bom (como processamento de texto).

    
por 18.07.2012 / 01:28
2

Não sei ao certo o que você está tentando fazer, mas um problema que pode surgir é obter awk para imprimir o que ls considera como o último campo, mas que awk não considera seja assim (via 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 campo ls . A abordagem tomada abaixo é encontrar o tamanho de todos os campos e delilimiter precedentes. 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 do campo tamanho de largura variável (necessário para a formatação de saída). Existem várias maneiras de obter essa largura; por exemplo. (1) use awk para processar cada linha de ls output, no loop principal, adicionando cada linha a uma matriz para processamento END{ } subseqüente. ou (2) escreva a saída de ls em um arquivo temporário e, em seguida, tenha awk processando esse arquivo. O método mostrado abaixo usa (2) .

Observe que a saída de ls pode enviar alguns talvez inesperados, não-simples, do seu jeito, como no caso de um link , por isso geralmente é mais seguro usar find e personalizar é a 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"
    
por 16.07.2012 / 08:47
1

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

tree(1) - Linux man page

Name

tree - list contents of directories in a tree-like format.

Synopsis

tree [-adfghilnopqrstuvxACDFNS] [-L level [-R]] [-H baseHREF] [-T title] [-o filename] [--nolinks] [-P pattern] [-I pattern] [--inodes] [--device] [--noreport] [--dirsfirst] [--version] [--help] [--filelimit #] [directory ...]

Description

Tree is a recursive directory listing program that produces a depth indented listing of files. Color is supported ala dircolors if the LS_COLORS environment variable is set, output is to a tty, and the -C flag is used. With no arguments, tree lists the files in the current directory. When directory arguments are given, tree lists all the files and/or directories found in the given directories each in turn. Upon completion of listing all files/directories found, tree returns the total number of files and/or directories listed.

By default, when a symbolic link is encountered, the path that the symbolic link refers to is printed after the name of the link in the format:

name -> real-path

If the '-l' option is given and the symbolic link refers to an actual directory, then tree will follow the path of the symbolic link as if it were a real directory.

    
por 18.07.2012 / 02:49