파일 이름에서 공백과 소문자를 제거하는 스크립트

파일 이름에서 공백과 소문자를 제거하는 스크립트

공백을 "-"로 바꾸고 현재 디렉터리에 있는 모든 파일의 모든 문자를 소문자로 만드는 스크립트를 작성하려고 합니다.

for x in 'ls'
    do
        if [ ! -f $x ]; then
            continue
        fi

        lc = `echo $x | tr '[A-Z]' '[a-z]'`
        if [ $lc != $x ]; then
            mv $x $lc
        fi
    done

find -name "* *" -type f | rename 's/ /-/g'

다음과 같은 출력이 나타납니다. call: rename from to files...

그러나 이름은 변경되지 않습니다. 예를 들면 다음과 같습니다.252680610243-Analyzed Sample2 2Jul12.txt

을(를) 사용하여 권한을 변경했는데 chmod 706이로 인해 문제가 발생합니까? 내가 여기서 무엇을 놓치고 있는 걸까요?

출력은 다음과 같습니다 bash -x lower.sh.

+ for x in ''\''ls'\'''
+ '[' '!' -f ls ']'
+ continue
+ find -name '* *' -type f
+ rename 's/ /-/g'
call: rename from to files...

답변1

Debian 및 그 파생 제품(Ubuntu 포함)에서는 간단한 호출로 쉽게 달성할 수 있습니다.rename:

$ touch 'A B'
$ rename 'tr/ A-Z/-a-z/' -- *
$ ls
a-b

하지만 당신 rename은 하나입니다같은 이름을 가진 다른 유틸리티.

답변2

스크립트에 몇 가지 문제가 있습니다.

for x in 'ls'

하나의 단어가 포함된 목록을 반복하고 있습니다 ls. 아마도 `ls`구문 분석을 위해 백틱을 사용하여 작성하려고 할 것입니다 ls.출력을 구문 분석하지 않습니다.ls: 파일 이름에 특수 문자(예: 공백)가 포함되어 있으면 작동하지 않습니다. 쉘에는 디렉토리의 파일을 나열하는 와일드카드라는 방법이 이미 내장되어 있습니다. 그러니 for x in *.

        if [ ! -f $x ]; then

파일 이름에 공백과 기타 특수 문자가 포함되어 있으면 작동하지 않습니다. $x외부에 따옴표를 쓰면 결과가 별도의 단어로 분할되기 때문입니다(그리고 각 단어는 부팅 시 와일드카드 패턴으로 해석됩니다). 이 효과를 피하려면 $x큰따옴표를 넣으십시오: if [ ! -f "$x" ]; then. 당신은 기억할 수 있습니다복잡한 규칙, 아니면변수 대체에는 항상 큰따옴표를 사용하십시오.

큰따옴표 누락으로 인해 다른 여러 줄이 깨졌습니다. 각 변수 대체 주위에 배치하십시오.

       lc = `echo $x | tr '[A-Z]' '[a-z]'`

등호 주위에 공백이 있기 때문에 이것은 작동하지 않습니다. 줄은 command 를 실행하고 lc이를 =첫 번째 인수(및 다른 인수)로 전달합니다. 쉘에서는 할당 기호 =주위에 공백이 있을 수 없습니다 .

매개변수를 묶을 필요는 없습니다 tr. 여기서 명령은 소문자 외에 [by [by 로 바꾸고 싶다고 말하기 때문에 작동합니다 ].]

명령 대체에는 $(…)백틱 대신 달러 괄호를 사용하는 것이 좋습니다 . `…`백틱에는 내용을 인용하기 위한 복잡한 규칙이 있는 반면 $(…)백틱에는 매우 직관적인 구문이 있다는 점을 제외하면 둘은 동일합니다. 전체적으로 명령은 다음과 같아야 합니다.lc=$(echo "$x" | tr A-Z a-z)

find -name "* *" -type f | rename 's/ /-/g'

오류 메시지는 다음이 있음을 나타냅니다.renameutil-linux 패키지의 명령대신에펄 스크립트Debian 및 그 파생물(Ubuntu 포함)에서 발견됩니다. 사용하는 구문은 Perl 스크립트에만 적합합니다.

