2015-12-01 2 views
6

Скажем, у меня есть следующая структура файлов и каталогов:Как пропустить каталог в awk?

$ tree 
. 
├── a 
├── b 
└── dir 
    └── c 

1 directory, 3 files 

То есть два файла a и b вместе с реж dir, где другой файл c стоит.

Я хочу, чтобы обработать все файлы с awk (GNU Awk 4.1.1, точно), так что я что-то вроде этого:

$ gawk '{print FILENAME; nextfile}' * */* 
a 
b 
awk: cmd. line:1: warning: command line argument `dir' is a directory: skipped 
dir/c 

Все это хорошо, но * также расширяется в каталог dir и awk пытается обработать Это.

Так что мне интересно: есть ли какой-либо родной способ awk может проверить, является ли данный элемент файлом или нет, и если да, пропустите его? То есть, не используя для этого system().

Я сделал свою работу, позвонив в BEGINFILE внешний system:

$ gawk 'BEGINFILE{print FILENAME; if (system(" [ ! -d " FILENAME " ]")) {print FILENAME, "is a dir, skipping"; nextfile}} ENDFILE{print FILENAME, FNR}' * */* 
a 
a 10 
a.wk 
a.wk 3 
b 
b 10 
dir 
dir is a dir, skipping 
dir/c 
dir/c 10 

Отметим также тот факт, что if (system(" [ ! -d " FILENAME " ]")) {print FILENAME, "is a dir, skipping"; nextfile} работает счетчик интуитивно: она должна возвращать 1, если верно, но он возвращает код завершения.

Я прочитал в A.5 Extensions in gawk Not in POSIX awk:

  • каталоги в командной строке производит предупреждение и пропускается (см Command-line directories)

А потом связанная страница говорит:

4.11 Справочники на Командная строка

Согласно стандарту POSIX, файлы, названные в командной строке awk , должны быть текстовыми; это фатальная ошибка, если они не являются. Большинство версий awk относятся к каталогу в командной строке как к фатальной ошибке.

По умолчанию gawk выдает предупреждение для каталога по команде , но в противном случае игнорирует его. Это делает его легче использовать Shell символы с AWK программы:

$ gawk -f whizprog.awk *  Directories could kill this program 

Если какая-либо из --posix или --traditional опции задано, то поглазеть возвращается к обработке каталога в командной строке в виде фатальная ошибка.

См. Extension Sample Readdir, для обработки каталогов как используемых данные из awk-программы.

И на самом деле это так: та же команда, как и раньше с --posix терпит неудачу:

$ gawk --posix 'BEGINFILE{print FILENAME; if (system(" [ ! -d " FILENAME " ]")) {print FILENAME, "is a dir, skipping"; nextfile}} ENDFILE{print FILENAME, NR}' * */* 
gawk: cmd. line:1: fatal: cannot open file `dir' for reading (Is a directory) 

Я проверил раздел 16.7.6 Reading Directories, привязанный выше, и они говорят о readdir:

Расширение readdir добавляет парсер ввода для каталогов. Использование выглядит следующим образом:

@load «READDIR»

Но я не уверен, что ни как назвать его, ни как использовать его из командной строки.

ответ

2

Если вы хотите, чтобы защитить ваш сценарий от других людей ошибочно проходящего каталог (или что-нибудь еще, что это не читаемый текстовый файл) к нему, вы могли бы сделать это:

$ ls -F tmp 
bar dir/ foo 

$ cat tmp/foo 
line 1 

$ cat tmp/bar 
line 1 
line 2 

$ cat tmp/dir 
cat: tmp/dir: Is a directory 

$ cat tst.awk 
BEGIN { 
    for (i=1;i<ARGC;i++) { 
     if ((getline line < ARGV[i]) <= 0) { 
      print "Skipping:", ARGV[i], ERRNO 
      delete ARGV[i] 
     } 
     close(ARGV[i]) 
    } 
} 
{ print FILENAME, $0 } 

$ awk -f tst.awk tmp/* 
Skipping: tmp/dir Is a directory 
tmp/bar line 1 
tmp/bar line 2 
tmp/foo line 1 

$ awk --posix -f tst.awk tmp/* 
Skipping: tmp/dir 
tmp/bar line 1 
tmp/bar line 2 
tmp/foo line 1 

Per POSIX getline возвращается -1, если/когда он терпит неудачу, пытаясь извлечь запись из файла (например, нечитаемый файл или файл не существует, или файл является каталогом), вам просто нужно GNU awk, чтобы рассказать вам, какой из этих сбоев он имеет значение ERRNO, если вам все равно.

+2

Niiiice! Поэтому 'getline' в каталоге не работает напрямую, но может быть обработан. – fedorqui

+0

RIght. Когда я впервые прочитал ваш вопрос, я подумал, что вы пытаетесь использовать awk для поиска файлов/dirs (извините - короткий охват внимания!), Но при повторном чтении это выглядит так, будто вы просто хотите защитить от того, кто вызывает скрипт с не файлом args - нет ничего плохого в этом, и выше, как вы это делаете. Я обновил свой ответ, чтобы немного поддержать это! –

+1

Да, точно. Это просто для предотвращения предупреждений или даже кодов выхода из-за того, что каталог расширяется в предположительно просто списке файлов. Очень интересный ответ, из которого я узнал довольно много, спасибо:) – fedorqui

4

Я бы просто избегал передавать каталоги на awk, так как даже POSIX говорит, что все имена файлов должны быть текстовыми.

Вы можете использовать find для обхода каталога:

find PATH -type f -exec awk 'program' {} + 
+0

Да! Я думаю, что это самый чистый способ сделать это. Тем не менее я задаюсь вопросом, может ли 'awk' это сделать. Я редактировал свой вопрос, потому что ошибочно использовал 'system()', поэтому теперь он работает так, но мне все же не нравится факт вызова внешней команды для этого. – fedorqui

+0

@fedorqui Я также немного поиграл с '@load readdir' (приятно знать, спасибо). Я пришел к тому же результату, что означает использование' system() 'для проверки того, является ли filename каталогом. Я не вижу другого способа. – hek2mgl

+0

Еще раз спасибо hek! Я, наконец, принял ответ Эд Мортона, так как он делает это по-английски. Хотя рекомендация заключается не в том, чтобы делать это в целом. – fedorqui