หลาม: เปลี่ยนไดเรกทอรีการทำงานของสคริปต์เป็นไดเรกทอรีของสคริปต์


171

ฉันใช้ python shell จาก crontab ทุกนาที:

* * * * * /home/udi/foo/bar.py

/home/udi/fooมีไดเรกทอรีย่อยที่จำเป็นบางอย่างเช่น/home/udi/foo/logและ/home/udi/foo/configซึ่ง/home/udi/foo/bar.pyหมายถึง

ปัญหาคือการcrontabเรียกใช้สคริปต์จากไดเรกทอรีการทำงานที่แตกต่างกันดังนั้นพยายามที่จะเปิด./log/bar.logล้มเหลว

มีวิธีที่ดีในการบอกให้สคริปต์เปลี่ยนไดเรกทอรีทำงานเป็นไดเรกทอรีของสคริปต์หรือไม่ ฉันจะนึกโซลูชันที่จะทำงานกับตำแหน่งสคริปต์ใด ๆ แทนที่จะบอกสคริปต์อย่างชัดเจนว่ามันอยู่ที่ไหน

แก้ไข:

os.chdir(os.path.dirname(sys.argv[0]))

เป็นทางออกที่กะทัดรัดที่สุด ขอบคุณสำหรับคำตอบและคำอธิบายของคุณ!


ไม่เกี่ยวข้องกับcrontabกรณีใช้: ทั้งสองsys.argv[0]และ__file__ล้มเหลวหากสคริปต์จะทำงานโดยใช้execfile(); inspectสามารถใช้โซลูชันพื้นฐาน
jfs

คำตอบ:


206

สิ่งนี้จะเปลี่ยนไดเรกทอรีการทำงานปัจจุบันของคุณเป็นเพื่อให้การเปิดเส้นทางสัมพัทธ์จะทำงาน:

import os
os.chdir("/home/udi/foo")

อย่างไรก็ตามคุณถามว่าจะเปลี่ยนไปยังไดเรกทอรีใดที่สคริปต์ Python ของคุณอยู่แม้ว่าคุณจะไม่ทราบว่าจะใช้ไดเรกทอรีใดเมื่อคุณเขียนสคริปต์ ในการทำเช่นนี้คุณสามารถใช้os.pathฟังก์ชั่น:

import os

abspath = os.path.abspath(__file__)
dname = os.path.dirname(abspath)
os.chdir(dname)

การดำเนินการนี้จะใช้ชื่อไฟล์ของสคริปต์ของคุณแปลงเป็นพา ธ สัมบูรณ์จากนั้นแตกไดเรกทอรีของเส้นทางนั้นแล้วเปลี่ยนเป็นไดเรกทอรีนั้น


3
เท่ากับการเข้ารหัสไดเรกทอรี
Ikke

2
หากคุณใช้งานจาก symlink สิ่งนี้จะไม่ทำงาน ใช้แทน__file__ sys.argv[0]
Chris Down

1
ทำไมต้องเป็นก้าวย่าง ทำไมไม่ง่าย ๆos.chdir(os.path.dirname(__file__))?
พันเอก Panic

8
__file__ล้มเหลวในโปรแกรม "แช่แข็ง" (สร้างโดยใช้ py2exe, PyInstaller, cx_Freeze) sys.argv[0]โรงงาน @ChrisDown: หากคุณต้องการติดตาม symlinks; os.path.realpath()สามารถใช้
jfs

3
@EliCourtwright หาก__file__ยังไม่ได้เป็นเส้นทางที่แน่นอนและผู้ใช้มีการเปลี่ยนแปลงไดเรกทอรีการทำงานแล้วos.path.abspathจะล้มเหลวต่อไป
Arthur Tacca

45

sys.path[0]คุณจะได้รับรุ่นสั้นโดยใช้

os.chdir(sys.path[0])

จากhttp://docs.python.org/library/sys.html#sys.path

ตามที่เริ่มต้นเมื่อโปรแกรมเริ่มต้นรายการแรกของรายการนี้ path[0]คือไดเรกทอรีที่มีสคริปต์ที่ใช้เรียกใช้ตัวแปล Python


23

อย่าทำอย่างนี้

สคริปต์และข้อมูลของคุณไม่ควรถูกบดเป็นไดเรกทอรีขนาดใหญ่ ใส่รหัสของคุณในสถานที่ที่เป็นที่รู้จักบางคน ( site-packagesหรือ/var/opt/udiหรือบางอย่าง) แยกออกจากข้อมูลของคุณ ใช้การควบคุมเวอร์ชันที่ดีในรหัสของคุณเพื่อให้แน่ใจว่าคุณมีเวอร์ชันปัจจุบันและเวอร์ชันก่อนหน้าแยกจากกันเพื่อให้คุณสามารถย้อนกลับไปเป็นเวอร์ชันก่อนหน้าและทดสอบเวอร์ชันในอนาคต

