공백이 있는 파일의 이름을 공백 없이 동일한 이름으로 일괄 변경하고 싶습니다. 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::Find
s finddepth()
또는 BSD/GNU 처럼 디렉터리를 변경하면 find -execdir
더 안전해지고 디렉터리 트리가 너무 깊어지는 문제를 피할 수 있지만 python
s로는 쉽게 그렇게 할 수 없을 것 같습니다 os.walk()
.
이제 코드에 몇 가지 다른 문제가 있습니다.
명령 주입 취약점
이제 후행 공백은 걱정거리가 가장 적습니다.
subprocess.call("mv '%s' '%s'"%(name,name.strip()),shell=True)
이는 기본적으로 명령 주입 취약점(예: '$(reboot)'
따옴표 포함)이라는 이름의 파일입니다.
일반적으로 쉘 코드(또는 해를 끼칠 수 있는 언어로 된 코드)로 해석되는 문자열에 임의의 텍스트를 포함하지 마십시오.
코드를 사용하면(양식 변형 사용) 'mv "%s" "%s"'
이름이 또는 인 파일에 대해 동일한 오류가 발생할 수 있습니다.randconf $x
randconf $(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
.)