Я хочу написать простой скрипт для обнаружения файла, созданного вирусом Windows. Обычно он создает файл .exe с тем же именем, что и каталог, который он удаляет.
Вот сценарий. Это работает, только если имя пути не содержит \ n. Может кто-нибудь помочь мне исправить этот сценарий, пожалуйста!
#!/bin/bash
if [ $# == 0 ]; then
echo ""
echo "==== Give me a directory to begin with! ===="
echo ""
exit
fi
for f in `find $1 -name '*.exe'` ; do
filename=`basename "$f" .exe`
dir_name=`dirname "$f"`
current_dir_name=`basename "$dir_name"`
if [ $filename == $current_dir_name ]; then
rm -f "$f" # It can't remove files
# where the path contains spaces or \n ??!!
fi
done
Это обработает имена файлов, содержащие любой символ, позволенный в имени файла, включая новые строки.
#!/bin/bash
if (($# != 1)); then
echo >&2 "Usage: $0 directory"
exit 1
fi
while IFS= read -r -d '' file; do
base=${file##*/} dir=${file%/*/*}
if [[ $dir/${base%.exe}/$base -ef $file ]]; then
echo rm "$file"
fi
done < <(find "$1" -type f -name "*.exe" -print0)
См. http://mywiki.wooledge.org/BashFAQ/020 и http://mywiki.wooledge.org/BashFAQ/100 для большего количества разработки.
Вы можете использовать ( *.exe )
для решения этой проблемы в bash
.
Помещение круглых скобок, подобных этим (
и )
, вокруг шаблона означает, что он будет расширен и сохранен в массиве.
Затем вы можете перебирать каждый элемент массива, используя что-то вроде этого:
exes=( *.exe )
for exe in "${exes[@]}"; do
echo "'$exe'"
done
Вот полный пример:
$ mkdir 'dir
> with
> spaces'
$ touch 'dir
> with
> spaces/dir
> with
> spaces'
$ touch 'dir
> with
> spaces/other
> file'
$ find .
.
./dir?with?spaces
./dir?with?spaces/dir?with?spaces
./dir?with?spaces/other?file
$ cd 'dir
> with
> spaces'
$ files=( * )
$ for file in "${files[@]}"; do
> echo "'$file'"
> done
'dir
with
spaces'
'other
file'
Я могу предоставить полное решение только после того, как вы уточнить, как выглядит ваша структура каталогов, потому что я не уверен, что current_dir_name
делает то, что вы думаете.
У Bash есть специальная переменная, которая называется IFS.
он используется bash для того, чтобы узнать, как разбить строку на список файлов. (Внутренний разделитель полей)
По умолчанию он содержит пробел, символ табуляции и новую строку.
for f in `find ...
разделится на символы, содержащиеся в переменной $ IFS. Если в именах файлов есть символ новой строки или пробел, возвращаемый командой find, он будет обрабатываться как другое имя файла и, следовательно, цикл будет выполняться для каждой части имени.
$ ls -l -rw-r--r-- 1 marko marko 0 2010-11-01 12:06 my?bad.exe -rw-r--r-- 1 marko marko 0 2010-11-01 12:06 space bad.exe $ for f in $(find -name "*.exe"); do echo "ITERATING ($f)"; done ITERATING (./space) ITERATING (bad.exe) ITERATING (./my) ITERATING (bad.exe)
Мы могли бы настроить переменную $ IFS, но это сложно. Давайте посмотрим почему:
(Протестируйте этот код в сценариях, чтобы изменение переменной IFS не осталось в вашей оболочке, вы можете запутаться)
$ IFS=""; for f in $(find -name "*.exe"); do echo "ITERATING ($f)"; done ITERATING (./space bad.exe ./my bad.exe)
Как вы можете видеть Теперь bash не имеет никакого правила для разделения строки, возвращаемой командой find, и рассматривает весь вывод как одно значение.
Очевидно, что проблема здесь в том, что если мы используем for f in `find....
, у нас нет никакого способа отличить новую строку, содержащуюся в файле, от новой строки, сгенерированной командой find.
Теперь для полноты картины я покажу вам, как заставить ваш подход работать, но, пожалуйста, посмотрите более простое решение в конце ответа.
Мы могли бы использовать другие символы-разделители и исправить вашу текущую проблему:
$ for f in $(find -name "*.exe" -exec echo -n {}: \;); do echo "ITERATING ($f)"; done; ITERATING (./space bad.exe) ITERATING (./my bad.exe) ITERATING ()
Здесь команда find -name "*.exe" -exec echo -n {}: \;
возвращает список файлов, разделенных «:» (которые недопустимы в именах файлов Windows) ,
Теперь следующим шагом будет удаление этих файлов. Примите во внимание, что последний файл завершается символом «:», и, таким образом, bash выполняет итерацию в последний раз с пустой строкой.
IFS=":"; for f in $(find -name "*.exe" -exec echo -n {}: \;); do if [ ! -z "$f" ]; then rm "$f"; fi; done;
Вы должны быть в состоянии выполнить также свой тест базового имени / имени каталога в этом цикле, как вы делали раньше.
Однако это решение безобразно и работает только потому, что у нас есть персонаж, на которого мы можем положиться.
Оказывается, что сама команда find может выполнять команды для каждого найденного файла.
find -name "*.exe" -exec ./yourscript.sh {} \;
Затем yourscript.sh получит полное имя в $ 1, и вы можете удалить его с помощью rm "$1"
, даже если он содержит символы новой строки и пробелы.
Команда find также может использоваться для выполнения встроенного теста базового имени / имени каталога, который вы выполняете в своем скрипте, но это более сложная тема.