![Tubulação em scripts awk](https://rvso.com/image/23956/Tubula%C3%A7%C3%A3o%20em%20scripts%20awk.png)
Estou tentando escrever um ls
wrapper que use awk
para analisar a saída de ls -lhF
. No momento, dividi o programa em dois arquivos - my_ls.sh
e my_ls.awk
. my_ls.sh
O único propósito de é canalizar a saída 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 ls -lhF
atravé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.awk
seria 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 getline
funçã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 BEGIN
bloco.
#!/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 awk
o que ls
considera ser o último campo, mas que awk
nã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 ls
campo. 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 awk
para 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 ls
um arquivo temporário e, em seguida, awk
processe esse arquivo. O método mostrado abaixo usa(2).
Observe que a saída de ls
pode enviar algumas saídas talvez inesperadas e não simples para você, como no caso de a link
, portanto, geralmente é mais seguro usar find
e 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.