Python

如何將帶有尾隨空格的字元串傳遞給python的子程序

  • September 23, 2018

我想將具有尾隨空格的文件批量重命名為沒有空格的相同名稱。在 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

在這裡,正如@jordanm 所指出的,您的問題是您正在呼叫遍歷的每個目錄的文件mv名*,*os.walkos.walk不會更改目前工作目錄。

因此,對於那些在子目錄中找到的文件,這是行不通的。

您需要將文件的完整路徑傳遞給mv,因此類似於os.path.join(dirpath, name).

理想情況下,您希望像’s或BSD /GNU那樣進行時更改目錄,這將使其更安全並避免目錄樹過深的問題,但我認為您不能使用’輕鬆做到這一點小號_perl File::Find``finddepth()``find -execdir``python``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實際上是一個大型全功能 shell 的系統上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()

空格與空格

python’sstrip()去除前導和尾隨空白字元。這裡所有字元串都以 開頭randconf,所以它與 相同rstrip()空白包括 ASCII 空格字元,但各種其他垂直和水平間距字元(雖然看起來只有 ASCII 字元),如 TAB、LF、CR…

當您在行中的任何位置查找包含空格字元的文件時,您最終可能不會重命名一些以空格結尾的文件名(比如 "randconf_\t"不包含任何空格),或者呼叫mv不以空格結尾的文件名(如"randconf_x y")。

如果您只關心尾隨空格字元,您可以使用fnmatch.filter(filenames, "randconf* ")並且rstrip(" ")

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 空格)。

引用自:https://unix.stackexchange.com/questions/470567