Python의 하위 프로세스에 후행 공백이 있는 문자열을 전달하는 방법

Python의 하위 프로세스에 후행 공백이 있는 문자열을 전달하는 방법

공백이 있는 파일의 이름을 공백 없이 동일한 이름으로 일괄 변경하고 싶습니다. Python 3.6.5에서는 다음이 제대로 작동합니다.

subprocess.call("mv '%s' '%s'"%(name,name.strip()),shell=True)

그러나 Python 2.7에서는 "파일을 찾을 수 없습니다"와 같은 오류가 발생합니다. Python 2.7에서 내가 원하는 것을 달성할 수 있는 방법이 있나요?

업데이트: 코드는 다음과 같습니다.

for root, dirnames, filenames in os.walk('.'):
   for name in fnmatch.filter(filenames, "randconf*"):     
          if " " in name: 
             subprocess.call('mv "%s" "%s"'%name,name.strip()),shell=True)

하위 프로세스 행을 "인쇄 이름"으로 바꾸면 다음과 같은 결과가 표시됩니다.

randconf_1                                                      
randconf_10                                                     
randconf_11                                                     
randconf_12                                                     
randconf_13                                                     
randconf_14                                                     
randconf_15

답변1

여기서 @jordanm이 지적했듯이 문제는 mv전화를 걸고 있다는 것입니다.이름현재 작업 디렉터리를 변경 os.walk하지 않고 각 디렉터리의 파일을 반복합니다 .os.walk

따라서 하위 디렉터리에 있는 파일에서는 작동하지 않습니다.

파일의 전체 경로를 전달해야 mv하므로 os.path.join(dirpath, name).

이상적으로는걷다perl File::Finds finddepth()또는 BSD/GNU 처럼 디렉터리를 변경하면 find -execdir더 안전해지고 디렉터리 트리가 너무 깊어지는 문제를 피할 수 있지만 pythons로는 쉽게 그렇게 할 수 없을 것 같습니다 os.walk().

이제 코드에 몇 가지 다른 문제가 있습니다.

명령 주입 취약점

이제 후행 공백은 걱정거리가 가장 적습니다.

subprocess.call("mv '%s' '%s'"%(name,name.strip()),shell=True)

이는 기본적으로 명령 주입 취약점(예: '$(reboot)'따옴표 포함)이라는 이름의 파일입니다.

일반적으로 쉘 코드(또는 해를 끼칠 수 있는 언어로 된 코드)로 해석되는 문자열에 임의의 텍스트를 포함하지 마십시오.

코드를 사용하면(양식 변형 사용) 'mv "%s" "%s"'이름이 또는 인 파일에 대해 동일한 오류가 발생할 수 있습니다.randconf $xrandconf $(test)

여기서는 다음을 사용하세요.

subprocess.call(("mv", "--", name, name.strip()),shell=False)

셸을 사용해야 하는 경우 해당 셸에 데이터를 전달하는 더 좋은 방법은 환경 변수를 사용하는 것입니다.

os.putenv("OLD", name)
os.putenv("NEW", name.strip())
subprocess.call('mv -- "$OLD" "$NEW"',shell=True)

쉘을 실행하는 것도 비용이 많이 듭니다. 특히 sh실제로 모든 기능을 갖춘 대규모 셸(예:)이 있는 시스템이나 일반적으로 로드 및 초기화하는 데 많은 시간이 걸리는 시스템에서는 bash더욱 ksh93그렇습니다 zsh.

쉘을 호출할 때 쉘 코드에서 전체 찾기 및 이름 바꾸기를 수행할 수도 있습니다.

모호mv

mv최고의 인터페이스를 갖춘 명령이 아닙니다( cp그리고 ln동일한 문제가 있습니다). 문제는 그것이 mv많은 다른 일을 한다는 것입니다. 그러나 질문/질문 방법에 기초한 것이 아니라 상황에 기초합니다.

mv A B

누구나

  • B가 존재하고 유형인 경우 A의 이름을 B/A로 바꿉니다.목차또는디렉토리에 대한 심볼릭 링크동일한 파일 시스템에서
  • 동일한 작업을 수행하되 가능한 한 많은 속성을 유지하면서 복사본을 사용한 다음 이름 변경이 파일 시스템 경계를 넘을 경우 삭제하십시오.
  • 그렇지 않으면 이름을 바꿉니다(이전에 대상이 존재했다면 제거).

여기서는 기본적인 rename()시스템 호출만 필요합니다. 또는 더 나은 방법 rename()은 이미 존재하는 파일(예: Linux' )을 손상시키지 않으므로 renameat2(... RENAME_NOREPLACE)두 가지 모두를 완화 "randconf_1 "하고 ."randconf_1 "randonf_1

GNU를 사용하면 다음을 mv통해 이 작업을 수행할 수 있습니다.

mv -nT -- "$old" "$new"

하지만 휴대성이 없습니다. (또한 GNU는 Linux에서 사용되지 mv않으므로 renameat2()여기에 (사소한) 경쟁 조건이 있습니다.)

그럼에도 불구하고 python파일 이름을 바꾸기 위해 별도의 프로그램을 호출할 필요는 없습니다.

os.rename(name, name.strip());

python(내 생각에는 이것이 Linux와 관련이 있는 것 같지 않습니다 renameat2().)

공백과 공백

python선행 strip()및 후행 스트립공백수치. 여기의 모든 문자열은 로 시작하므로 randconf와 동일합니다 rstrip().공백ASCII 공백 문자뿐만 아니라 TAB, LF, CR...과 같은 다양한 기타 수직 및 수평 공백 문자(그러나 ASCII 전용 문자로 표시됨)도 포함됩니다.

파일을 찾고 있을 때포함하다줄의 아무 곳에나 공백 문자를 추가하면 공백으로 끝나는 일부 파일 이름(예: "randconf_\t"공백을 포함하지 않는 파일 이름)의 이름을 바꾸지 않거나 mv공백으로 끝나지 않는 파일 이름(예: "randconf_x y")을 호출하게 될 수 있습니다.

후행 공백 문자에만 관심이 있는 경우 다음을 사용할 수 있습니다 fnmatch.filter(filenames, "randconf* ").rstrip(" ")

POSIX 셸에 해당:

이를 수행하려면 POSIX 셸 및 유틸리티 구문을 사용하십시오.

find . -depth -name 'randconf*[[:space:]]' ! -type d -exec sh -c '
  for file do
    newfile=${file%"${file##*[![:space:]]}"}
    [ -e "$newfile" ] || [ -L "$newfile" ] || mv -- "$file" "$newfile"
  done' sh {} +

또는 약간 더 안정적인 GNU 유틸리티를 사용하십시오.

find . -depth -name 'randconf*[[:space:]]' ! -type d -execdir bash -O extglob -c '
  for file do
    newfile=${file%*([[:space:]])}
    mv -nT -- "$file" "$newfile"
  done' sh {} +

(파이썬과 같은 ASCII 문자뿐만 아니라 현재 로케일에서 공백으로 간주되는 모든 문자를 제거합니다. ASCII 공백에만 일치하도록 로케일을 변경하십시오 C.)

관련 정보