如何将带有尾随空格的字符串传递给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::Find像sfinddepth()或 BSD/GNU那样在进行时更改目录find -execdir,这将使它更安全并避免目录树太深的问题,但我认为你不能用pythons轻松做到这一点os.walk()

现在,您的代码还存在一些其他问题:

命令注入漏洞

现在,尾随空格是您最不用担心的:

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

这基本上是一个命令注入漏洞(例如一个名为'$(reboot)'(带引号)的文件)。

通常,不要在解释为 shell 代码(或可能造成任何损害的任何语言的代码)的字符串中嵌入任意文本。

使用您的代码(使用表单的变体),您可能会对名为或'mv "%s" "%s"'的文件出现相同的错误。randconf $xrandconf $(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实际上有一个大型全功能外壳(例如 )的系统上bashksh93或者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()

空格与空白

pythonstrip()条带前导和尾随空白人物。这里所有的字符串都以 开头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 空白)。

相关内容