POSIX 이식성과 함께 모든 유형의 문자를 사용하여 많은 수의 파일 이름을 바꿉니다.

POSIX 이식성과 함께 모든 유형의 문자를 사용하여 많은 수의 파일 이름을 바꿉니다.

때로는 디렉터리에 있는 모든 파일의 이름을 바꿔야 하는 경우가 있습니다(이름 바꾸기 규칙은 나중에 다룹니다). 여기서 파일 이름은 항상 "filenamename.extension" 형식입니다(확장자는 항상 존재하며 변경됩니다). 이름에는 [:graph:] 클래스의 공백과 문자가 포함될 수 있습니다. 첫 번째 질문은 *NIX 시스템(특히 Linux, BSD 및 이후 AIX와 같은 다른 시스템) 간에 완전히 이식 가능해야 한다는 것입니다. 두 번째 질문은 [:graph:] 클래스에 관한 것입니다. 파일 이름은 다음과 같습니다.

cat.txt
dog_and_cat.txt
Where is the cat?.png
my.cat.is.cute.txt.js.html
;;; ;;; ;;;.......321
áéúő _[a lot of whitespaces]_ óü^^^^^ö.jpg

이를 처리하고 for 루프에 넣는 것이 어렵다는 것을 쉽게 알 수 있습니다. 예를 들어,

for i in *; do something; done

특히 다양한 운영 체제에서는 공백과 이상한 문자가 항상 선호되는 것은 아닙니다.

이름 바꾸기 규칙은 모든 파일의 이름을 md5sum과 같은 특정 $FOOBAR.$EXTENSION형식 의 해시 로 바꾸는 것입니다. $FOOBARfor 루프 안에는 다음과 같은 줄이 있습니다.

mv $FILE $(md5sum $FILE | sed 's/\ \ .\+//');

파일을 자체 md5sum으로 이동하지만 확장자는 사라집니다. 확장 기능을 유지하고 싶습니다. 거의 항상 형식입니다 .[a-zA-Z0-9]{1,3}. 때로는 .tar.gz유사한 확장을 유지해야 할 때도 있습니다(물론 변수에 추가할 수 있습니다 MYEXTENSIONS='tar.gz tar.bz2 foo.bar').

내 직감으로는 이 문제가 잘 매개변수화된 기본 UNIX/셸 명령으로 해결될 수 있다고 말하지만 지금은 매우 어렵습니다. 답변을 통해 많은 것을 배울 수 있을 거라 확신합니다. 내가 마법의 단어를 말한 걸 알아요이식성, 그러나 언어를 지정해야 하는 경우 선호되는 솔루션은 bash입니다.

답변1

실제로 a로 시작하는 파일 이름이 와일드카드 일치에서 제외된다는 for i in *; do something; done점을 제외하면 모든 파일 이름은 올바르게 처리됩니다 . .모든 파일( .및 제외 ..) 을 이식 가능하게 일치시키려면 * .[!.]* ..?*일치하지 않는 패턴이 그대로 남아서 존재하지 않는 파일을 일치시키고 건너뜁니다.

$i문제가 발생하면 나중에 인용을 올바르게 하지 않았기 때문일 수 있습니다.변수 대체 및 명령 대체는 항상 큰따옴표로 묶습니다 "$foo"."$(cmd)"필드 분할 및 와일드카드 발생을 계획하지 않는 한.

파일 이름을 외부 명령에 전달해야 하는 경우(여기에서는 필요하지 않음) echo "$foo"항상 $foo문자 그대로 인쇄되지는 않는다는 점에 유의하세요. 일부 쉘은 백슬래시 확장을 수행하며, $foo로 시작하는 일부 값은 -옵션으로 처리됩니다. 문자열을 정확하게 인쇄하는 안전하고 POSIX 호환 방법은 다음과 같습니다.

printf '%s' "$foo"

