bash로 많은 수의 이미지 파일 이름 바꾸기


16

대략 이름을 바꿔야합니다. 70,000 개 파일 예를 들면 :에서 sb_606_HBO_DPM_0089000sb_606_dpm_0089000

숫자 범위는에서 0089000로 이동 합니다 0163022. 이름의 첫 부분 만 변경하면됩니다. 모든 파일은 단일 디렉토리에 있으며 순차적으로 번호가 매겨집니다 (이미지 시퀀스). 숫자는 변경되지 않아야합니다.

내가 이것을 bash에서 시도하면 '인수 목록이 너무 길다'는 소리가 난다.

편집하다:

먼저 단일 파일의 이름을 바꾸려고했습니다 mv.

mv sb_606_HBO_DPM_0089000.dpx sb_606_dpm_0089000.dpx

그런 다음 범위의 이름을 바꾸려고했습니다 (지난 주에 파일로드를 이동하는 방법을 배웠으므로 동일한 이름의 구문을 사용하여 파일 이름을 바꿀 수 있다고 생각했습니다 ...). 나는 다음과 같은 것을 시도 했다고 생각한다 .

mv sb_606_HBO_DPM_0{089000..163023}.dpx sb_606_dpm_0{089000..163023}.dpx

4
리뷰어들에게 : 나는 이것이 중복이라고 생각하지 않습니다. 다른 질문에 대한 대부분의 CLI 답변은 많은 수의 파일이 셸 ARG_MAX제한 과 충돌하기 때문에 작동하지 않습니다 . 이 질문은 명령 행 솔루션을 명시 적으로 요청하므로 다른 질문과 마찬가지로 GUI 솔루션도 일치하지 않을 수 있습니다.
디저트

1
파일 이름을 바꾸는 것에 대해 두 가지 이상의 질문이 있으면 괜찮습니다. 실제로 대답하지 않는 일반 자원에 대한 구체적인 질문을 닫지 마십시오 ...
Zanna

1
@rich 시도한 명령을 명시 적으로 편집 할 수 있다면 이것이 속임수가 아님이 더 분명합니다. (이것은 당신이이 접근법을 알고 있음을 보여줍니다.) 건배.
Sparhawk

2
부자, 당신의 질문은 특정 질문이기 때문에 속이 아닙니다. 그것에 대해 걱정하지 마십시오. 더 중요한 것은, 질문에 다수의 찬란한 답변이 접수 된 후에는 편집 한 내용이 기존 답변의 유효성을 떨어 뜨릴 수 있으므로 편집하는 것은 좋은 생각이 아닙니다. 이제 내 대답이 왜 mv {1..2} {3..4}작동하지 않는지 설명해야한다고 생각 합니다. 이것은 전혀 다른 문제입니다 ARG_MAX... 대답 한 다른 사람은 아마 같은 느낌을 줄 것입니다! 내 관점에서, 나는 당신이 마지막 편집을 롤백하기를 원하며, 원한다면 mv범위에 대한 것에 대해 완전히 새로운 질문을 할 것입니다.
Zanna

1
@Sparhawk OP는 문제의 첫 번째 버전에서 문제가 argument list too long오류 라는 것을 명확하게 썼습니다 . 더 명확하게 할 필요는 없습니다. ARG_MAX를 다루기위한 해결 방법이 필요하고 제안 된 중복에 대한 답변이 그렇게하지 않기 때문에 이것은 분명히 속지 않습니다.
terdon

답변:


25

한 가지 방법은 사용하는 것입니다 find함께 -exec하고 +옵션을 선택합니다. 이것은 인수 목록을 구성하지만, 최대 인수 목록을 초과하지 않고 모든 파일에서 작동하는 데 필요한만큼의 호출로 목록을 나눕니다. 모든 인수가 동일하게 취급 될 때 적합합니다. 이것은의 경우 rename로 생각하지 mv.

Perl 이름 바꾸기를 설치해야 할 수도 있습니다.

sudo apt install rename

그런 다음 예를 들어 사용할 수 있습니다.

find . -maxdepth 1 -exec rename -n 's/_HBO_DPM_/_dpm_/' {} +

-n실제로 파일 이름을 바꾸려면 테스트 후 제거하십시오 .


11

세 가지 대안을 제안하겠습니다. 각각은 간단한 한 줄 명령이지만, 처리 할 파일이 동일한 직접 경로의 다른 파일과 혼합되는 경우를 대비하여보다 복잡한 경우에 대한 변형을 제공합니다.

mmv

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

mmv '*HBO_DPM*' '#1dpm#2'

인수는 문자열로 전달되므로 셸에서 glob 확장이 발생하지 않습니다. 이 명령은 정확히 두 개의 인수를받은 다음 파일 수에 대한 제한없이 내부에서 해당 파일을 찾습니다. 또한 위의 명령은 첫 번째 glob과 일치하는 모든 파일의 이름이 바뀌어야한다고 가정합니다. 물론보다 구체적으로 설명 할 수 있습니다.

mmv 'sb_606_HBO_DPM_*' 'sb_606_dpm_#1'

