Bash로 일괄 이름 바꾸기 파일


101

Bash는 버전 번호를 제거하기 위해 일련의 패키지 이름을 어떻게 바꿀 수 있습니까? 나는 모두 주위 놀겠다는 거 봤는데 expr%%아무 소용.

예 :

Xft2-2.1.13.pkg 된다 Xft2.pkg

jasper-1.900.1.pkg 된다 jasper.pkg

xorg-libXrandr-1.2.3.pkg 된다 xorg-libXrandr.pkg


1
한 번 쓰기를 많이 사용하는 스크립트로 이것을 정기적으로 사용하려고합니다. 내가 사용할 모든 시스템에는 bash가 있으므로 매우 편리한 바 시즘을 두려워하지 않습니다.
Jeremy L

더 일반적으로 참조 : stackoverflow.com/questions/28725333/…
tripleee

답변:


190

bash의 매개 변수 확장 기능을 사용할 수 있습니다.

for i in ./*.pkg ; do mv "$i" "${i/-[0-9.]*.pkg/.pkg}" ; done

공백이있는 파일 이름에는 따옴표가 필요합니다.


1
나도 좋아하지만 bash 특정 기능을 사용합니다. 일반적으로 "표준"sh를 선호하지 않습니다.
Diego Sevilla

2
일회용 한 줄 대화 형 재료의 경우 항상 sed-fu 대신 이것을 사용합니다. 대본이라면 bashisms를 피하십시오.
richq

코드에서 발견 한 한 가지 문제 : 파일의 이름이 이미 변경된 후 다시 실행하면 어떻게됩니까? 자체 하위 디렉토리로 이동하려고하면 경고가 표시됩니다.
Jeremy L

1
재실행 오류를 방지하려면 " .pkg"부분 에 동일한 패턴을 사용할 수 있습니다. 즉 "for i in *-[0-9.] .pkg; do mv $ i $ {i /-[0-9 .] *. pkg / .pkg}; 완료 ". 그러나 오류는 충분히 무해합니다 (동일한 파일로 이동).
richq 09-02-02

3
bash 솔루션은 아니지만 perl이 설치되어 있으면 일괄 이름 바꾸기를위한 편리한 이름 바꾸기 명령을 제공합니다. 간단히 수행하십시오.rename "s/-[0-9.]*//" *.pkg
Rufus

33

모든 파일이 동일한 디렉토리에 있으면 시퀀스

ls | 
sed -n 's/\(.*\)\(-[0-9.]*\.pkg\)/mv "\1\2" "\1.pkg"/p' | 
sh

당신의 일을 할 것입니다. 나오지 명령의 시퀀스를 생성합니다 MV , 명령하는 할 수 있습니다 후 껍질에 파이프. | sh명령이 원하는 작업을 수행하는지 확인 하려면 먼저 후행없이 파이프 라인을 실행하는 것이 가장 좋습니다 .

여러 디렉토리를 반복하려면 다음과 같이 사용하십시오.

find . -type f |
sed -n 's/\(.*\)\(-[0-9.]*\.pkg\)/mv "\1\2" "\1.pkg"/p' |
sh

에가 있습니다 나오지 순서를 그룹화 정규 표현식은 앞에 백 슬래시 브라켓입니다, \(그리고 \)오히려 하나의 브라켓보다, (하고 ).


내 순진함을 용서하십시오. 그러나 백 슬래시로 괄호를 이스케이프하면 문자 그대로 처리되지 않습니까?
devios1 2014 년

