![Canalización en scripts awk](https://rvso.com/image/23956/Canalizaci%C3%B3n%20en%20scripts%20awk.png)
Estoy intentando escribir un ls
contenedor que se utilice awk
para analizar la salida de ls -lhF
. Ahora he dividido el programa en dos archivos: my_ls.sh
y my_ls.awk
. my_ls.sh
El único propósito de es canalizar la salida ls -lhF
de my_ls.awk
. Parece que:
#!/bin/bash
ls -lhF "$@" | my_ls.awk
Me preguntaba si había alguna forma de leer el resultado ls -lhF
a través del script awk.
EDITAR:Mi objetivo principal es escribir un script que muestre el contenido del directorio actual en forma de un bonito árbol. Una versión borrador de my_ls.awk
se vería así:
#!/usr/bin/awk -f
( NF >= 9 ) {
print "|-- [" $5 "] " $9
}
Estees donde he llegado hasta ahora.
Respuesta1
Me uniré al otro consejo de que no debes analizar el resultado de ls
, por lo que este es un mal ejemplo. Pero como cuestión más general, incluiría el script awk directamente en el script de shell pasándolo como argumento a awk
.
#!/bin/bash
ls -lhF "$@" | awk '
( NF >= 9 ) {
print "|-- [" $5 "] " $9
}'
Tenga en cuenta que si el script awk debe incluir el '
carácter (comilla simple), debe citarlo: use '\''
(comillas simples cerradas, comillas simples literales, comillas simples abiertas).
Para evitar tener que cotizar, puedes utilizar unaquí documentoen cambio. Pero es incómodo porque no se puede usar la entrada estándar tanto para alimentar la entrada a awk como para alimentar el script. Necesita utilizar un descriptor de archivo adicional (consulte¿Cuándo usarías un descriptor de archivo adicional? Descriptores de archivos y scripts de shell).
#!/bin/bash
ls -lhF "$@" | awk -f /dev/fd/3 3<<'EOF'
( NF >= 9 ) {
print "|-- [" $5 "] " $9
}
EOF
Dentro de awk, puedes leer la entrada de otro comando usando la getline
función y la construcción de tubería. No es la forma en que awk está diseñado principalmente para usarse, pero se puede hacer que funcione. Debe citar los argumentos del nombre del archivo para el shell subyacente, que es muy propenso a errores. Y dado que el texto a procesar no proviene de las fuentes esperadas (entrada estándar o los archivos nombrados en la línea de comando), terminas con todo el código en el BEGIN
bloque.
#!/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 }
}
}
En resumen, use un shell para aquello en lo que es bueno (como canalizar comandos) y awk para aquello en lo que es bueno (como procesamiento de texto).
Respuesta2
No estoy muy seguro de lo que está intentando hacer, pero un problema que puede surgir es imprimir awk
lo que ls
se considera el último campo, pero que awk
no se considera así (a través de su análisis predeterminado). p.ej.
-rw-r--r-- | 433k | filename-with-no-spaces
-rw-r--r-- | 1k | link containing spaces -> /home/user/filename-with-no-spaces
De alguna manera necesitas aislar el último ls
campo. El enfoque adoptado a continuación es encontrar la longitud de todos los campos anteriores y el delimitador. El resto es el campo del nombre del archivo (más otra información, como el destino de un enlace).
El siguiente script determina el ancho máximo del ancho variabletamaño(necesario para formatear la salida). Hay varias formas de obtener este ancho; p.ej.(1)utilícelo awk
para procesar cada línea de ls
salida, en el bucle principal, agregando cada línea a una matriz para END{ }
el procesamiento posterior. o(2) escriba la salida en ls
un archivo temporal y luego procese awk
ese archivo. El método que se muestra a continuación utiliza(2).
Tenga en cuenta que la salida de ls
puede enviarle alguna salida inesperada y no simple, como en el caso de a link
, por lo que generalmente es más seguro usar find
y personalizar su salida para que se adapte mejor a sus necesidades de análisis.
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"
Respuesta3
Recomiendo evitar reinventar la rueda y, en su lugar, usar tree
, que presenta los archivos/carpetas de un directorio y los archivos/carpetas de los subdirectorios:
árbol(1) - página de manual de Linux
Nombre
árbol: enumera el contenido de los directorios en formato de árbol.
Sinopsis
árbol [-adfghilnopqrstuvxACDFNS] [-L nivel [-R]] [-H baseHREF] [-T título] [-o nombre de archivo] [--nolinks] [-P patrón] [-I patrón] [--inodes] [ --device] [--noreport] [--dirsfirst] [--version] [--help] [--filelimit #] [directorio...]
Descripción
Tree es un programa de listado de directorios recursivo que produce un listado de archivos con sangría profunda. El color se admite en todos los directorios si la variable de entorno LS_COLORS está configurada, la salida es a un tty y se usa el indicador -C. Sin argumentos, el árbol enumera los archivos en el directorio actual. Cuando se dan argumentos de directorio, el árbol enumera todos los archivos y/o directorios que se encuentran en los directorios dados, cada uno por turno. Al finalizar la lista de todos los archivos/directorios encontrados, el árbol devuelve el número total de archivos y/o directorios enumerados.
De forma predeterminada, cuando se encuentra un enlace simbólico, la ruta a la que hace referencia el enlace simbólico se imprime después del nombre del enlace en el formato:
nombre -> ruta-real
Si se proporciona la opción '-l' y el enlace simbólico se refiere a un directorio real, entonces el árbol seguirá la ruta del enlace simbólico como si fuera un directorio real.