동일한 디렉토리에 요청 된 번호 범위를 벗어난 파일이있는 경우이 답변에서 더 아래에 주어진 루프 오버 번호를 사용하는 것이 좋습니다. 그러나 적절한 패턴으로 일련의 mmv 호출을 사용할 수도 있습니다.

mmv 'sb_606_HBO_DPM_0089*'       'sb_606_dpm_0089#1'    # 0089000-0089999
mmv 'sb_606_HBO_DPM_009*'        'sb_606_dpm_009#1'     # 0090000-0099999
mmv 'sb_606_HBO_DPM_01[0-5]*'    'sb_606_dpm_01#1#2'    # 0100000-0159999
mmv 'sb_606_HBO_DPM_016[0-2]*'   'sb_606_dpm_016#1#2'   # 0160000-0162999
mmv 'sb_606_HBO_DPM_01630[01]?'  'sb_606_dpm_01630#1#2' # 0163000-0163019
mmv 'sb_606_HBO_DPM_016302[0-2]' 'sb_606_dpm_016302#1'  # 0163020-0163022

숫자를 반복하다

설치를 피하거나이 범위 밖의 일치를 피하면서 숫자 범위로 선택해야하고 74,023 명령 호출을 기다릴 준비가 되었다면 일반 bash 루프를 사용할 수 있습니다.

for i in {0089000..0163022}; do mv sb_606_HBO_DPM_$i sb_606_dpm_$i; done

시퀀스에 간격이 없기 때문에 이것은 특히 잘 작동합니다. 그렇지 않으면 소스 파일이 실제로 존재하는지 확인할 수 있습니다.

for i in {0089000..0163022}; do
  test -e sb_606_HBO_DPM_$i && mv sb_606_HBO_DPM_$i sb_606_dpm_$i
done

for ((i=89000; i<=163022; ++i))괄호 확장 과 달리 몇 년 전에 일부 Bash 릴리스 이후 선행 0을 처리합니다. 실제로 변경을 요청 했으므로 유스 케이스를 보게되어 기쁩니다.

추가 자료 : Bash 정보 페이지의 브레이스 확장 , 특히 관련 부분 {x..y[..incr]}.

파일을 반복

또 다른 옵션은 해당 정수 범위를 반복하는 대신 적절한 glob를 반복하는 것입니다. 이 같은:

for i in *HBO_DPM*; do mv "$i" "${i/HBO_DPM/dpm}"; done

이 역시 mv파일 당 한 번의 호출입니다. 그리고 루프는 요소의 긴 목록에 있지만 전체 목록은 하위 프로세스에 인수로 전달되지 않지만 bash에 의해 내부적으로 처리되므로 제한이 문제를 일으키지 않습니다.

추가 정보 : Bash 정보 페이지의 쉘 매개 변수 확장${parameter/pattern/string} .

숫자 범위를 제공 한 숫자 범위로 제한하려면 해당 검사를 추가 할 수 있습니다.

for i in sb_606_HBO_DPM_+([0-9]); do
  if [[ "${i##*_*(0)}" -ge 89000 ]] && [[ "${i##*_*(0)}" -le 163022 ]]; then
    mv "$i" "${i/HBO_DPM/dpm}"
  fi
done

여기에 ${i##pattern}가장 긴 접두사 일치 제거 pattern에서를 $i. 가장 긴 접두사는 무엇이든, 밑줄, 0 또는 그 이상의 0으로 정의됩니다. 후자는 설정 되는 옵션 에 따라 *(0)확장 된 glob 패턴 으로 작성됩니다 . 앞의 0을 제거하는 것은 숫자를 밑이 8이 아닌 밑이 10으로 취급하는 데 중요합니다. in 루프 인수는 파일이 동일하게 시작하지만 끝나지 않는 파일이있는 경우를 대비하여 하나 이상의 숫자와 일치하는 또 다른 확장 된 glob입니다. 번호.extglob+([0-9])


감사합니다! 이것은 꿈처럼 작동했습니다. i의 경우 {0089000..0163022}; mv sb_606_HBO_DPM_ $ i sb_606_dpm_ $ i; 완료-파일 이름 확장자를 추가해야 작동하지만 원하는대로하고 구문을 이해합니다. 감사합니다 @MvG
rich

@rich : 기꺼이 도와 드리겠습니다 – 당신과 희망찬 미래의 방문객들. 가장 유용한 답 을 받아들이 는 것을 잊지 마십시오 . 나중에 더 좋은 것이 나오면 언제든지 해당 확인 표시를 변경할 수 있습니다.
MvG

10

ARG_MAX한계를 극복 하는 한 가지 방법 은 bash 쉘의 내장을 사용하는 것입니다 printf.

printf '%s\0' sb_* | xargs -0 rename -n 's/HBO_DPM/dpm/'

전의.

rename -n 's/HBO_DPM/dpm/' sb_*
bash: /usr/bin/rename: Argument list too long

그러나

printf '%s\0' sb_* | xargs -0 rename -n 's/HBO_DPM/dpm/'
rename(sb_606_HBO_DPM_0089000, sb_606_dpm_0089000)
.
.
.
rename(sb_606_HBO_DPM_0163022, sb_606_dpm_0163022)

7
find . -type f -exec bash -c 'echo $1 ${1/HBO_DPM/dpm}' _ {} \;
./sb_606_HBO_DPM_0089000 ./sb_606_dpm_0089000

find.모든 파일의 현재 디렉토리 에 있고 파일 을 하나씩 바꾸면서 -type f찾은 파일의 이름을 바꾸십시오.$1HBO_DPMdmp -exec ... \;

교체 echomv이름 변경을 수행 할 수 있습니다.


6

다음과 같은 작은 파이썬 스크립트를 작성할 수 있습니다.

import os
for file in os.listdir("."):
    os.rename(file, file.replace("HBO_DPM", "dpm"))

파일이 들어 rename.py있는 폴더에서 와 같이 텍스트 파일로 저장 한 다음 해당 폴더의 터미널로 이동하십시오.

python rename.py

6

파일별로 파일을 만들 수 있습니다 (시간이 걸릴 수 있음)

sudo apt install util-linux  # if you don't have it already
for i in *; do rename.ul HBO_DPM dpm "$i"; done

rename다른 답변에 사용 된 Perl과 마찬가지로 rename.ul옵션 -n또는 --no-act테스트가 있습니다.


Zanna의 답변에 대한 귀하의 의견을 편집했습니다. Zanna의 답변을 수정하거나 의견을 남겨주십시오.
fosslinux

내 대답에 대한 의견이 아닌 @ubashu- -n테스트에 사용 된 플래그를 참조하여 사용할 수 있음을 제안했습니다 rename.ul.
Zanna

3

나는 나의 가장 친한 친구 sed를 파티에 초대 한 사람이 없다는 것을 안다 . :). 다음 for루프는 목표를 달성합니다.