บรรทัดล่างสุด: อย่าปะปนรหัสและข้อมูล

ข้อมูลมีค่า รหัสมาและไป

จัดทำไดเรกทอรีทำงานเป็นค่าอาร์กิวเมนต์บรรทัดคำสั่ง คุณสามารถระบุค่าเริ่มต้นเป็นตัวแปรสภาพแวดล้อม อย่าอนุมานมัน (หรือเดา)

ทำให้มันเป็นค่าอาร์กิวเมนต์ที่จำเป็นและทำสิ่งนี้

import sys
import os
working= os.environ.get("WORKING_DIRECTORY","/some/default")
if len(sys.argv) > 1: working = sys.argv[1]
os.chdir( working )

อย่า "ถือว่า" ไดเรกทอรีตามตำแหน่งของซอฟต์แวร์ของคุณ มันจะไม่ได้ผลดีในระยะยาว


9
ฉันคิดว่าคุณถูกต้องเกี่ยวกับการแยกรหัสและข้อมูลสำหรับแพคเกจซอฟต์แวร์ขนาดใหญ่ แต่ดูเหมือนว่าจะค่อนข้างไกลสำหรับสคริปต์การบำรุงรักษาขนาดเล็ก ฉันเห็นด้วยทั้งหมดเกี่ยวกับการควบคุมเวอร์ชัน
Adam Matan

3
S. Lott ถูกต้อง แยกข้อมูลและรหัสออกจากกันเสมอเว้นเสียแต่ว่าข้อมูลจะไม่หยุดชั่วคราว ตัวอย่างเช่นถ้าคุณมีไอคอนที่เป็นข้อมูล แต่ก็ไม่ได้ชั่วคราวและมันทำให้ความรู้สึกที่จะพิจารณามันเทียบกับมัดซอฟแวร์ (สิ่งที่หมายถึง)
สเตฟาโนโอบอรินี่

5
@Udi Pasmon: ไม่ลึกซึ้งเลย เป็น "สคริปต์บำรุงรักษาขนาดเล็ก" ที่ทำให้องค์กรประสบปัญหาอย่างลึกซึ้ง หลายปีต่อจากนี้ "สคริปต์การบำรุงรักษาขนาดเล็ก" และเป็นลูก ๆ และการสืบทอดและไฟล์ข้อมูลจะเป็นฝันร้ายที่จะคลี่คลายและนำกลับมาใช้ใหม่ เก็บข้อมูลให้ห่างจากโค้ดเท่าที่จะทำได้ - ผ่านพารามิเตอร์สำหรับทุกสิ่ง - ไม่ต้องคิดมาก
S.Lott

1
+1 ฉันคิดว่าฉันต้องการทำในฐานะ OP แต่หลังจากอ่านคำแนะนำของคุณฉันได้แก้ไขสคริปต์ของฉันแทน ตอนนี้มันต้องใช้พารามิเตอร์เพื่อระบุตำแหน่งของไฟล์บันทึก
เลนซามูเอลแมคลีนผู้อาวุโส

+1 การสร้างแพ็คเกจ (รอบต่อนาที) เป็นเรื่องง่ายสำหรับสคริปต์ไพ ธ อนถ้าไดเรกทอรีข้อมูลสามารถปรับแต่งได้ง่าย
jfs

18

เปลี่ยนคำสั่ง crontab ของคุณเป็น

* * * * * (cd /home/udi/foo/ || exit 1; ./bar.py)

(...)เริ่มย่อยเปลือกที่รัน crond ของคุณเป็นคำสั่งเดียว || exit 1ทำให้เกิด cronjob ของคุณล้มเหลวในกรณีที่ว่าไดเรกทอรีที่ใช้งานไม่ได้

แม้ว่าโซลูชันอื่น ๆ อาจดูสง่างามกว่าในระยะยาวสำหรับสคริปต์เฉพาะของคุณตัวอย่างของฉันยังคงมีประโยชน์ในกรณีที่คุณไม่สามารถแก้ไขโปรแกรมหรือคำสั่งที่คุณต้องการเรียกใช้งานได้


1
นี่เป็นวิธีแก้ปัญหาเสียงที่ยอดเยี่ยมมาก ฉันมักจะพบว่าตัวเองแก้ไขประชาชนคำตอบอื่น ๆ || exit 1เพื่อเพิ่มสิ่งที่ต้องการ รู้สึกสดชื่นเมื่อได้เห็นสิ่งนี้ แม้ว่าฉันจะต้องสงสัยว่าทำไมคุณจะไม่ทำเพียงแค่cd /home/udi/foo/ && ./bar.py
Bruno Bronosky

2
@BrunoBronosky ด้วยความชัดเจนexit 1ของ crond ของคุณจะได้รับแจ้งข้อผิดพลาดและในกรณีส่วนใหญ่จะส่งการแจ้งเตือนทางอีเมลถึงความล้มเหลว
Ruud Althuizen
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.