Невозможно переместить ./test в сам подкаталог «./test/test».

1067
Egon Olieux

При попытке конвертировать некоторые файлы в нижний регистр я заметил что-то странное для меня.

При выполнении этого в Bash:

find . -iname 'test' | while IFS='\n' read item; do mv $item $(echo $item | tr [:upper:] [:lower:]); done 

И источник и цель неизменны для mv, я получаю следующую ошибку:

mv: cannot move '.test' to a subdirectory of itself, './test/test' 

Откуда ./test/testвзялся? Когда эхо вместо выполнения mv, я получаю mv ./test ./test. Я тестировал это как на FreeBSD 10, так и на Debian 8.2.0 с одинаковыми результатами. Что мне не хватает?

1
Хорошо, имеет смысл. Что бы вы предложили, если я хочу переименовать несколько каталогов? Можно ли это сделать без ошибок без явной проверки, являются ли они одинаковыми? Egon Olieux 8 лет назад 0
Я понимаю, почему переход на другое дерево будет работать, но чем `./*` отличается от `.`? `./*` будет расширяться до каждого элемента в каталоге, в то время как `.` будет брать текущий каталог, но, в конце концов, он все еще перемещается в том же каталоге. Давайте возьмем каталог `dir`, например, который содержит 5 каталогов` a`, `b`,` c`, `d` и` e`. Если я хочу переименовать все подкаталоги `dir`,` find .` и `find ./*` вернет тот же результат. Egon Olieux 8 лет назад 0
Предполагая, что текущим рабочим каталогом является `dir`. Egon Olieux 8 лет назад 0
@FrankThomas + Egon: `-iname test` не соответствует` .test`; из второго результата (с эхом) видно, что это действительно `. / test` и просто неправильно цитируется. `-ilname` в точности совпадает с` -iname`, за исключением символических ссылок, которые здесь не используются. `./*` обычно раскрывается для всех записей в `.`, кроме тех, которые начинаются с` .` (точка), хотя некоторые оболочки имеют возможность изменить это в `bash`` shopt -s dotglob`. OTOH `find .` всегда будет смотреть на имена точек как` ./. Profile` (кроме `.` и` ..`), хотя здесь любое такое имя не соответствует `-iname`. dave_thompson_085 8 лет назад 0

1 ответ на вопрос

2
Gabe

If the name is already in lower case, it will be given to the mv command twice, as you've seen.

If the last argument to mv is a directory, it will be treated as the destination dir into which all the other named entities should be moved.

mv test test will first check the last argument, whether it exists and, if so, if it's a directory. In this case it is, so mv will check whether it's the same mount point (if it isn't, i.e. the destination is a different device, it will need to copy the file then remove the original); in this case, it is. So mv constructs a sequence of rename(2) syscalls, appending all source names in turn to the directory specified as the destination: mv a b c d/ would generate rename("a", "d/a"), rename("b", "d/b"), and rename("c", "d/c") - so of course mv test test will try to call rename("test", "test/test");. Which is obviously an error, you can't move a directory inside itself.

To fix your problem, make the mv command conditional on the new name being different and not already existing (if you have two different files called "Test" and "test", you probably don't want to replace the second without asking). Without me having tested this, and typing it on a phone, it'll look something like this: new="$(echo "$old" | tr ...)"; [[ $new != $old && ! -e $new ]] && mv "$old" "$new" (watch the quotes, you don't want spaces in names to give you metaphorical wedgies).

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