2
sed에서 정규식 그룹화 시퀀스는 (, 단일 대괄호가 아닙니다.
Diomidis Spinellis

나를 위해 작동하지 않았다, 당신의 도움을 받으면 기뻐할 것입니다 .... FROM 파일의 접미사가 TO 파일에 추가됩니다 : $ find. 유형 d | sed -n 's /(.*)\/(.*) anysoftkeyboard (. *) / mv "\ 1 \ / \ 2anysoftkeyboard \ 3" "\ 1 \ / \ 2effectedkeyboard \ 3"/ p'| sh >> >>>>> OUTPUT >>>> mv : ./base/src/main/java/com/anysoftkeyboard/base/dictionaries의 이름을 ./base/src/main/java/com/effectedkeyboard/base/dictionaries/dictionaries로 바꿉니다. : 그런 파일이 없습니다 또는 디렉토리
비탈리 폼은

괄호 안에 백 슬래시가 누락 된 것 같습니다.
Diomidis Spinellis

1
ls스크립트에서 사용하지 마십시오 . 첫 번째 다양성은 printf '%s\n' * | sed ...창의력 으로 얻을 수 있으며 약간의 창의력으로 제거 할 수도 sed있습니다. Bash 는 리터럴 쉘 메타 문자를 포함하는 파일 이름이있는 경우 유용 할 수 printf있는 '%q'형식 코드를 제공합니다 .
tripleee

10

다음과 같이 할 것입니다.

for file in *.pkg ; do
    mv $file $(echo $file | rev | cut -f2- -d- | rev).pkg
done

모든 파일이 현재 디렉토리에 있다고 가정합니다. 그렇지 않다면 위에서 Javier가 조언 한대로 find를 사용해보십시오.

편집 : 또한이 버전은 위의 다른 기능과 같이 bash 특정 기능을 사용하지 않으므로 더 많은 이식성을 얻을 수 있습니다.


그들은 모두 같은 디렉토리에 있습니다. rq의 대답에 대해 어떻게 생각하십니까?
Jeremy L

나는 bash 특정 기능을 사용하지 않고 대신 표준 sh를 사용하는 것을 좋아합니다.
Diego Sevilla

1
나는 이것이 차세대 크로스 플랫폼, 포터블 엔터프라이즈 애플리케이션을 작성하기위한 것이 아니라 빠른 이름 바꾸기를위한
것이라고 생각했다

1
하하, 충분히 공평하다 ... 나 같은 휴대성에 관심이있는 사람에게서 :)
Diego Sevilla

이것은 세 번째 예인 xorg-libXrandr (이름에 하이픈이 사용됨)을 설명하지 않습니다.
Jeremy L

5

다음은 현재 허용되는 답변 과 거의 동일한 POSIX 입니다. 이것은 ${variable/substring/replacement}Bourne 호환 쉘에서 사용할 수 있는 Bash 전용 매개 변수 확장을 교환합니다 .