Perl 스크립트를 사용하는 경우 대소문자를 소문자로 변경할 수 있으므로 미리 이 작업을 수행할 필요가 없습니다. 그리고 find하위 디렉터리(첫 번째 부분에서는 처리하지 않음)에 있는 파일의 이름을 바꾸려는 경우가 아니면 이를 호출할 필요가 없습니다 . 현재 디렉터리의 모든 파일 이름을 바꾸려면 다음과 같이 작성할 수 있습니다.

prename 'tr/A-Z /a-z-/' -- *

하위 디렉터리, 심볼릭 링크 등이 아닌 일반 파일의 이름만 바꾸려면 다음을 사용하세요 find.

find . -type f -maxdepth 1 -exec prename 'tr/A-Z /a-z-/' {} +

하위 디렉터리의 파일에도 작업을 수행하려는 경우 디렉터리 이름에 공백이나 대문자가 포함될 수 있으므로 이를 유지하지 않도록 주의해야 합니다. Linux를 사용하고 있으므로 디렉터리 이름을 포함하지 않는 인수를 사용하여 호출할 수 있습니다 -execdir.prename

find . -type f -execdir prename 'tr/A-Z /a-z-/' {} +

Perl 유틸리티가 없으면 어떻게 됩니까 rename? util-linux rename유틸리티는 여기서 도움을 줄 수 없습니다. 루프에 대체물을 하나 더 넣을 수 있습니다.

for x in ./*; do
  if [ -f "$x" ]; then
    y=$(echo "$x" | tr 'A-Z-' 'a-z ')
    mv "$x" "$y"
  fi
done

Bash를 사용하면 를 호출할 필요가 없으며 tr문자열 대체 구조를 사용할 수 있습니다.

for x in ./*; do
  if [ -f "$x" ]; then
    lc=${x,,}            # convert all letters to lowercase
    y=${lc// /-}         # replace spaces by hyphens
    if [ "$x" != "$y" ]; then
      mv "$x" "$y"
    fi
  fi
done

하위 디렉터리의 파일에도 작업을 수행하려는 경우 재귀 glob을 사용할 수 있습니다( 옵션으로 활성화 globstar). 또한, 목차 섹션에 대문자나 공백이 포함될 수 있으므로 이를 방해하지 않도록 주의하십시오.

shopt -s globstar
for x in ./**/*; do
  if [ -f "$x" ]; then
    old_basename=${x##*/}
    new_basename=${old_basename,,}
    new_basename=${new_basename// /-}
    if [ "$new_basename" != "$old_basename" ]; then
      mv "${x%/*}/$old_basename" "${x%/*}/$new_basename"
    fi
  fi
done

zsh에서 autoload -U zmv귀하의 것을 입력하면 .zshrc사용할 수 있습니다zmv그리고글로벌 예선 .일반 파일만 일치하며매개변수 확장 구조이름 바꾸기:

zmv -Q '*(.)' '${(L)f// /-}'

하위 디렉터리에 있는 파일에 대해서도 작업을 수행할 수 있습니다.

zmv -Qw '**/*(.)' '$1${(L)2// /-}'

답변3

주요 문제는 파일을 반복하는 것이 아니라 개별 문자열을 반복한다는 것입니다 "ls". 아마도 작은따옴표 대신 백틱을 사용하려고 했을 것입니다. 그럼에도 불구하고,파싱되지 않음ls.

특히 변수를 인용해야 합니다.알다공백이 포함되어 있습니다.

Bash에서는 다음과 같이 할 수 있습니다:

declare -l lc    # whatever is stored in $lc is automatically lower cased
for x in *; do
    lc=${x//[[:space:]]/}  # replace all whitespace with nothing
    [ "$lc" != "$x" ] && mv -- "$x" "$lc"
done

답변4

Bash 4에서 ${var,,}는 var를 소문자로 변환합니다.

for f in *; do f2=${f,,}; mv "$f" "${f2// /-}"; done

관련 정보