Bash: для имени файла do

Привет, я не привык делать что-то в Bash, поэтому у меня есть некоторые проблемы:

Моя цель - искать специальные файлы в папке и, если они найдены, создавать другие файлы с теми же именами файлов, но с другим расширением.

Что-то вроде:

For Files-that-are-called-"SysBackup*.Now" 
do
  NewFileName = ChangeFileExt(FoundFilename),BK
  GenerateNewfile(NewFileName)
done

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

Итак, цель должна быть:

Если папка содержит Sysbackup123.now и Sysbackup666.now, у меня в итоге останутся файлы Sysbackup123.bk и Sysbackup666.bk

Спасибо за любую помощь

Майкл


person Reiler    schedule 21.11.2009    source источник


Ответы (5)


Довольно просто:

for a in Sysbackup*.now;
do
  [ -f $a ] && touch $(basename $a .now).bk ;
done
person Stephen Darlington    schedule 21.11.2009
comment
Bash также позволяет удалять символы с конца строки с помощью %. И вам, вероятно, следует взять за привычку заключать имена файлов в кавычки: touch ${a%.now}.bk - person NVRAM; 21.11.2009
comment
Хммм, одно: для файла в Sysbackup*.now делать echo WTF? done Проблема в том, что каждый раз, когда я запускаю свой скрипт, WTF? записывается на экран, хотя файла Sysback.now нет. Я бы предположил (и мне нужно), что функция работает только в том случае, если на самом деле есть файл Sysbackup*.now - person Reiler; 21.11.2009
comment
@Reiler: если ни один файл не соответствует, то буквенный шаблон поиска будет назначен a. Таким образом, вам нужно поставить перед командой touch префикс типа [ -e $F ] && или даже более конкретный [ -f $F ] && . Смотрите мой ответ ниже для примера. - person Stephan202; 21.11.2009
comment
Я добавил проверку файла перед командой touch. (У меня изначально была эта строка, но я удалил ее! Дох!) - person Stephen Darlington; 21.11.2009
comment
Большое спасибо, проверка на файл сработала. - person Reiler; 21.11.2009
comment
Рейлер, не забудьте поставить галочку напротив этого ответа, чтобы система знала, что это принятый ответ. - person Lars D; 23.11.2009

Вот как я это делаю с sh:

#!/bin/sh

for file in SysBackup*.now; do
  if [ ! -e "$file" ]; then
    continue
  fi

  base=${file##*/}
  bk=${base%.now}.bk

  touch $bk
done
person Gregory Pakosz    schedule 21.11.2009
comment
Хотя я не считаю это особенно интересным, не будет ли решение, которое я использую, быстрее, поскольку оно не требует разветвления и вызова basename? - person Gregory Pakosz; 26.11.2009

Вот еще один способ замены. Bash имеет эквивалент s/string1$/string2/ sed, который использует знак доллара регулярного выражения для выбора конца шаблона:

for a in Sysbackup*.Now;
do
  [ -f $a ] && touch "${a/%.Now/.BK}"
done

Знак процента совпадает в конце, а решетка (#) — в начале.

person Dennis Williamson    schedule 21.11.2009
comment
Вам нужно использовать .Now в первой строке: если вы сопоставите файлы, они будут заканчиваться .now, а не .Now, как используется OP , и как вы использовали в своем шаблоне переменных, и, как следствие, вы просто коснетесь исходного файла. - person NVRAM; 23.11.2009
comment
Как я уже сказал ghostdog74 - это единственная причина, по которой я обычно избегаю шаблона substitute и вместо этого использую remove/append: ${ file%.Now}.BK, которое никогда не будет совпадать с исходным именем файла. Однажды я сделал что-то вроде этого: grep $PATTERN $a › ${a/%.Now/.BK} -- и в результате потерял все свои данные. - person NVRAM; 23.11.2009

Вы можете сделать что-то вроде этого:

for F in SysBackup*.Now; do
    [ -f $F ] && touch $(echo "$F" | sed 's/Now$/BK/')
done

При этом используется echo для передачи имен файлов в sed, который изменяет строку постфикса "Now" на "BK". touch создает файл, если он еще не существует. Новый файл будет пуст. Тест [ -f $F ] гарантирует, что учитываются только файлы (не каталоги и не символические ссылки).

В качестве альтернативы вы можете использовать find. Пример использования basename, как показано в ответ Стивена Дарлингтона:

find . -type f -iname "SysBackup*.Now" -exec sh -c "touch \$(basename '{}' .Now).BK" \;

Обратите внимание, что в обоих примерах важно заключать имена файлов в кавычки, чтобы обеспечить правильную обработку имен файлов с пробелами.

person Stephan202    schedule 21.11.2009

вот еще один способ, если вы не хотите использовать цикл for и проверять, является ли это файлом или нет

find /path -maxdepth 1 -type f -name "Sysbackup*now" | while read file
do
  touch "${file/%.now/.bK}"
done
person ghostdog74    schedule 21.11.2009
comment
Не уверен, что мне нравится использовать find просто для того, чтобы избежать проблемы с несоответствием, но неважно. Однако вам нужна точка в вашем аргументе -name, иначе будет найден файл с именем Sysbackup-before-snow и, поскольку он не заканчивается на .now, имя файла будут без изменений переданы программе touch. Это единственная причина, по которой я обычно избегаю шаблона substitute и вместо этого использую remove/append: ${file%.now}.bk -- которое никогда не будет таким же, как исходное имя файла (по крайней мере, оно добавит .bk). - person NVRAM; 23.11.2009