for i in ./*.pkg; do
    mv "$i" "${i%-[0-9.]*.pkg}.pkg"
done

매개 변수 확장 ${variable%pattern}variable일치하는 접미사가 pattern제거 된 값을 생성합니다 . ( ${variable#pattern}접두사 제거 도 있습니다 .)

-[0-9.]*오해의 소지가 있지만 수락 된 답변에서 하위 패턴 을 유지했습니다 . 정규식이 아니라 glob 패턴입니다. 따라서 "대시 뒤에 0 개 이상의 숫자 또는 점이 표시됨"을 의미하지 않습니다. 대신, "대시, 숫자 또는 점, 그 뒤에 오는 것"을 의미합니다. "anything"은 가장 긴 것이 아니라 가능한 가장 짧은 일치입니다. (배쉬 제공 ##하고 %%오히려 짧은보다, 가장 긴 접두사 나 접미사를 트리밍.)


5

sed* nix에서 사용할 수 있다고 가정 할 수 있지만 sed -nmv 명령 생성을 지원할 수 있을지 확신 할 수 없습니다 . ( 참고 : GNU 만이 작업을 sed수행합니다.)

그럼에도 불구하고 bash 내장 및 sed, 우리는 이것을 수행하기 위해 쉘 함수를 빠르게 채울 수 있습니다.

sedrename() {
  if [ $# -gt 1 ]; then
    sed_pattern=$1
    shift
    for file in $(ls $@); do
      mv -v "$file" "$(sed $sed_pattern <<< $file)"
    done
  else
    echo "usage: $0 sed_pattern files..."
  fi
}

용법

sedrename 's|\(.*\)\(-[0-9.]*\.pkg\)|\1\2|' *.pkg

before:

./Xft2-2.1.13.pkg
./jasper-1.900.1.pkg
./xorg-libXrandr-1.2.3.pkg

after:

./Xft2.pkg
./jasper.pkg
./xorg-libXrandr.pkg

대상 폴더 만들기 :

mv은 (는) 대상 폴더를 자동으로 생성하지 않기 때문에 초기 버전의 sedrename.

상당히 작은 변경이므로 해당 기능을 포함하는 것이 좋습니다.

abspathbash에는이 빌드가 없기 때문에 유틸리티 함수 (또는 절대 경로)가 필요합니다.

abspath () { case "$1" in
               /*)printf "%s\n" "$1";;
               *)printf "%s\n" "$PWD/$1";;
             esac; }

일단 우리는 새 폴더 구조를 포함하는 sed / rename 패턴에 대한 대상 폴더를 생성 할 수 있습니다.

이렇게하면 대상 폴더의 이름을 알 수 있습니다. 이름을 바꿀 때 대상 파일 이름에 사용해야합니다.

# generate the rename target
target="$(sed $sed_pattern <<< $file)"

# Use absolute path of the rename target to make target folder structure
mkdir -p "$(dirname $(abspath $target))"

# finally move the file to the target name/folders
mv -v "$file" "$target"

다음은 전체 폴더 인식 스크립트입니다.

sedrename() {
  if [ $# -gt 1 ]; then
    sed_pattern=$1
    shift
    for file in $(ls $@); do
      target="$(sed $sed_pattern <<< $file)"
      mkdir -p "$(dirname $(abspath $target))"
      mv -v "$file" "$target"
    done
  else
    echo "usage: $0 sed_pattern files..."
  fi
}

물론 특정 대상 폴더가 없을 때도 여전히 작동합니다.

모든 노래를 폴더에 넣으려면 다음과 ./Beethoven/같이 할 수 있습니다.

용법

sedrename 's|Beethoven - |Beethoven/|g' *.mp3

before:

./Beethoven - Fur Elise.mp3
./Beethoven - Moonlight Sonata.mp3
./Beethoven - Ode to Joy.mp3
./Beethoven - Rage Over the Lost Penny.mp3

after:

./Beethoven/Fur Elise.mp3
./Beethoven/Moonlight Sonata.mp3
./Beethoven/Ode to Joy.mp3
./Beethoven/Rage Over the Lost Penny.mp3

보너스 라운드 ...

이 스크립트를 사용하여 폴더에서 단일 폴더로 파일 이동 :

일치하는 모든 파일을 모아서 현재 폴더에 저장한다고 가정하면 다음과 같이 할 수 있습니다.

sedrename 's|.*/||' **/*.mp3

before:

./Beethoven/Fur Elise.mp3
./Beethoven/Moonlight Sonata.mp3
./Beethoven/Ode to Joy.mp3
./Beethoven/Rage Over the Lost Penny.mp3

after:

./Beethoven/ # (now empty)
./Fur Elise.mp3
./Moonlight Sonata.mp3
./Ode to Joy.mp3
./Rage Over the Lost Penny.mp3

sed 정규식 패턴에 대한 참고 사항

정규 sed 패턴 규칙이이 스크립트에 적용되며 이러한 패턴은 PCRE (Perl 호환 정규식)가 아닙니다. 플랫폼에 따라 sed -r또는 둘 중 하나를 사용하여 확장 정규식 구문을 sed 할 수 있습니다 sed -E.

man re_formatsed 기본 및 확장 정규식 패턴에 대한 전체 설명은 POSIX 준수 를 참조하십시오 .


4

이름 바꾸기가 이런 종류의 작업에 사용하기에 훨씬 더 간단한 도구라는 것을 알았습니다. OSX 용 Homebrew에서 찾았습니다.

