나는 이것을 간단한 디렉토리 구조에서 테스트해왔습니다.
"Season"이라는 이름의 디렉터리 및/또는 하위 디렉터리를 "Sn"으로 변경하려고 합니다. 스크립트가 내가 원하는 것을 변경하는 지점에 이르렀습니다... 아래 목록에 표시된 것처럼 최상위 디렉토리는 제외합니다 - "시즌 1" "시즌 2" "시즌 3".
디렉토리 구조:
.
├── AnotherShow
│ ├── Sn1
│ ├── Sn2
│ ├── Sn3
│ └── Sn4
├── Movie1
│ ├── Sn1
│ └── Sn2
├── Movie2
│ ├── Sn1
│ ├── Sn2
│ ├── Sn3
│ └── Sn4
├── Movie3
│ ├── Sn1
│ └── Sn3
├── Movie4
│ ├── Sn2
│ └── Sn3
├── Season 1
├── Season 2
├── Season 3
├── Show
│ ├── Sn1
│ ├── Sn2
│ ├── Sn3
│ ├── Sn4
│ └── Sn5
└── TV
├── Sn1
├── Sn2
├── Sn3
└── Sn4
내 스크립트는 다음과 같습니다
array="$(find . -maxdepth 2 -type d -iname 'Season*' -print)"; # A more refined way to search for Seasons in a directory.
for dir in "${array[@]}"; do # Put this list into an array. Surround the array with quotes to keep all space s (if any) together.
new="$(echo "$dir" | sed -e 's/Season 0/Sn/' -e 's/Season /Sn/')"; # Only change Season 0 to SN. Leave othe rs alone.
sudo mv -v "$dir" "$new" && echo Changed "$dir" to "$new";
done
답변1
문제는 배열을 만드는 것이 아니라 문자열을 만드는 것입니다. 배열의 첫 번째 요소를 인쇄해 보면 이를 쉽게 테스트할 수 있습니다.
$ array="$(find . -maxdepth 2 -type d -iname 'Season*' -print)";
$ echo $array
./Season 3 ./Season 1 ./Season 2
괜찮아 보이는데요? 그러나 이것이 배열이라면 각 요소를 개별적으로 인쇄할 수 있습니다. 불행하게도 위의 내용은 배열이 아닌 단순한 문자열 1 입니다.
$ echo ${array[0]}
./Season 3 ./Season 1 ./Season 2
$ echo ${array[1]}
배열은 실제로 단일 문자열로 구성되므로 ${array[1]}
"배열"의 두 번째 요소( )가 비어 있습니다. 어떤 경우든 이를 위해 배열을 사용할 필요는 없으며 단지 읽기의 출력만 사용하면 됩니다 find
(귀하도 마찬가지입니다 -print
. 기본적으로 그렇게 합니다). 스크립트의 작업 버전은 다음과 같습니다.
#!/usr/bin/env bash
find . -maxdepth 2 -type d -iname 'Season*' | sort -r |
while IFS= read -r dir
do
mv "$dir" ${dir/Season /Sn} && echo Changed "$dir" to "$dir/Season /Sn}";
done
여기에는 몇 가지 트릭이 사용됩니다. 먼저 while
결과를 반복합니다 find
. 이는 사용한 루프와 동일한 기본 원리이지만 표준 입력에서 읽는 것과 for
결합됩니다 .read
공백으로 인한 분할을 피해야 합니다 IFS=
(공백이 없으면 디렉토리는 및 Season 1
에서 분할됩니다 ).Season
1
of read는 백슬래시 이스케이프 문자( 및 문자 그대로 처리되는 문자)를 -r
허용하지 않도록 지시하여 가장 이상한 이름을 처리할 수 있도록 합니다.\t
\n
이는 sort -r
일치하는 다른 디렉터리의 패턴과 일치하는 디렉터리가 있는 경우 하위 디렉터리가 상위 디렉터리 앞에 나열되도록 하기 위해 필요합니다. 예를 들어:
./Season 12/Season 13
./Season 12
이렇게 하면 실행이 보장됩니다.mv "Season 12" "Sn12"
뒤쪽에달리기 mv "Season 12"/"Season 13" "Season 12"/"Sn13"
. 이렇게 하지 않으면 두 번째 명령이 Season 12
더 이상 존재하지 않기 때문에 실패합니다.
마지막으로 을(를) 제거했습니다 sudo
. 스크립트를 로 실행해야 합니다 sudo script.sh
. sudo
스크립트에서 무작위로 호출하는 것은 결코 좋은 생각이 아닙니다.
1 자세한 내용은 잘 모르겠지만 bash는 문자열 변수를 한 요소의 배열로 취급합니다.
답변2
아직 문제가 무엇인지 모르지만 귀하의 스크립트에 대한 몇 가지 의견이 있습니다.
루프에서 (단일 데이터가 포함된) 바이너리 파일을 호출하지 마세요. 이는 성능에 영향을 미칩니다.
전체 스크립트를 호출하려면 sudo를 사용해야 하지만 스크립트 내에서는 호출할 수 없습니다.
sed
파일 이름을 변경할 필요가 없습니다 . bash
스스로 할 수 있습니다:
if [[ $dir =~ "Season 0" ]]; then
new="${dir/Season 0/Sn}"
else
new="${dir/Season /Sn}"
fi
아니면 if
:
new="${dir/Season /Sn}"
new="${new/Sn0/Sn}"
배열이 필요하지 않습니다.
find ... | while read dirpath; do
이것의 "장점"은 디렉토리 이름의 공백이 배열 정의를 손상시키지 않는다는 것입니다. 당신이 왜 일하는지 모르겠어요 array="$(find ... -print)"
. (쉘은 공백과 개행 문자 사이에서 단어 분할을 수행하지 않기 때문에) 그렇게 하면 안 됩니다.
답변3
위의 내용을 간단히 변형한 것입니다.
for dir in ./*/Season\ */
do
mv "$dir" "${dir/eason /n}" && echo "$dir changed to ${dir/eason /n}"
done
하위 디렉터리와 함께 사용계절. (디렉토리뿐만 아니라) 파일의 이름을 바꾸려면 /
패턴 끝에서 삭제하면 다음과 같습니다../*/Season\ *
필요한 경우 sudo
스크립트 대신 한 줄 명령을 사용하여 이 작업을 수행하는 것이 좋습니다.
for dir in ./*/Season\ */ ; do sudo mv -v "$dir" "${dir/eason /n}" ; done