我想将具有尾随空格的文件批量重命名为不带空格的相同名称。在 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
像sfinddepth()
或 BSD/GNU那样在进行时更改目录find -execdir
,这将使它更安全并避免目录树太深的问题,但我认为你不能用python
s轻松做到这一点os.walk()
。
现在,您的代码还存在一些其他问题:
命令注入漏洞
现在,尾随空格是您最不用担心的:
subprocess.call("mv '%s' '%s'"%(name,name.strip()),shell=True)
这基本上是一个命令注入漏洞(例如一个名为'$(reboot)'
(带引号)的文件)。
通常,不要在解释为 shell 代码(或可能造成任何损害的任何语言的代码)的字符串中嵌入任意文本。
使用您的代码(使用表单的变体),您可能会对名为或'mv "%s" "%s"'
的文件出现相同的错误。randconf $x
randconf $(test)
在这里,使用:
subprocess.call(("mv", "--", name, name.strip()),shell=False)
如果您必须使用 shell,将数据传递到该 shell 的更好方法是使用环境变量:
os.putenv("OLD", name)
os.putenv("NEW", name.strip())
subprocess.call('mv -- "$OLD" "$NEW"',shell=True)
运行 shell 也很昂贵。特别是在sh
实际上有一个大型全功能外壳(例如 )的系统上bash
,ksh93
或者zsh
通常需要大量时间来加载和初始化的系统上。
当您调用 shell 时,您也可以在 shell 代码中完成整个查找和重命名工作。
歧义性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"
但这不是便携式的。 (另请注意,GNUmv
不在renameat2()
Linux 上使用,因此确实有一个(此处较小的)竞争条件)
无论如何,在 中python
,您不必调用单独的程序来重命名文件。
os.rename(name, name.strip());
(我不认为这python
与 Linux 有绑定renameat2()
)
空格与空白
python
的strip()
条带前导和尾随空白人物。这里所有的字符串都以 开头randconf
,所以和 一样rstrip()
。空白包括 ASCII 空格字符,但也包括各种其他垂直和水平空格字符(但看起来只有 ASCII 字符),例如 TAB、LF、CR...
当您寻找文件时包含如果在行中的任何位置添加空格字符,您最终可能不会重命名某些以空格结尾的文件名(例如 "randconf_\t"
不包含任何空格的文件名),或者调用mv
不以空格结尾的文件名(例如"randconf_x y"
)。
如果您只关心尾随空格字符,您可以使用fnmatch.filter(filenames, "randconf* ")
andrstrip(" ")
POSIX shell 等效项:
请使用 POSIX shell 和实用程序语法来做到这一点:
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 {} +
(请注意,它会删除当前区域设置中被视为空白的所有字符,而不仅仅是像 python 中那样的 ASCII 字符;将区域设置更改为C
仅匹配 ASCII 空白)。