комната работает над командной строкой, но не в сценарии

Когда я делаю 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
11
задан 11 September 2016 в 06:07

3 ответа

Если я понимаю то, что Вы делаете (удалите любые файлы с эти .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 вызывает функцию копии, и '{}' имя файла

4
ответ дан 23 November 2019 в 03:53

В Вашем сценарии существуют различные возможные места ошибки. В первую очередь, 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
14
ответ дан 23 November 2019 в 03:53

Единственные случаи 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 уже удаленный файл (снова, не заключая переменную в кавычки), это не имеет никакого смысла, необходимо проверить, что зажим и делает необходимые исправления на основе цели.

8
ответ дан 23 November 2019 в 03:53

Другие вопросы по тегам:

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