2014-02-19 3 views
1

Я пытаюсь перебрать файлы из списка указанных расширений с помощью скрипта bash. Я попробовал решение, указанное в Matching files with various extensions using for loop, но оно не работает должным образом. Приведенное решение было:Зацикливание файлов с указанными расширениями в bash

for file in "${arg}"/*.{txt,h,py}; do 

Вот моя версия этого:

for f in "${arg}"/*.{epub,mobi,chm,rtf,lit,djvu} 
    do 
     echo "$f" 
    done 

Когда я запускаю это в директории с epub файла в нем, я получаю:

/*.epub 
/*.mobi 
/*.chm 
/*.rtf 
/*.lit 
/*.djvu 

Поэтому я попытался изменить заявление for:

for f in "${arg}"*.{epub,mobi,chm,rtf,lit,djvu} 

Тогда я получил:

089281098X.epub 
*.mobi 
*.chm 
*.rtf 
*.lit 
*.djvu 

Я также получить тот же результат с:

for f in *.{epub,mobi,chm,rtf,lit,djvu} 

Таким образом, кажется, что "${arg}" аргумент не нужен.

Хотя любое из этих утверждений находит файлы указанных расширений и может передать их программе, я получаю ошибки чтения из неразрешенных имен файлов *..

Я запускаю это на OS X Mountain Lion. Я знал, что оболочка bash по умолчанию устарела, поэтому я обновил ее с 3.2.48 до 4.2.45, используя homebrew, чтобы узнать, была ли эта проблема. Это не помогло, поэтому мне интересно, почему я получаю эти неожиданные результаты. Является ли данное решение неправильным или представляет собой оболочку bash OS X, как-то отличную от версии *NIX? Возможно ли альтернативный способ выполнить то же самое, что может работать лучше в оболочке bash OS X?

+0

Hi. Не могли бы вы более подробно объяснить, что вы пытаетесь сделать? Так как код сломан, трудно использовать * это * как описание того, что вы хотите. – jpaugh

ответ

2

Это может быть BASH 4.2ism. Это не работает в моем BASH, который все равно 3.2. Однако, если вы shopt -s extglob, вы можете использовать вместо *(...):

shopt -s extglob 
for file in *.*(epub|mobi|chm|rtf|lit|djvu) 
do 
    ... 
done 

@David W.: shopt -s extglob for f in .(epub|mobi|chm|rtf|lit|djvu) results in: 089281098X.epub @kojiro: arg=. shopt -s nullglob for f in "${arg}"/.{epub,mobi,chm,rtf,lit,djvu} results in: ./089281098X.epub shopt -s nullglob for f in "${arg}".{epub,mobi,chm,rtf,lit,djvu} results in: 089281098X.epub So all of these variations work but I don't understand why. Can either of you explain what is going on with each variation and what ${arg} is doing? I would really like to understand this so I can increase my knowledge. Thanks for the help.

В шахте:

for f in *.*(epub|mobi|chm|rtf|lit|djvu) 

я не включил ${arg}, которая расширяется до значения $arg. *(...) соответствует шаблону, найденному в круглых скобках, который является одним из любых серий расширений. Таким образом, он соответствует *.epub.

Кодзиро-х:

arg=. 
shopt -s nullglob 
for f in "${arg}"/*.{epub,mobi,chm,rtf,lit,djvu} 

ли включая $arg и косой черты в его согласовании. Таким образом, начало кориро начинается с ./, потому что это то, о чем они просят.

Это как разница между:

echo * 

и

echo ./* 

Кстати, вы могли бы сделать это с другими выражениями тоже:

echo *.*(epub|mobi|chm|rtf|lit|djvu) 

оболочка делает все расширение для вас. Это действительно не имеет никакого отношения к самому заявлению for.

+0

+1 Ах да, прекрасный «extglob». Я забыл, что у него есть это выражение. – kojiro

+0

@ Давид W .: Спасибо за объяснение. BTW 'echo *. * (Epub | mobi | chm | rtf | lit | djvu)' дает ошибку: '-bash: синтаксическая ошибка около неожиданного токена '(' '. Я не знаю почему. – hmj6jmh

+0

У вас есть' shopt -s extglob'? По какой-то причине Bash не выполняет расширенное автоматическое разбиение. –

-1

Это следует сделать это:

for file in $(find ./ -name '*.epub' -o -name '*.mobi' -o -name '*.chm' -o -name '*.rtf' -o -name '*.lit' -o -name '*.djvu'); do 
    echo $file 
done 
+2

-1 Анализ вывода 'find' почти такой же [плохой, как синтаксический анализ' ls'] (http://mywiki.wooledge.org/ParsingLs). Он сломается, если в найденных файлах есть пробелы в именах. Кроме того, если 'find' является правильным ответом, просто выполните' find ./ -name '* .epub' -name ... 'и найдите печать. – kojiro

+0

@kojiro 1. Вы издеваетесь надо мной? Единственное решение, предлагаемое в этой статье, - использовать глобус. Не совсем самое гибкое решение, особенно если вам нужно работать с подкаталогами. 2. OP имеет петлю для «без причины», жалуйтесь на них. Я работаю в предположении, что цикл for необходим для того, что они * на самом деле хотят выполнить. – Sammitch

+0

Я стою от того, что я сказал, - зацикливание на замену команды wordplit редко бывает хорошей идеей. 1. Глобус чрезвычайно гибкий, особенно с расширениями, такими как nullglob, extglob и globstar, но даже без них неясно, что OP хотел рекурсировать. 2. Опять же, если 'find' является правильным ответом, весь ответ может быть выражен с помощью' find'. Если 'find ... -print' не прав,' find ... -exec' есть. Запрет на то, 'find ... -print0 | xargs -0 ... '. – kojiro

0

Глобы должно расширяться к существующему, найденному имени, или он остается наедине со звездочкой неповрежденной. Если у вас пустой каталог, то *.foo будет расширяться до *.foo. (Если вы не используете расширение Bash nullglob.)

Проблема с вашим кодом заключается в том, что вы начинаете с arg, $arg, который, по-видимому, пуст или не определен. Таким образом, ваш glob, ${arg}/*.epub расширяется до /*.epub, потому что в корневом каталоге нет файлов, заканчивающихся на «.epub». Он никогда не ищет в текущем каталоге. Для этого вам нужно сначала установить arg=..

В вашем втором примере ${arg}*.epub действительно расширяется, потому что $arg пуст, но другие файлы не существуют, поэтому они не расширяются, как глобусы. Как я уже намекал, одним из простых способов было бы активировать nullglob с shopt -s nullglob. Это зависит от bash, но приведет к тому, что *.foo будет расширяться до пустой строки, если нет соответствующего файла. Для строгого решения POSIX вам нужно будет отфильтровать нерасширенные глобусы, используя [ -f "$f" ]. (Опять же, если вы хотите POSIX, вы также не можете использовать расширение скобки.)

+0

@ David W .: 'shopt -s extglob' ' for f in *. * (Epub | mobi | chm | rtf | lit | djvu) ' результаты: ' 089281098X.epub' @kojiro: 'Arg = .' ' shopt -s nullglob' 'для F в "$ {арг}"/ * {EPUB, Mobi, CHM, RTF, горит, DjVu}' приводит. :. '/ 089281098X.epub' ' shopt -s nullglob' 'для F в "$ {арг}" * {EPUB, Mobi, CHM, RTF, горит, DjVu}' результаты в:. ' 089281098X.epub' Так что все эти вариации работают, но я не понимаю, почему. Может ли кто-нибудь из вас объяснить, что происходит с каждым вариантом и что делает '$ {arg}'? Я бы очень хотел это понять, чтобы я мог увеличить свои знания. Спасибо за помощь. – hmj6jmh

+0

См. Добавление к моему первоначальному ответу. –

0

Подводя итог, лучшие решения использовать (наиболее интуитивный и элегантный):

shopt -s extglob 
for f in *.*(epub|mobi|chm|rtf|lit|djvu) 

или, в соответствии с оригинальным решением данного в указанной теме (что было неправильно, как указано):

shopt -s nullglob 
for f in "${arg}"*.{epub,mobi,chm,rtf,lit,djvu} 
Смежные вопросы