Когда я делаю rm *.old.*
на командной строке это удаляет правильно, но когда я делаю это в следующей части моего сценария, это не делает комнаты весь *.old.*
файлы.
Что не так в моем сценарии удара:
for i in ./*; do
if [[ -f $i ]]; then
if [[ $i == *.old.* ]]; then
oldfile=$i
echo "this file is to be removed: $oldfile"
rm $oldfile
exec 2>errorfile
if [ -s $errorfile ]
then
echo "rm failed"
else
echo "removed $oldfile!"
fi
else
echo "file with old extension does not exist"
fi
orig=$i
dest=$i.old
cp $orig $dest
echo "Copied $i"
else
echo "${i} is not a file"
fi
done
Если я понимаю то, что Вы делаете (удалите любые файлы с эти .old
суффикс и сделайте копию любых существующих файлов с .old
суффикс), Вы могли использовать, находят вместо этого:
#!/bin/sh
find . -maxdepth 1 -name \*.old -type f -printf "deleting %P\n" -delete
find . -maxdepth 1 -type f -printf "copying %P to %P.old\n" -exec cp '{}' '{}.old' \;
-maxdepth 0
остановки команда находки, смотрящая в подкаталогах, -type f
, ищет регулярные файлы только; -printf
создает сообщения (%P
, найденное имя файла). Эти -exec cp
вызывает функцию копии, и '{}'
имя файла
В Вашем сценарии существуют различные возможные места ошибки. В первую очередь, rm *.old*
будет использовать globbing для создания списка всех файлов соответствия, и это может иметь дело с именами файлов, содержащими пробел. Ваш сценарий, однако, присваивает переменную каждому результату шарика и делает это без заключения в кавычки. Это повредится, если Ваши имена файлов будут содержать пробел. Например:
$ ls
'file name with spaces.old.txt' file.old.txt
$ rm *.old.* ## works: both files are deleted
$ touch "file.old.txt" "file name with spaces.old.txt"
$ for i in ./*; do oldfile=$i; rm -v $oldfile; done
rm: cannot remove './file': No such file or directory
rm: cannot remove 'name': No such file or directory
rm: cannot remove 'with': No such file or directory
rm: cannot remove 'spaces.old.txt': No such file or directory
removed './file.old.txt'
Как Вы видите, цикл, отказавший для файла с пробелами на его имя. Чтобы сделать это правильно, необходимо было бы заключить переменную в кавычки:
$ for i in ./*; do oldfile="$i"; rm -v "$oldfile"; done
removed './file name with spaces.old.txt'
removed './file.old.txt'
Та же проблема относится в значительной степени к каждому использованию $i
в Вашем сценарии. Необходимо всегда заключать переменные в кавычки.
Следующая возможная проблема - то, что Вы, кажется, ожидаете это *.old.*
файлы соответствий с расширением .old
. Это не делает. Это соответствует "0 или больше символам" (*
), затем a .
, затем "старый", затем другой .
и затем "0 или больше символов снова". Это означает, что не будет соответствовать чему-то как file.old
, но только что-то как 'file.old.foo:
$ ls
file.old file.old.foo
$ for i in *; do if [[ "$i" == *.old.* ]]; then echo $i; fi; done
file.old.foo
Так, никакой противник соответствия file.old
. В любом случае Ваш сценарий намного более сложен, чем необходимый. Попробуйте этого вместо этого:
#!/bin/bash
for i in *; do
if [[ -f "$i" ]]; then
if [[ "$i" == *.old ]]; then
rm -v "$i" || echo "rm failed for $i"
else
echo "$i doesn't have an .old extension"
fi
cp -v "$i" "$i".old
else
echo "$i is not a file"
fi
done
Обратите внимание, что я добавил -v
к rm
и CPwhich does the same thing as what you were doing with your
повторите' операторы.
Это не прекрасно с тех пор, когда Вы находите, например, file.old
, это будет удалено и, позже, сценарий попытается скопировать его и сбой, так как файл больше не существует. Однако Вы не объяснили, чего Вы пишете сценарий, на самом деле пытается сделать так, я не могу зафиксировать это для Вас, если Вы не говорите нам, что Вы действительно пытаетесь выполнить.
Если то, что Вы хотите, к i), удаляют все файлы с .old
расширение и ii), добавляют .old
для расширения любых существующих файлов, которые не имеют его, все Вы действительно, нужно:
#!/bin/bash
for i in *.old; do
if [[ -f "$i" ]]; then
rm -v "$i" || echo "rm failed for $i"
else
echo "$i is not a file"
fi
done
## All the ,old files have been removed at this point
## copy the rest
for i in *; do
if [[ -f "$i" ]]; then
## the -v makes cp report copied files
cp -v "$i" "$i".old
fi
done
Единственные случаи rm $oldfile
мог перестать работать, когда Ваше имя файла содержит любой символ IFS
(пространство, вкладка, новая строка) или любой символ шарика (*
, ?
, []
).
Если любой символ IFS
присутствует оболочка выполнит разделение слова и на основе присутствия globbing расширения пути символов на переменном расширении.
Так, например, если имя файла foo bar.old.
, переменная oldfile
содержал бы foo bar.old.
.
Когда Вы делаете:
rm $oldfile
оболочка в первых разделениях расширение oldfile
на пространстве в два слова, foo
и bar.old.
. Таким образом, команда становится:
rm foo bar.old.
который, очевидно, привел бы к неожиданному результату. Между прочим, если у Вас есть какие-либо globbing операторы (*
, ?
, []
) в расширении затем расширение пути было бы сделано также.
Необходимо заключить переменные в кавычки для получения желаемого результата:
rm "$oldfile"
Теперь, никакое разделение слова или расширение пути не были бы сделаны, следовательно необходимо получить желаемый результат, т.е. желаемый файл был бы удален. Если какое-либо имя файла, оказывается, запускается с -
, затем сделайте:
rm -- "$oldfile"
Вы могли бы спросить, почему мы не должны заключать переменные в кавычки при использовании внутри [[
, причем причина [[
a bash
ключевое слово и это обрабатывают переменное расширение, внутренне сохраняющее литерал расширения.
Теперь, несколько точек:
Необходимо перенаправить STDERR (exec 2>errorfile
) перед rm
команда иначе [[ -s errorfile ]]
тест дал бы ложные положительные стороны
Вы использовали [ -s $errorfile ]
, Вы используете переменное расширение $errorfile
, который был бы данным NUL errorfile
переменная не определяется нигде. Возможно, Вы имели в виду, просто [ -s errorfile ]
, на основе перенаправления STDERR
Если переменная errorfile
определяется, при использовании [ -s $errorfile ]
, это снова дросселировало бы на вышеупомянутых случаях IFS
и globbing, потому что, в отличие от этого, [[
, [
не обрабатывается внутренне bash
В более поздней части сценария Вы пытаетесь cp
уже удаленный файл (снова, не заключая переменную в кавычки), это не имеет никакого смысла, необходимо проверить, что зажим и делает необходимые исправления на основе цели.