귀하의 예를 들면 다음과 같습니다.

rename 's/\d*?\.\d*?\.\d*?//' *.pkg

's'는 대체를 의미합니다. 양식은 s / searchPattern / replacement / files_to_apply입니다. 약간의 연구가 필요하지만 노력할만한 가치가있는 정규식을 사용해야합니다.


나는 동의한다. 사용 가끔 일, 들어 forsed등 괜찮지 만, 그냥 사용에 더 빨리 더 간단하고 rename자신은 종종이 일을 찾을 경우.
argentum2f

그게 당신이 생리를 피하는 건가요?
Big Money

문제는 이것이 이식성이 없다는 것입니다. 모든 플랫폼에 rename명령 이있는 것은 아닙니다 . 또한 일부는 기능, 구문 및 / 또는 옵션이 다른 다른 rename 명령을 사용합니다. 이것은 rename꽤 인기있는 Perl처럼 보입니다 . 그러나 대답은 이것을 정말로 명확히해야합니다.
tripleee

3

sed이것에 대해 더 잘 사용하십시오 .

find . -type f -name "*.pkg" |
 sed -e 's/((.*)-[0-9.]*\.pkg)/\1 \2.pkg/g' |
 while read nameA nameB; do
    mv $nameA $nameB;
 done

정규 표현식을 파악하는 것은 연습으로 남겨집니다 (공백을 포함하는 파일 이름을 다루는 것처럼)


감사합니다 : 이름에 공백이 사용되지 않습니다.
Jeremy L

1

이것은 가정에서 작동하는 것 같습니다

  • 모든 것은 $ pkg로 끝납니다.
  • 버전 번호는 항상 "-"로 시작합니다.

.pkg를 떼어 낸 다음-..

for x in $(ls); do echo $x $(echo $x | sed 's/\.pkg//g' | sed 's/-.*//g').pkg; done

이름에 하이픈이있는 일부 패키지가 있습니다 (세 번째 예 참조). 이것이 코드에 영향을 줍니까?
Jeremy L

1
를 사용하여 여러 sed 표현식을 실행할 수 있습니다 -e. 예 sed -e 's/\.pkg//g' -e 's/-.*//g'.
tacotuesday

ls출력을 구문 분석 하고 변수를 인용 하지 마십시오 . 쉘 스크립트의 일반적인 문제와 안티 패턴을 진단 하려면 shellcheck.net 을 참조하십시오 .
tripleee

1

동일한 폴더에서 *.txt이름을 바꿀 여러 파일이 있습니다 .sql. 아래는 나를 위해 일했습니다.

for i in \`ls *.txt | awk -F "." '{print $1}'\` ;do mv $i.txt $i.sql; done

2
OP의 실제 사용 사례로 추상화하여 답변을 개선 할 수 있습니다.
dakab

여기에는 명백한 구문 오류뿐만 아니라 여러 문제가 있습니다. 시작 하려면 shellcheck.net 을 참조하십시오 .
tripleee

0

이 답변에 감사드립니다. 나는 또한 어떤 종류의 문제가 있었다. .nzb.queued 파일을 .nzb 파일로 이동. 파일 이름에 공백과 기타 균열이 있었고 이것은 내 문제를 해결했습니다.

find . -type f -name "*.nzb.queued" |
sed -ne "s/^\(\(.*\).nzb.queued\)$/mv -v \"\1\" \"\2.nzb\"/p" |
sh

그것은 Diomidis Spinellis의 대답을 기반으로합니다.

정규식은 전체 파일 이름에 대해 하나의 그룹과 .nzb.queued 이전 부분에 대해 하나의 그룹을 만든 다음 셸 이동 명령을 만듭니다. 인용 된 문자열로. 이것은 이미 sed에 의해 수행되기 때문에 쉘 스크립트에서 루프를 생성하는 것을 방지합니다.


이것은 GNU sed에만 해당됩니다. BSD에서 sed는 -n같은 방식으로 옵션을 해석하지 않습니다 .
ocodo
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.