![Конвейеризация в скриптах awk](https://rvso.com/image/23956/%D0%9A%D0%BE%D0%BD%D0%B2%D0%B5%D0%B9%D0%B5%D1%80%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D1%8F%20%D0%B2%20%D1%81%D0%BA%D1%80%D0%B8%D0%BF%D1%82%D0%B0%D1%85%20awk.png)
Я пытаюсь написать 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' и символическая ссылка ссылается на реальный каталог, то дерево будет следовать пути символической ссылки, как если бы это был реальный каталог.