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::Findsfinddepth()やBSD / GNUのようにディレクトリを変更すると、find -execdirより安全になり、ディレクトリツリーが深すぎる問題を回避できますが、pythonsでは簡単にはできませんos.walk()

今、コードにはいくつかの異なる問題があります。

コマンド注入の脆弱性

今後の空白は心配が最も少なくなります。

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

これは、デフォルトでコマンドインジェクションの脆弱性('$(reboot)'引用符など)という名前のファイルです。

通常、シェルコード(または害を及ぼす可能性のある言語のコード)として解釈される文字列には任意のテキストを含めないでください。

コードを使用すると(フォームバリアントを使用)、'mv "%s" "%s"'名前またはファイルに同じエラーが発生する可能性があります。randconf $xrandconf $(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 {} +

(PythonなどのASCII文字だけでなく、現在のロケールからスペースと見なされているすべての文字を削除します。ASCIIスペースにのみ一致するようにロケールを変更します。C

おすすめ記事