공백을 "-"로 바꾸고 현재 디렉터리에 있는 모든 파일의 모든 문자를 소문자로 만드는 스크립트를 작성하려고 합니다.
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'
오류 메시지는 다음이 있음을 나타냅니다.rename
util-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