如何將帶有尾隨空格的字元串傳遞給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
在這裡,正如@jordanm 所指出的,您的問題是您正在呼叫遍歷的每個目錄的文件
mv
名*,*os.walk
但os.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 $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
實際上是一個大型全功能 shell 的系統上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"
但這不是攜帶式的。(另請注意,GNU
mv
不在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 空格)。