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" _ {} +
Другие вопросы по тегам