또는 printf '%s\n' "$foo"끝에 개행 문자를 추가하세요. 주목해야 할 또 다른 점은 명령 대체가 후행 줄 바꿈을 제거한다는 것입니다. 줄 바꿈을 유지해야 하는 경우 가능한 한 가지 방법은 줄 바꿈이 아닌 문자를 데이터에 추가하고 변환 시 해당 문자가 유지되는지 확인한 다음 마지막으로 해당 문자를 자르는 것입니다. 예를 들어:

mangled_file_name="$(printf '%sa' "$file_name" | tr -sc '[:alnum:]-+_.' '[_*]')"
mangled_file_name="${mangled_file_name%a}"

파일의 md5sum을 추출하려면 출력에 파일 이름을 포함하지 마십시오 md5sum. 이렇게 하면 제거가 어려워집니다. 데이터를 md5sum표준 입력 으로 전달합니다 .

md5sum명령은 POSIX에 없습니다. 일부 UNIX 변형에는 이 기능이 있거나 md5전혀 없습니다. cksumPOSIX이지만 충돌이 발생하기 쉽습니다.

바라보다파일 이름의 확장자를 가져옵니다.파일 확장자를 얻는 방법.

모두 함께 정리해 보겠습니다(테스트되지 않음). 여기에 있는 모든 기능은 POSIX 셸에서 작동하지만 약간의 bash 기능을 제공하지만 너무 많지는 않습니다.

for old_name in * .[!.]* ..?*; do
  if ! [ -e "$old_name" ]; then continue; fi
  hash=$(md5sum <"$old_name")
  case "$old_name" in
    *.*.gz|*.*.bz2)                   # double extension
      ext=".${old_name##*.}"
      tmp="${old_name%.*}"
      ext=".${old_name##*.}$ext";;
    ?*.*) ext=".${old_name##*.}";;    # simple extension
    *) ext=;;                         # no extension
  esac
  mv -- "$old_name" "$hash$ext"
done

지정된 이름의 대상 파일이 이미 존재하는 경우는 고려하지 않습니다. 특히 이름이 채택한 규칙과 비슷하지만 체크섬 부분이 파일의 내용과 일치하지 않지만 동일한 확장자를 가진 다른 파일의 내용이 상대적인 사전 편찬 순서에 따라 달라지는 기존 파일이 있는 경우 어떻게 되나요? 파일 이름.

답변2

꽤 복잡한 질문이므로 몇 가지만 알려 드리겠습니다.지침:

  • 큰따옴표파일 이름 변수는 어디에나 있습니다. 이렇게 하면 단어 분할로 인한 거의 모든 공백 문제를 피할 수 있습니다.
  • 내부 변수는 $()외부 구조와 마찬가지로 참조되어야 합니다. 추가 이스케이프가 필요하지 않습니다.
  • 이것$()``후행 개행 문자로 스트립을 구성합니다 ., 따라서 다른 문자를 추가한 다음 구성에서 제거해야 합니다 $().

    varx="$([command which might print a value ending in \n]; echo x)"
    var="${varx%x}"
    
  • --명령에 필요한파일 이름에서 매개변수를 분리하세요., 파일 이름은 으로 시작할 수 있으므로 --매개변수로 처리됩니다.
    • find이 구문은 지원되지 않으므로 readlink정의에 따라 슬래시로 시작하는 절대 경로를 가져오거나 제공된 경로가 find이미 절대 경로이거나 로 시작하는지 확인하는 데 사용하십시오 ./.
  • <(전송 프로세스가 종료될 때 파이프 손상을 방지하려면 파이프 대신 프로세스 대체를 사용하십시오 .
  • 욕심 많은 명령(예: 모든 데이터를 삼키 cat거나 삼키는 것) 을 피하기 위해 데이터 전달에 표준 입력 대신 3에서 9 사이의 파일 설명자를 사용하십시오 .ssh
  • 첫 번째,시험! 나는 일반적으로 위에서 언급한 내용을 테스트하기 위해 이 파일 이름을 사용합니다.$'--$`!*@\a\b\E\f\r\t\v\\\'"\360\240\202\211 \n'

관련 정보