for i in sb_606_HBO_DPM*; do
  mv "$i" "$(echo $i | sed 's/HBO_DPM/dpm/')";
done

이러한 작업을위한 많은 도구가 있으므로 가장 이해하기 쉬운 도구를 선택하십시오. 이것은 간단하고 쉽게이 또는 다른 목적에 맞게 변경됩니다 ...


이 특정 경우에는별로 관련이 없지만 파일 이름에 줄 바꿈이 포함되어 있으면 실패합니다. 다른 답변이 강력하고 임의의 파일 이름을 처리하거나 OP의 파일 이름 지정 체계 에서만 작동 하기 때문에 이것을 언급합니다 .
terdon

... 줄 바꿈, 공백, 와일드 카드 등 ... $i명령 대체에서 인용 하면 일부를 피할 수 있지만 파일 이름에서 후행 줄 바꿈을 처리하는 쉬운 방법은 없습니다.
muru

3

옵션을 제공하고 있으므로 Perl 방식이 있습니다. cd대상 디렉토리에 넣고 다음을 실행하십시오.

perl -e 'foreach(glob("sb_*")){rename $_, s/_HBO_DPM_/_dpm_/r}'

설명

  • perl -e:에서 제공 한 스크립트를 실행하십시오 -e.
  • foreach(glob){}: { }glob의 각 결과에있는 것을 실행 하십시오.
  • glob("sb_*"): 이름이 쉘 glob와 일치하는 현재 디렉토리의 모든 파일 및 디렉토리 목록을 리턴합니다 sb*.
  • rename $_, s/_HBO_DPM_/_dpm_/r: 펄 매직. $_는 반복하는 각 요소를 보유하는 특수 변수입니다 (에서 foreach). 여기에 각 파일이 있습니다. s/_HBO_DPM_/_dpm_/의 첫 번째 항목을 _HBO_DPM_로 바꿉니다 _dpm_. 그것은에 실행 $_은 각 파일 이름에서 실행하므로, 기본적으로. /r수단 "대상 문자열 (파일 이름)의 사본이 교체를 적용하고 수정 된 문자열을 반환합니다. rename당신이 기대하는 것을 수행 :이 파일 이름을 변경 모든 것이 현재 파일 이름 (이름을 변경합니다 그래서. $_자체에) _HBO_DPM_로 교체되었습니다 _dpm_.

확장되고 읽기 쉬운 스크립트와 같은 내용을 작성할 수 있습니다.

#! /usr/bin/env perl
use strict;
use warnings;

foreach my $fileName (glob("sb_*")){
  ## Copy the name to a new variable
  my $newName = $fileName;
  ## change the copy. $newName is now the changed version
  $newName =~ s/_HBO_DPM_/_dpm_/;
  ## rename
  rename $fileName, $newName;
}

1

구상중인 이름 변경의 종류에 따라 여러 줄 편집과 함께 vidir 을 사용하는 것이 좋습니다 .
특별한 경우 텍스트 편집기에서 모든 줄을 선택하고 몇 번의 키 입력으로 파일 이름 의 _ " HBO" 부분을 제거 할 수 있습니다.


예, vi는 훌륭한 찾기 및 바꾸기 기능을 제공합니다.
Jasen

2
답변을 연장하고 OP의 목표를 달성하는 방법에 대한 예를 들어 주 vidir시겠습니까?
디저트
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.