찾기를 사용하여 여러 파일의 이름을 바꾸는 방법


37

명령 find명령을 사용하여 여러 파일 (file1 ... filen을 file1_renamed ... filen_renamed)의 이름을 바꾸고 싶습니다 .

find . -type f -name 'file*' -exec mv filename='{}' $(basename $filename)_renamed ';'

그러나이 오류가 발생합니다.

mv: cannot stat filename=./file1’: No such file or directory

filename이 쉘 변수로 해석되지 않기 때문에 작동하지 않습니다.

답변:


46

다음은 접근 방식을 직접 수정 한 것입니다.

find . -type f -name 'file*' -exec sh -c 'x="{}"; mv "$x" "${x}_renamed"' \;

그러나 일치하는 파일이 많은 경우 mv각 일치 항목에 대해 새 셸 (을 실행 )을 시작하므로 비용이 많이 듭니다 . 파일 이름에 재미있는 문자가 있으면 폭발합니다. 보다 효율적이고 안전한 접근 방식은 다음과 같습니다.

find . -type f -name 'file*' -print0 | xargs --null -I{} mv {} {}_renamed

또한 이상한 이름의 파일로 작업하는 이점도 있습니다. 경우 find지원을, 이것은 줄일 수있다

find . -type f -name 'file*' -exec mv {} {}_renamed \;

xargs사용하지 않을 경우 버전에 유용 {}같이,

find .... -print0 | xargs --null rm

여기 rm에서 한 번 (또는 여러 파일로 여러 번) 호출되지만 모든 파일에 대해 호출되는 것은 아닙니다.

나는 제거 basename아마 잘못 때문에 당신의이 질문 : 당신은 이동 것 foo/bar/file8까지 file8_renamed하지 foo/bar/file8_renamed.

편집 (의견에서 제안한대로) :

  • find없이 단축 추가xargs
  • 보안 스티커 추가

x쓸모없는 경우 : find . -type f -name 'file*' -exec mv {} "{}_renamed" \; xargs버전은 첫 번째 예제와 같은 효율을 갖습니다
Costas

이는 asker의 접근 방식 x직접 수정하기위한 것입니다.
Thomas Erker

2
(1) {}쉘 ( sh -c "…") 명령 에서 직접 사용하는 것은 매우 위험 합니다. 항상 인수로 전달해야합니다. (2) 모든 버전의 구문 이 find지원 되지는 않습니다 {}_renamed. (3) xargs파일 이름을 바꾸는 것과 달리 파일을 제거하는 데 유용한 진술을 이해하지 못합니다 .
G-Man, 'Reinstate

@ G-Man :와의 차이점 xargsmvvs. 가 아니라 vs.의 rm사용입니다 {}. 전자는 mv file1 file1_renamed; mv file2 file2_renamed후자 와 비슷하다 rm file1 file2.
Thomas Erker

1
그런 다음 _renamed 파일을 원래 이름으로 다시 변경하려면 어떻게해야합니까?
mcmillab

17

첫 번째 답변을 시도 하고 조금 놀아 후에 나는 -execdir다음을 사용하여 약간 짧고 덜 복잡하게 할 수 있음을 발견했습니다 .

find . -type f -name 'file*' -execdir mv {} {}_renamed ';'

필요한 것을 정확하게 수행 해야하는 것처럼 보입니다.


2
find구현 지원이 -execdir{}전체가 아닌, 그것은 또한 가장 안전합니다. 당신은 추가 할 수 있습니다 -imv불구하고 (그리고 -T만약 당신의 mv지원을)
스테판 Chazelas가

1
@ StéphaneChazelas mv는 프롬프트 에 의존하는 대신에 더 나아지 거나 그에 더하여, find지원 구현 여부에 따라 의심의 여지없이 -okdir명령을 실행하기 전에 실행할 명령을 출력 할 수 있습니다.
Matijs

-depth디렉토리 이름을 터치하려는 경우에도 언급 할 가치 가 있습니다.
Ciro Santilli 新疆 改造 中心 法轮功 六四 事件

Argh -execdir는 매우 성가신 단점이있는 것을 발견했습니다. 상대 경로 findPATH포함되어 있으면 아무것도하지 않습니다 ... askubuntu.com/questions/621132/… find: The relative path XXX is included in the PATH environment
Ciro Santilli 新疆 改造 中心 法轮功 六四 事件

6

또 다른 방법은 while read루프 오버 find출력 을 사용하는 것 입니다. 이를 통해 각 파일 이름에 변수로 액세스 sh -c하여 find-exec옵션을 사용하여 별도의 프로세스를 생성하는 추가 비용 / 잠재적 인 보안 문제에 대해 걱정할 필요가 없습니다 .

find . -type f -name 'file*' |
    while IFS= read file_name; do
        mv "$file_name" "${file_name##*\/}_renamed"
    done

그리고 사용중인 쉘이 구분 기호 -d를 지정하는 옵션을 read지원하는 경우 다음을 사용하여 이상한 이름의 파일 (예 : 줄 바꿈)을 지원할 수 있습니다.

find . -type f -name 'file*' -print0 |
    while IFS= read -d '' file_name; do
        mv "$file_name" "${file_name##*\/}_renamed"
    done

3

첫 번째 답변 을 확장 하고 ./경로 접두사가 파일 이름 인수에 있기 때문에 파일 이름에 추가 할 수는 없습니다 .

Thomas Erker의 답변을 수정하면 더 일반적인 접근 방식입니다.

find . -name PATTERN -printf "%f\0" | xargs --null -I{} mv {} "prefix {} suffix"

xargs 옵션 :

--null전달 된 각 인수 stdin가 널 문자 ( \0)로 끝나는 것을 나타냅니다 . 이런 식으로 파일 이름에 공백이 포함될 수 있습니다. 그렇지 않으면 각 단어가 mv명령 의 다른 매개 변수로 위협됩니다 .

-I replace-str모든 발생은 replace-str에서 읽은 인수로 대체됩니다 stdin. 따라서 필요한 경우 다른 문자열로 변경할 수 있습니다.


pringf "%f\0"대신에 print0?
slm

1
@sim 은 원래 이름을 접두사로 바꿀 때 셸 메타 문자가 포함되어 있으면 접두사 -print0를 생성 하기 때문 입니다 . (예를 바꾸기 에 , 발 등)./PATTERN0 - foo.txt00 - foo.txt1 - bar.txt01 - bar.txt
DAS-g

2

난과 비슷한 할 수 있었다 for, find등을 mv.

for i in $(find . -name 'config.yml'); do mv $i $i.bak; done

모든 config.yml파일을 찾아서 이름을 바꿉니다.config.yml.bak


이것은 가변 확장을 사용할 수 있다는 장점이 있습니다. $ {i : r}. 좋은 대답입니다.
살라미

그래, 당신은 거의 모든 표현을 넣고 for대량 작업을 수행 할 수 있습니다.
Varun Risbud
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.