Как использовать глобы в управлении потоком?

У меня есть список каталогов (или папок, если хотите).

В каждой директории находится файл под названием <что-то>.txt, который создается из дампа базы данных.

Я хочу запустить задание cron, которое проходит и переименовывает файлы с именем .txt. .txt в "${PWD##*/}"_info.txt, но только если файл *.txt присутствует, потому что когда этот файл отсутствует, команда mv не срабатывает, и тогда, соблюдая cd . она постоянно перемещается вверх по каталогу, не выполняя оставшиеся проверки, которые намеренно не проиллюстрированы или указаны в приведенном ниже фрагменте кода .

Итак, КОНТЕКСТ кода таков:

for f in <list of directories>
do
  cd "${f}"
  if [ -e "*.txt" ]; then       ← This is the part that is the question
    mv *.txt "${PWD##*/}".txt   ← The rest is merely to provide context
  fi
  cd ..
done

Есть какие-нибудь экспертные мысли по поводу этого небольшого фрагмента?

Я почистил пример. Критической частью является условие:

[ -e "*.txt" ]

Даже если файл <что-то>.txt существует, условие не выполняется.

Вот о чем я спрашиваю.

-2
21.11.2016, 02:31
2 ответа

Если возможно, было бы создать массив с этими элементами из глобуса, тогда вы могли бы увидеть, есть ли в массиве какие-либо элементы. Например:

cur_opt=$(shopt -p nullglob) # get current status so we can restore
shopt -s nullglob # to let the glob expand to nothing
txt_files=( *txt )
$cur_opt # restore nullglob setting to what it was before
if [ ${#txt_files} -gt 0 ]; then
    ...
fi

тогда, если вы хотите работать с файлами, вы также можете перебирать массив, а не просто проверять, есть ли в массиве какие-либо элементы

Если вы действительно просто хотите увидеть, существует ли что-нибудь, что могло бы соответствовать , вы можете использовать compgen , например

if [ ! -z "$(compgen -G "*.txt") ]; then
    ...
fi

, это также может просто проверить возврат для compgen , так что может быть

if compgen -G "*.txt" &>/dev/null; then
    ...
fi
1
28.01.2020, 05:17

Мнения экспертов об этом маленьком фрагменте:

  1. Если вы имеете дело со списком каталогов, и куча файлов (с заранее неизвестными именами ), не используйте fдля представления имени каталога. Это слишком запутанно.
  2. Когда вы имеете дело только с переменной оболочки, изолированно (, не связанный ни с чем другим ), просто, без изменений (, например, ##, %, :или /), вам не нужны фигурные скобки. См. Когда я должен назначать двойные кавычки вокруг переменных, таких как "${var}"для предотвращения проблем?

    Итак, я предлагаю, чтобы внешний цикл выглядел так:

    for d in <list of directories>
    do
        cd "$d"
        (Code to execute in each directory)
        cd..
    done

В дополнение к прекрасному ответу @Eric Renouf, существует несколько способов написать код(для выполнения в каждом каталоге). Вот простой, близкий к тому, что вы пробовали:

for f in *.txt
do
    if [ -e "$f" ]
    then
        mv -- "$f" "${PWD##*/}".txt
    fi
done

Если есть один <something>.txtфайл в каталоге, то это будет делать то, что вы хотите и ожидаете :цикл for f …выполняется один раз, где $fсоответствует имени файла, и файл будет переименован. (Я использую mv --для защиты от файлов чьи имена начинаются с-и в противном случае интерпретировались бы как опции. mv./"$f" …наверное тоже будет безопасно, пока вы уверены, что $f— это файл в текущем каталоге, или является относительным путем. )Если в каталоге нет файлов *.txt, то кроме того, цикл будет выполняться один раз, где $fравно (буквально )до *.txt. Поскольку файла с таким именем нет, тест -eне пройдет. и mvне будет выполнено.

Возникает проблема, если в каталоге несколько текстовых файлов. Если таких файлов n , то цикл будет выполняться n раз, и каждый текстовый файл будет переименован в "${PWD##*/}".txt. Как указал Закари Брейди , это может привести к тому, что последний файл перезапишет первые n −1 файлов. Один из способов исправить это — добавить параметр -iк команде mv. поэтому вы будете предупреждены о любой попытке перезаписать существующий файл, и предоставляется возможность одобрить его или отклонить. Это, конечно, бесполезно, если сценарий работает как задание cron. Другой подход заключается в добавлении команды breakв цикл :

.
for f in *.txt
do
    if [ -e "$f" ]
    then
        mv -- "$f" "${PWD##*/}".txt
        break
    fi
done

Это заставит цикл выполниться ровно один раз, т.е. потому что, когда он запускается, он выполняет команду break, что завершает цикл. Таким образом, будет переименован только первый текстовый файл, а любые другие останутся в покое. Вы не будете уведомлены о наличии нескольких файлов.

Чуть более сложный подход для обнаружения и обработки нескольких файлов:

first=1
for f in *.txt
do
    if [ -e "$f" ]
    then
        if [ "$first" ]
        then
            mv -- "$f" "${PWD##*/}".txt
            first=
        else
            printf "Warning: file %s not renamed.\n" "$f"
        fi
    fi
done

, в котором флаг firstустановлен в начале цикла, а затем очищается во время первой итерации, чтобы последующие итерации знали, что они не первые. В качестве альтернативы вы можете проверить, существует ли уже "${PWD##*/}".txt.

0
28.01.2020, 05:17

Теги

Похожие вопросы