У меня есть список каталогов (или папок, если хотите).
В каждой директории находится файл под названием <что-то>.txt
, который создается из дампа базы данных.
Я хочу запустить задание cron, которое проходит и переименовывает файлы с именем
.
в "${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
существует, условие не выполняется.
Вот о чем я спрашиваю.
Если возможно, было бы создать массив с этими элементами из глобуса, тогда вы могли бы увидеть, есть ли в массиве какие-либо элементы. Например:
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
Мнения экспертов об этом маленьком фрагменте:
f
для представления имени каталога. Это слишком запутанно. Когда вы имеете дело только с переменной оболочки, изолированно (, не связанный ни с чем другим ), просто, без изменений (, например, ##
, %
, :
или /
), вам не нужны фигурные скобки. См. Когда я должен назначать двойные кавычки вокруг переменных, таких как "${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
.