디렉터리 이름 바꾸기 bash 스크립트 - 대상... 해당 파일이나 디렉터리가 없습니다.

디렉터리 이름 바꾸기 bash 스크립트 - 대상... 해당 파일이나 디렉터리가 없습니다.

나는 이것을 간단한 디렉토리 구조에서 테스트해왔습니다.
"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에서 분할됩니다 ).Season1

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

관련 정보