програйте список файлів, крім випадків, коли вони порожні


33

Я думав, що це буде просто - але це виявляється складніше, ніж я очікував.

Я хочу повторити всі файли певного типу в каталозі, тому я записую це:

#!/bin/bash

for fname in *.zip ; do
   echo current file is ${fname}
done

Це працює , якщо в каталозі є принаймні один відповідний файл . Однак якщо немає відповідних файлів, я отримую це:

current file is *.zip

Потім я спробував:

#!/bin/bash

FILES=`ls *.zip`
for fname in "${FILES}" ; do
    echo current file is ${fname}
done

Хоча тіло циклу не виконується, коли файлів немає, я отримую помилку від ls:

ls: *.zip: No such file or directory

Як записати цикл, який чітко обробляє не відповідні файли?


7
Додати shopt -s nullglobперед запуском циклу for.
cuonglm

@cuolnglm: спокійно це призводить до повернення імені виконуваного сценарію, а не до порожнього списку цього поля RHEL5 (bash 3.2.25), якщо я роблю FILES=ls * .zip, ; for fname in "${FILES}"...але він працює так, як очікувалосяfor fname in *.zip ; do....
symcbean

3
Використовуйте for file in *.zip, не `ls ...`. @ cuonglm пропозиція така, що *.zipрозширюється до нічого, коли шаблон не відповідає жодному файлу. lsбез аргументів перераховує поточний каталог.
Стефан Шазелас

1
У цьому питанні обговорюється, чому lsзагалом слід уникати розбору результатів : Чому б не розібратися ls? ; також дивіться посилання вгорі цієї сторінки до статті BashGuide's ParsingLs .
PM 2Ring

Відповіді:


34

В bash, ви можете встановити nullglobопцію , так що модель , яка не збігається ні з чим «зникає», а не розглядається як літерна рядок:

shopt -s nullglob
for fname in *.zip ; do
   echo "current file is ${fname}"
done

У скрипті оболонки POSIX ви просто переконайтеся, що fnameіснує (і одночасно з цим [ -f ]переконайтеся, що це звичайний файл (або символьне посилання на звичайний файл), а не інші типи, наприклад каталог / fifo / device ...):

for fname in *.zip; do
    [ -f "$fname" ] || continue
    printf '%s\n' "current file is $fname"
done

Замініть [ -f "$fname" ]на, [ -e "$fname" ] || [ -L "$fname ]якщо ви хочете перебирати всі (не приховані) файли, ім'я яких закінчується .zipнезалежно від їх типу.

Замініть *.zipна, .*.zip .zip *.zipякщо ви також хочете розглянути приховані файли, ім'я яких закінчується .zip.


1
shopt -s nullglobне працював для мене на Ubuntu 17.04, але [ -f "$fname" ] || continueпрацював добре.
коппор

@koppor Це здається, що ви насправді не використовуєте bash.
чепнер

2
set ./*                               #set the arg array to glob results
${2+":"} [ -e "$1" ] &&               #if more than one result skip the stat "$1"
printf "current file is %s\n" "$@"    #print the whole array at once

###or###

${2+":"} [ -e "$1" ] &&               #same kind of test
for    fname                          #iterate singly on $fname var for array
do     printf "file is %s\n" "$fname" #print each $fname for each iteration
done                                  

У коментарі тут ви згадуєте про виклик функції ...

file_fn()
    if     [ -e "$1" ] ||               #check if first argument exists
           [ -L "$1" ]                  #or else if it is at least a broken link
    then   for  f                       #if so iterate on "$f"
           do : something w/ "$f"
           done
    else   command <"${1-/dev/null}"    #only fail w/ error if at least one arg
    fi

 file_fn *

2

Скористайтеся знахідкою

export -f myshellfunc
find . -mindepth 1 -maxdepth 1 -type f -name '*.zip' -exec bash -c 'myshellfunc "$0"' {} \;

Ви повинні Експорт вашої функції оболонки, export -fщоб це працювало. Тепер findвиконується, bashякий виконує вашу функцію оболонки, і залишається лише на поточному рівні dir.


Що повторюється через підкаталоги, і я хочу викликати функцію bash (не скрипт) для відповідностей.
symcbean

@symcbean Я відредагував обмеження на функцію
одиночного режиму та керування

-3

Замість:

FILES=`ls *.zip`

Спробуйте:

FILES=`ls * | grep *.zip`

Таким чином, якщо ls не вдасться (що це робиться у вашому випадку), він буде схоплювати невдалий вихід і повертатися як порожня змінна.

current file is      <---Blank Here

Ви можете додати трохи логіки до цього, щоб повернути "Файл не знайдено"

#!/bin/bash

FILES=`ls * | grep *.zip`
if [[ $? == "0" ]]; then
    for fname in "$FILES" ; do
        echo current file is $fname
    done
else
    echo "No Files Found"
fi

Таким чином, якщо попередня команда вдалася (вийшла зі значенням 0), вона надрукує поточний файл, інакше буде надруковано "Не знайдено файлів"


1
Я думаю, що це погана ідея додати ще один процес ( grep), а не намагатися виправити проблему, скориставшись кращим інструментом ( find) або змінивши відповідне налаштування для поточного рішення (з shopt -s nullglob)
Thomas Baruchel

1
Відповідно до коментаря ОП до їхнього оригінального допису, shopt -s nullglobце не працює. Я намагався findперевіряти свою відповідь, і це не вдавалось. Я думаю, що через експортну річ сказав Дані.
Kip K
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.