Конвейеризация в скриптах awk

Конвейеризация в скриптах awk

Я пытаюсь написать lsобертку, которая использует awkдля разбора вывода ls -lhF. Сейчас я разделил программу на два файла - my_ls.shи my_ls.awk. my_ls.shединственная цель - передать вывод ls -lhFв my_ls.awk. Выглядит это так:

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

Мне было интересно, есть ли способ прочитать вывод ls -lhFчерез сам скрипт awk.

РЕДАКТИРОВАТЬ:Моя главная цель — написать скрипт, который показывает содержимое текущего каталога в виде красивого дерева. Черновая версия my_ls.awkбудет выглядеть так:

#!/usr/bin/awk -f

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

Этотвот чего я достиг на данный момент.

решение1

Я присоединяюсь к другим советам, что вам не следует анализировать вывод ls, так что это плохой пример. Но в более общем плане я бы включил скрипт awk непосредственно в скрипт оболочки, передав его в качестве аргумента awk.

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

Обратите внимание, что если скрипт awk должен включать 'символ (одинарная кавычка), вам необходимо заключить его в кавычки: используйте '\''(закрытая одинарная кавычка, буквальная одинарная кавычка, открытая одинарная кавычка).

Чтобы избежать необходимости цитировать, вы можете использоватьздесь документВместо этого. Но это неудобно, потому что вы не можете использовать стандартный ввод и для подачи ввода в awk, и для подачи скрипта. Вам нужно использовать дополнительный файловый дескриптор (см.Когда следует использовать дополнительный файловый дескриптор? Файловые дескрипторы и скрипты оболочки).

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

Внутри awk вы можете читать ввод из другой команды, используя getlineфункцию и конструкцию конвейера. Это не тот способ, для которого изначально был разработан awk, но его можно заставить работать. Вам нужно заключать в кавычки аргументы имени файла для базовой оболочки, что очень подвержено ошибкам. И поскольку текст для обработки не поступает из ожидаемых источников (стандартный ввод или файлы, указанные в командной строке), вы в итоге получаете весь код в блоке 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 }
    }
}

Короче говоря, используйте оболочку для тех задач, для которых она подходит лучше всего (например, для объединения команд), а awk — для тех задач, для которых она подходит лучше всего (например, для обработки текста).

решение2

Я не совсем уверен, что вы пытаетесь сделать, но одна из проблем, которая может возникнуть, — это необходимость awkраспечатать то, что lsсчитается последним полем, но которое awkтаковым не считается (из-за его синтаксического анализа по умолчанию). Например:

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

Каким-то образом вам нужно изолировать последнее lsполе. Подход, используемый ниже, заключается в том, чтобы найти длину всех предыдущих полей и разделитель. Остальное — это поле имени файла (плюс другая информация, например, цель ссылки). 

Скрипт ниже определяет максимальную ширину переменной шириныразмерполе (необходимо для форматирования вывода). Есть несколько способов получить эту ширину; например.(1)использовать awkдля обработки каждой строки ls вывода в основном цикле, добавляя каждую строку в массив для последующей END{ }обработки. или(2) записать вывод lsво временный файл, а затем обработать awkэтот файл. Метод, показанный ниже, использует(2).

Обратите внимание, что выходные данные lsмогут быть неожиданными, непростыми и нестандартными, как в случае с link, поэтому обычно безопаснее использовать findи настраивать его выходные данные в соответствии с вашими потребностями в анализе.

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"

решение3

Я рекомендую не изобретать велосипед, а вместо этого использовать tree, который представляет файлы/папки каталога и подкаталоги файлы/папки:

tree(1) - страница руководства Linux

Имя

дерево - список содержимого каталогов в древовидном формате.

Синопсис

дерево [-adfghilnopqrstuvxACDFNS] [-L уровень [-R]] [-H baseHREF] [-T заголовок] [-o имя_файла] [--nolinks] [-P шаблон] [-I шаблон] [--inodes] [--device] [--noreport] [--dirsfirst] [--version] [--help] [--filelimit #] [каталог ...]

Описание

Tree — это рекурсивная программа листинга каталогов, которая создает список файлов с отступом по глубине. Цвет поддерживается как dircolors, если установлена ​​переменная среды LS_COLORS, вывод осуществляется на tty и используется флаг -C. Без аргументов tree выводит список файлов в текущем каталоге. Если указаны аргументы каталога, tree выводит список всех файлов и/или каталогов, найденных в указанных каталогах, по очереди. После завершения вывода списка всех найденных файлов/каталогов tree возвращает общее количество перечисленных файлов и/или каталогов.

По умолчанию при обнаружении символической ссылки путь, на который ссылается символическая ссылка, выводится после имени ссылки в формате:

имя -> реальный-путь

Если указана опция '-l' и символическая ссылка ссылается на реальный каталог, то дерево будет следовать пути символической ссылки, как если бы это был реальный каталог.

Связанный контент