Просмотрите все файлы с определенным расширением

110
for i in $(ls);do
    if [ $i = '*.java' ];then
        echo "I do something with the file $i"
    fi
done

Я хочу перебрать каждый файл в текущей папке и проверить, соответствует ли он определенному расширению. Приведенный выше код не работает, знаете почему?

AR89
источник
4
О чем for i in $(ls *.java); do echo "do something with file $i"; done?
Speaker
нет способа исправить это выражение if?
AR89
1
Вы сравниваете $iбуквальную строку "* .java"; Раскрытие паттернов здесь не выполняется.
chepner
Чтобы исправить оператор if в том виде, в каком он у вас есть, используйте if [[ $i == *.java ]]; then.. (обратите внимание на двойные [[]] s и * .java без кавычек).
тот другой парень
2
Неls разбирайте - примите ответ
@chepner

Ответы:

202

Никаких хитрых уловок:

for i in *.java; do
    [ -f "$i" ] || break
    ...
done

Защитник гарантирует, что при отсутствии подходящих файлов цикл завершится без попытки обработать несуществующее имя файла *.java. В bash(или в оболочках, поддерживающих нечто подобное) вы можете использовать nullglobопцию, чтобы просто игнорировать неудачное совпадение и не вводить тело цикла.

shopt -s nullglob
for i in *.java; do
    ...
done
Чепнер
источник
5
Самый простой способ добавить еще один шаблон: for i in *.java *.cpp; do. Если вы расширили шаблоны включены в bashс shopt -s extglob, вы можете написать for i in *.@(java|cpp); do.
chepner
8
Будет, если он действительно соответствует каким-либо файлам. Вам нужно использовать shopt -s nullglobтак, чтобы несоответствующий шаблон расширялся до пустой последовательности, а не обрабатывался буквально.
chepner
1
@zygimantus: да, должно, если текущий каталог - это место, откуда запускается скрипт. Если вы не находитесь в каталоге, в котором хотите быть, вам следует cdперейти в этот каталог, прежде чем начинать forцикл
danielsdesk
1
@puk Это зависит от параметров вашей оболочки. По умолчанию несоответствующий шаблон обрабатывается как буквальная строка. Вы можете настроить nullglobее обработку как пустую последовательность или настроить failglobее обработку как ошибку. (Если вы установите оба, failglobимеет приоритет.)
Чепнер 07
3
@chepner Для неспециалистов может быть полезно указать это в ответе, поскольку кто-то может скопировать и вставить его и обнаружить, что он не работает. Прекрасный пример может быть кто - то , кто является преобразование всех « файлов .jpg» в « .png» перед проведением важной функции
PUK
13

правильный ответ @ chepner's

EXT=java
for i in *.${EXT}; do
    ...
done

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

EXT=java
for i in *; do
    if [ "${i}" != "${i%.${EXT}}" ];then
        echo "I do something with the file $i"
    fi
done
умлют
источник
как было бы с переменной extвместо .java?
AR89
13

Рекурсивно добавлять вложенные папки,

for i in `find . -name "*.java" -type f`; do
    echo "$i"
done
Луисмартингил
источник
вместо того, find . -name "*.java" -type f -exec echo \{\} \; чтобы избежать неправильного анализа выводаfind
umläute
1
А если нет, по крайней мере, вы должны цитировать "$i"внутри цикла.
Tripleee 07
2
Это не сработает, если в имени любого из файлов есть пробел.
codeforester
1
@codeforester У меня была именно эта проблема, и я исправил ее, используя метод из этого ответа: askubuntu.com/a/343753/551184
fsinisi90
7

Перебор всех файлов , заканчивающихся: .img, .bin, .txtсуффикс, и напечатать имя файла:

for i in *.img *.bin *.txt;
do
  echo "$i"
done

Или рекурсивным способом (также можно найти во всех подкаталогах):

for i in `find . -type f -name "*.img" -o -name "*.bin" -o -name "*.txt"`;
do
  echo "$i"
done
Бенни
источник
3

Я согласен с другими ответами относительно правильного способа просмотра файлов. Однако OP спросил:

Приведенный выше код не работает, знаете почему?

Да!

Отличная статья В чем разница между test, [и [[?]] Подробно объясняется, что среди других различий вы не можете использовать expression matchingили pattern matchingвнутри testкоманды (что является сокращением для [)

Функция новый тест [[старый тест [Пример

Сопоставление с шаблоном = (или ==) (недоступно) [[$ name = a *]] || echo "имя не начинается с 'a': $ name"

Регулярное выражение = ~ (недоступно) [[$ (date) = ~ ^ Пт \ ... \ 13]] && echo "Сегодня пятница, 13-е!"
соответствие

Вот почему ваш скрипт не работает. Если OP заинтересован в ответе с [[синтаксисом (недостаток которого заключается в том, что он не поддерживается на таком количестве платформ, как [команда), я был бы счастлив отредактировать свой ответ, включив его.

РЕДАКТИРОВАТЬ: Любые советы о том, как отформатировать данные в ответе в виде таблицы, были бы полезны!

user000001
источник
2

как @chepner говорит в своем комментарии, вы сравниваете $ i с фиксированной строкой.

Чтобы расширить и исправить ситуацию, вы должны использовать [[]] с оператором регулярного выражения = ~

например:

for i in $(ls);do
    if [[ $i =~ .*\.java$ ]];then
        echo "I want to do something with the file $i"
    fi
done

регулярное выражение справа от = ~ проверяется на соответствие значению левого оператора и не должно заключаться в кавычки (кавычки не будут ошибкой, но будут сравниваться с фиксированной строкой и, скорее всего, не удастся "

но ответ @chepner выше с использованием glob - гораздо более эффективный механизм.

петеши
источник
как было бы с переменной extвместо .java?
AR89
1
ack, регулярное выражение не требуется: if [[ $i == *.java ]]или if [[ $i == *.$ext ]]. Но не
разбирайте
1

Я нашел это решение весьма удобным. Он использует эту -orопцию в find:

find . -name \*.tex -or -name "*.png" -or -name "*.pdf"

Он будет искать файлы с расширением tex, pngи pdf.

f0nzie
источник