명령 줄에서 여러 파일의 확장자를 재귀 적으로 변경하려면 어떻게합니까?


답변:


85

이식 가능한 방법 (POSIX 호환 시스템에서 작동) :

find /the/path -depth -name "*.abc" -exec sh -c 'mv "$1" "${1%.abc}.edefg"' _ {} \;

bash4에서 globstar를 사용하여 재귀 글로브를 얻을 수 있습니다 (**).

shopt -s globstar
for file in /the/path/**/*.abc; do
  mv "$file" "${file%.abc}.edefg"
done

renameUbuntu 의 (perl) 명령은 perl 정규식 구문을 사용하여 파일 이름을 바꿀 수 있으며, globstar 또는 find:

# Using globstar
shopt -s globstar
files=(/the/path/**/*.abc)  

# Best to process the files in chunks to avoid exceeding the maximum argument 
# length. 100 at a time is probably good enough. 
# See http://mywiki.wooledge.org/BashFAQ/095
for ((i = 0; i < ${#files[@]}; i += 100)); do
  rename 's/\.abc$/.edefg/' "${files[@]:i:100}"
done

# Using find:
find /the/path -depth -name "*.abc" -exec rename 's/\.abc$/.edefg/' {} +

http://mywiki.wooledge.org/BashFAQ/030 참조


첫 번째 확장은 단순히 기존 확장에 새 확장을 추가하지만 대체하지는 않습니다.
user2757729

3
@ user2757729를 대체합니다. 끝에 없는 "${1%.abc}"경우를 $1제외하고 값으로 확장됩니다 .abc. 쉘 문자열 조작에 대한 자세한 내용은 FAQ 100 을 참조하십시오 .
geirha

나는 ~ 70k 파일 파일 구조에서 이것을 실행했습니다 ... 적어도 내 쉘에서는 그것을 대체하지 않았습니다.
user2757729

1
@ user2757729 아마 "abc"를${1%.abc}...
kvnn

1
다른 사람이 첫 번째 명령에서 밑줄이 무엇인지 궁금해하는 경우 sh -c 첫 번째 인수는 $ 0에 할당되고 나머지 인수는 위치 매개 변수에 할당되므로 필요합니다 . 따라서는 _더미 인수이고 '{}'은의 첫 번째 위치 인수 sh -c입니다.
hellobenallan

48

모든 파일이 같은 폴더에 있으면 필요한 작업을 수행합니다.

rename 's/.abc$/.edefg/' *.abc

파일의 이름을 재귀 적으로 바꾸려면 다음을 사용하십시오.

find /path/to/root/folder -type f -name '*.abc' -print0 | xargs -0 rename 's/.abc$/.edefg/'

8
또는 rename 's/.abc$/.edefg/' /path/to/root/folder/**/*.abcBash의 최신 버전에서.
Adam Byrtek

폴더에서 * .abc를 재귀 적으로 사용하는 방법에 대한 팁을 주신 Adam에게 감사합니다!
Rafał Cieślak

좋은 팁, 감사합니다! 작은 정규 표현식 구문에 대한 추가 문서는 어디서 찾을 수 있습니까? 무엇처럼 s처음에, 그리고 다른 어떤 옵션 것은 내가 거기에 사용할 수 있습니다. 감사 !
Anto

@AdamByrtek 방금 Utopic에 대한 제안으로 실패했습니다. ( '그러한 파일이 없음'등)
Jonathan Y.

14

재귀 이름 바꾸기의 한 가지 문제는 파일을 찾는 데 사용하는 모든 방법이 rename파일 이름뿐만 아니라 전체 경로를에 전달한다는 것 입니다. 따라서 중첩 된 폴더에서 복잡한 이름 바꾸기를 수행하기가 어렵습니다.

의 문제를 해결하기 위해 find-execdir조치를 사용 합니다. -execdir대신에 사용 -exec하면 지정된 명령이 일치하는 파일을 포함하는 서브 디렉토리에서 실행됩니다. 따라서 전체 경로를로 전달하는 대신 rename로만 전달 ./filename됩니다. 따라서 정규식을 훨씬 쉽게 작성할 수 있습니다.

find /the/path -type f \
               -name '*.abc' \
               -execdir rename 's/\.\/(.+)\.abc$/version1_$1.abc/' '{}' \;

상세히:

  • -type f 디렉토리가 아닌 파일 만 찾습니다.
  • -name '*.abc' .abc로 끝나는 파일 이름과 만 일치합니다.
  • '{}'-execdir찾은 경로를 삽입 할 위치를 표시하는 자리 표시 자입니다 . 작은 따옴표는 공백과 셸 문자로 파일 이름을 처리 할 수 ​​있도록해야합니다.
  • 백 슬래시는 후 -type-namebash는 줄 연속 문자입니다. 이 예제를 더 읽기 쉽게 만들기 위해 사용하지만 명령을 한 줄에 모두 넣는 경우에는 필요하지 않습니다.
  • 그러나 -execdir줄 끝에 백 슬래시 가 필요합니다. 세미콜론을 이스케이프하면에 의해 실행되는 명령이 종료됩니다 -execdir. 장난!

정규식에 대한 설명 :

  • s/ 정규식의 시작
  • \.\/-execdir이 전달하는 선행 ./와 일치합니다. \를 사용하여. 및 / 메타 문자 (참고 :이 부분은 버전에 따라 다릅니다 find. @apollo 사용자의 의견 참조)
  • (.+)파일 이름과 일치하십시오. 괄호는 나중에 사용하기 위해 일치를 캡처합니다.
  • \.abc 점을 탈출, abc와 일치
  • $ 문자열의 끝에 일치를 고정

  • / 정규식의 "일치"부분의 끝과 "대체"부분의 시작을 표시합니다.

  • version1_ 이 텍스트를 모든 파일 이름에 추가

  • $1괄호로 캡처 한 기존 파일 이름을 참조합니다. "일치"부분에 여러 괄호 세트를 사용하는 경우 여기에서 $ 2, $ 3 등을 사용하여 괄호를 참조 할 수 있습니다.
  • .abc새 파일 이름은 .abc로 끝납니다. "바꾸기"섹션에서 도트 메타 문자를 이스케이프 할 필요가 없습니다.
  • / 정규식의 끝

전에

tree --charset=ascii

|-- a_file.abc
|-- Another.abc
|-- Not_this.def
`-- dir1
    `-- nested_file.abc

tree --charset=ascii

|-- version1_a_file.abc
|-- version1_Another.abc
|-- Not_this.def
`-- dir1
    `-- version1_nested_file.abc

힌트 : rename의 -n 옵션이 유용합니다. 드라이 런을 수행하고 변경 될 이름을 표시하지만 변경하지는 않습니다.


자세한 설명에 감사하지만 이상하게도 시도 할 때 --execdir선행을 통과하지 못했기 ./때문에 \.\/정규 표현식 의 일부를 생략 할 수 있습니다. 내가 OSX하지 우분투이기 때문에 월 즉
아폴로

5

또 다른 휴대용 방법 :

find /the/path -depth -type f -name "*.abc" -exec sh -c 'mv -- "$1" "$(dirname "$1")/$(basename "$1" .abc).edefg"' _ '{}' \;

4
Ask Ubuntu에 오신 것을 환영합니다! 이것은 귀중한 답변이지만 편집을 통해 확장하여 해당 명령의 작동 방식과 이유 를 설명 하는 것이 좋습니다 .
Eliah Kagan


0

이것이 내가하고 원하는 방식으로 일한 것입니다. 나는 mv명령을 사용했다 . .3gp파일 이 여러 개 있는데 모두 파일 이름을 바꾸고 싶었습니다..mp4

여기에 짧은 oneliner가 있습니다.

for i in *.3gp; do mv -- "$i" "ren-$i.mp4"; done

단순히 현재 디렉토리를 스캔하고 모든 .3gp 파일을 가져온 다음 mv를 사용하여 이름을 바꿉니다. ren-name_of_file.mp4


그 이름을 바꿉니다 file.3gpfile.3gp.mp4대부분의 사람들이 원하는 것을하지 않을 수있는.
muru

@muru 그러나 원칙은 여전히 ​​존재합니다. 간단하게 선택할 수 있는 다음 그들에게 이름을 변경하려면 대상 확장자를 지정 확장. .3gp또는 .mp4여기에는 설명을 위해 단지 있었다.
KhoPhi

구문은 바람직하고, 한 줄짜리이며 이해하기 쉽습니다. 그러나 basename "$i" .mp4"ren- $ i.mp4"대신 이전 확장을 제거하는 데 사용 합니다.
TaoPR

참된. 좋은 지적.
KhoPhi

0

나는 이것을 달성하는 쉬운 방법을 찾았다. 많은 파일의 확장자를 jpg에서 pdf로 변경하려면 다음을 사용하십시오.

for file in /path/to; do mv $file $(basename -s jpg $file)pdf ; done

0

동일한 이름패키지 에서 mmv 명령 을 사용합니다 .

mmv ';*.abc' '#1#2.edefg'

;경기는 0 개 이상의 */과에 해당 #1교체에. 에 *해당합니다 #2. 비 재귀 버전은

mmv '*.abc' '#1.edefg'

0

파일 및 디렉토리 이름 변경 find -execdir | rename

단순히 접미사가 아닌 파일과 디렉토리의 이름을 바꾸려면 좋은 패턴입니다.

PATH="$(echo "$PATH" | sed -E 's/(^|:)[^\/][^:]*//g')" \
  find . -depth -execdir rename 's/findme/replaceme/' '{}' \;

awesome -execdir옵션 은와 달리 명령을 cd실행하기 전에 디렉토리를 수행합니다 .rename-exec

-depth 부모 디렉토리가 누락 될 때 발생할 수있는 잠재적 인 문제를 방지하기 위해 이름 변경은 먼저 자식에서, 그런 다음 부모에서 일어나야합니다.

-execdir 이름이 아닌 이름이 아닌 입력 경로에서 이름이 제대로 재생되지 않기 때문에 필수입니다 (예 : 다음 실패).

rename 's/findme/replaceme/g' acc/acc

PATH때문에 해킹이 필요 -execdir하나 매우 짜증나는 단점이 find매우 독단적를하고 아무것도 할 거부 -execdir당신이 당신의 상대 경로가있는 경우 PATH예를 들어 환경 변수 ./node_modules/.bin와 실패를 :

find : 상대 경로 './node_modules/.bin'이 PATH 환경 변수에 포함되어 있으며 find의 -execdir 조치와 함께 사용하면 안전하지 않습니다. $ PATH에서 해당 항목을 제거하십시오

PATH에있는 디렉토리에 대해 '-execdir'조치를 사용하는 것이 안전하지 않은 이유무엇입니까?

-execdirPOSIX에 대한 GNU 찾기 확장 입니다. renamePerl 기반이며 rename패키지 에서 제공 됩니다. 우분투에서 테스트 18.10.

미리보기 대안 이름 바꾸기

입력 경로가 출신이 아니 find거나 상대 경로 성가심이 충분하면 Perl lookahead를 사용하여 다음과 같이 디렉토리 이름을 안전하게 바꿀 수 있습니다.

git ls-files | sort -r | xargs rename 's/findme(?!.*\/)\/?$/replaceme/g' '{}'

나는을 위해 편리한 아날로그를 발견하지 않았습니다 -execdirxargs: https://superuser.com/questions/893890/xargs-change-working-directory-to-file-path-before-executing/915686

sort -r더 이상 경로가 동일한 접두사와 짧은 사람 한 후 온 이후 파일이 각 디렉토리 이후에 올 수 있도록해야합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.