Linux, переименуйте файлы с ошибкой bash "нет такого файла или каталога"
Я пытаюсь добавить префикс для всех файлов, которые не являются .sh
файлы в текущем каталоге и все подкаталоги, с bash
с помощью find
,
Проблема в том, что я получаю ошибку:
mv: cannot move './test.txt' to 'PRE_./test.txt': No such file or directory
Мой однострочник это:
find -type f ! -name "*.sh" -exec sh -c 'mv "{}" PRE_"{}"' _ {} \;
Я старался xargs
с rename
, тоже:
find -type f ! -name "*.sh" -print0 | xargs -0 rename 's/(.*)$/PRE_$1/'
Но я получил ту же ошибку. В чем дело? Заранее спасибо!
5 ответов
find -type f ! -name "*.sh" -exec rename -n 's|.*/\K|PRE_|' {} +
.*/
matches upto last/
в имени файла- используя
\K
lookbehind, we can avoid need of capture group in this case
Удалить -n
вариант из rename
once it shows correct renaming
Если вы не хотите изменять файлы в подкаталогах, вы можете использовать это:
find -type f ! -name "*.sh" -printf "%f\n" | xargs -r rename 's/(.*)$/PRE_$1/'
проблема была./ перед именами файлов.
find
возвращает путь от .
вплоть до файла, поэтому каждый find
результат начинается с ./
когда вы начинаете поиск в текущем каталоге. В результате ваше новое имя файла PRE_./test.txt
, который test.txt
в каталоге под названием PRE_.
, Поскольку нет каталога под названием PRE_.
, mv
не может двигаться test.txt
в этот каталог, так что дает вам сообщение об ошибке, которое вы видели.
Если вы переименовываете файлы только в текущем каталоге, а не в подкаталогах, вы можете просто использовать bash:
shopt -s extglob
for f in !(*.sh) ; do [[ -d "$f" ]] || mv "$f" "PRE_$f" ; done
Благодаря этому ответу за !(pattern)
построить в bash, доступно, когда extglob
включен. Возвращает все имена файлов не совпадают pattern
,
Для файлов во всех подкаталогах тоже используйте:
shopt -s extglob globstar
for f in **/!(*.sh) ; do [[ -d "$f" ]] || mv "$f" "${f%/*}/PRE_${f##*/}" ; done
Благодаря этому ответу для Globstar. %
а также ##
измельчить компоненты пути и наклеить PRE_
в нужное место, в начале имени файла.
Редактировать [[ -d "$f" ]] ||
в обоих вышеперечисленных случаях петля не может переименовывать каталоги.
Во многих дистрибутивах Linux команда переименования по умолчанию недоступна.
Если в вашей системе отсутствует команда переименования, установите ее с помощью:
Для Ubuntu и Debian используйте
sudo apt install rename
Для CentOS и Fedora используйте
sudo yum install prename
Это хорошая практика, чтобы не использовать {}
прямо в аргументе sh -c
, но передать его значение в качестве аргумента. Затем вы можете использовать операции расширения параметра, как обычно, чтобы изменить его значение. Здесь мы разделим аргумент на его компоненты каталогов и имен файлов, а затем соберем их вместе с префиксом имени файла PRE_
использовать в качестве аргумента mv
,
script='
d=${1%/*}
f=${1#$d}
mv "$1" "$d/PRE_$f"
'
find -type f ! -name "*.sh" -exec sh -c "$script" _ {} \;
(Это лишь небольшое изменение по сравнению с вашей первоначальной попыткой; вы проходили мимо {}
в качестве аргумента, но не используя его. Вместо этого вы "встраивали" значение непосредственно в строку, переданную sh
.)
Вы также можете использовать -exec +
Форма для минимизации количества звонков на sh
, передавая несколько имен файлов и перебирая их в оболочке. (Это начинает сходиться с чистым bash
решение, предложенное @cxw, но поддерживающее POSIX, а также bash
3.x, совместимость):
script='
for arg;
d=${arg%/*}
f=${arg#$d}
mv "$1" "$d/PRE_$f"
done
'
find -type f ! -name "*.sh" -exec sh -c "$script" _ {} +