iT邦幫忙

0

python Permission denied、PermissionError

  • 分享至 

  • xImage

程式目的:依圖片寬度,複製至不同目錄。
問題:
出錯1→PermissionError?(windows之目錄已設定為完全控制)
出錯2→Permission denied?(永遠無法複製檔案至目錄b1。(但b2、b3鐵定ok))
網上看了很多網頁,多半說是絕對路徑的問題,但是我是寫絕對路徑沒錯吧?
先謝謝大大指導。m(__)m


from PIL import Image as im 
import os, shutil as s

o = 'c:\\temp\\p454bu'
os.chdir(o)

for n in range(1, 4):
    k = "b"+str(n)
    if os.path.exists(k):
        s.rmtree(k)
    os.makedirs(k, exist_ok=True) 
    # 出錯1:PermissionError: [WinError 5] 存取被拒。: 'b2' ← 有時是b3

a, b, c = os.path.join(o, "b1"), os.path.join(o, "b2"), os.path.join(o, "b3")

for n in os.listdir(o): # listdir return relative path
    f = im.open(n)
    if f.size[0] < 500:
        # s.copy(n, b1) # shutil need absolute path
        s.copy(n, a) # 出錯2:[Errno 13] Permission denied: 'b1' ←永遠是b1出錯
    elif f.size[0] < 1000:
        s.copy(n, b)
    else:
        s.copy(n, c)
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中
0
ccutmis
iT邦高手 2 級 ‧ 2023-03-10 21:04:10
最佳解答
from PIL import Image as im 
import os, shutil as s

o = 'c:\\temp\\p454bu'
#刪除目錄
def delete_folder(dir_path):
    s.rmtree(dir_path, ignore_errors=True)
    print(f"\tDeleted '{dir_path}' successfully")

for n in [1,2,3]: delete_folder(f"{o}\\b{n}")

#創建目錄,如果目錄不存在
def mkdir_if_not_exists(out_dir):
    if not os.path.exists(out_dir):
        os.makedirs(out_dir)
        print(f"\t{out_dir} Created!")
    else:
        print(f"\t{out_dir} Existed!")

a,b,c = f"{o}\\b1",f"{o}\\b2",f"{o}\\b3"
mkdir_if_not_exists(a)
mkdir_if_not_exists(b)
mkdir_if_not_exists(c)

for n in os.listdir(o):
    print(n)
    if '.png' in n or '.jpg' in n:
        tmp_file_src = o+"\\"+n
        f = im.open(tmp_file_src)
        if f.size[0] < 500:
            s.copy(tmp_file_src, a)
        elif f.size[0] >= 500 and f.size[0] < 1000:
            s.copy(tmp_file_src, b)
        else: # f.size[0] >=1000
            s.copy(tmp_file_src, c)

直接給你一個範例參考,你遇到的問題可能是:

  1. 圖片路徑
  2. 目標資料夾可能不存在

建議多利用 Python 現有的函式/函式庫不要自己造輪子,比如說如果你要取得某資料夾裡面所有的圖片列表,照你原本的寫法,是包括資料夾b1,b2,b3都會列在n裡面,這時你用im.open()去處理它,不報錯才奇怪,我用的是最簡單的判斷n裡面是否有包含'.png'或'.jpg',確定是圖檔的才作判斷圖片尺寸並依width分資料夾複制,
在google搜尋'python img list in a folder'應該能找到不少現成的範例,不過那離題了在此打住。

另外你在寫分資料夾複制那邊的if ... elif ... 也有邏輯 bug,想想看~ if fs<500 ... elif fs <1000 ... 請問 fs若為 300 是不是<500 跟 <1000 都成立?

条件判断从上向下匹配,当满足条件时执行对应的块内语句,后续的elif和else都不再执行。
網路上查是這樣↑ 所以我一直誤以為300通過<500這關,就不會再進到下一關<1000了 = ="""
在os.listdir() 因為我先前錯誤的印象是它不理會目錄內的子目錄,實際看了執行結果,的確子目錄也列出來=會被處理到。我沒聯想到它會強碰「自身複製自身」的問題。
非常感謝您的指正,語法也很優秀,謝謝m(__)m

ccutmis iT邦高手 2 級 ‧ 2023-03-11 15:10:02 檢舉

不客氣~祝您學習順利!

0
re.Zero
iT邦研究生 5 級 ‧ 2023-03-11 16:25:51
  1. 資料夾刪改的操作時,建議避免以下(但不限於)類型的程式占用欲刪改的資料夾、子資料夾或子檔案:

    • Windows 檔案總管 之類的 檔案管理程式
    • Win-Cmd PowerShellTerminalConsole 類的程式或相關 script。

    例如開啟資料夾開啟檔案切換工作目錄(working directory)至相關資料夾等操作,有可能在檔案系統上造成安全性鎖定衝突之類的現象。

  2. file-open 操作時,建議:

    • 最小化 file-open 時的操作。
    • 養成關閉 file-open 的習慣。

    以降低在檔案系統上發生安全性鎖定衝突之類的現象。

  3. 建議避免或最小化切換工作目錄(working directory)的操作, 尤其是 常會忘記身在何處的人在有資料夾刪改的操作時函式或模組等程式具工作目錄依存性時 等情境下。
    或必要時,至少,處理完必要程序後馬上切換回安全的目錄。

  4. 根據這裡的敘述,if 陳述式的確是由上而下,執行首個運算式為真值的套組(suite)後,就跳離 if 陳述式了。
    所以我認為你的 if f.size[0] < 500: ## ... 陳述式的流程設計邏輯沒問題。
    除非在麻煩的環境如平行處理等情境下,不然我認為是沒必要採用 ccutmis 大提出的嚴謹用法;因為多個判斷就是多消耗系統時間。

  5. 其他的就請看後續的「小改版」程式碼內容。
    至於 Permission-whatever 原因我是懶得分析~就提供上述建議給你參考而已。


下面是基於你的程式碼的小改版:

## Tested @[Python 3.10];
from PIL import Image as im 
import os, shutil as s

o = 'c:\\temp\\p454bu'
os.chdir(o)

for n in range(1, 4):
    k = "b"+str(n)
    if os.path.exists(k):
        s.rmtree(k)
    os.makedirs(k, exist_ok=True)

a, b, c = os.path.join(o, "b1"), os.path.join(o, "b2"), os.path.join(o, "b3")

#for n in os.listdir(o): # listdir return relative path
for n in [ 
    ## os.listdir() 會列出所有物件的名稱: 不論是檔案(file)或目錄(directory)等物件,
    ## 所以用 os.path.isfile() 過濾出檔案的名稱;
    id for id in os.listdir(o) 
    if os.path.isfile(os.path.join(o, id))
]:
    #f = im.open(n)
    try: ## 用 try-except 敘述處理「無法判斷圖片格式的檔案」等例外情況;
        ## 用 with 敘述以在讀取尺寸後自動關閉檔案,避免鎖死等意外;
        with im.open(n) as f:
            w = f.size[0] ## get-img-width;
    except Exception as exc:
        print('□ Exception:[' + type(exc).__name__ + ']:', exc)
        continue
    if w < 500:
        # s.copy(n, b1) # shutil need absolute path
        #s.copy(n, 'b1') ## 這裡要用'字串' (或許是你一時寫錯?);
        ## 我試過 shutil.copy() 能用相對路徑,沒限定需使用絕對路徑;
        s.copy(n, a)
    elif w < 1000:
        s.copy(n, b)
    else:
        s.copy(n, c)

範例小改版: 絕對路徑/相對路徑 切換版:

## Tested @[Python 3.10];
from PIL import Image as im 
import os, shutil as s

## https://docs.python.org/zh-tw/3/tutorial/introduction.html?highlight=raw#strings
o = os.path.abspath(r'c:\temp\p454bu')
os.chdir(o)

myDirs = list() ## list for directory-path (or directory-name);
for n in range(3):
    ## https://docs.python.org/zh-tw/3/tutorial/inputoutput.html#tut-f-strings
    k = f'b{n+1}'
    if os.path.exists(k): s.rmtree(k)
    os.makedirs(k, exist_ok=True)
    match 0: ## aSwitch:(0|1): 切換用相對路徑(@0)或絕對路徑(@1);
        case 0: myDirs.append(k)
        case 1: myDirs.append(os.path.join(o, k))
for n, e in enumerate(myDirs): print(n, e, type(e))

for n in [
    id for id in os.listdir(o) 
    if os.path.isfile(os.path.join(o, id))
]:
    try:
        with im.open(n) as f: w = f.size[0]
    except Exception as exc:
        print(f'□ Exception:[{type(exc).__name__}]: {exc}')
        continue
    if 		w < 500 	: s.copy(n, myDirs[0])
    elif 	w < 1000 	: s.copy(n, myDirs[1])
    else 				: s.copy(n, myDirs[2])
## 

另,在「iT邦幫忙」的語法高亮 Markdown 的自動判斷常錯誤,建議用下面方式指定語言:

    #### this is a markdown script;
    #### next block is a python script;
    ```py
    print('hello, world')
    ```
看更多先前的回應...收起先前的回應...
ccutmis iT邦高手 2 級 ‧ 2023-03-11 18:56:21 檢舉

我只是拿以前寫別的程式的經驗來引用,因為它是 if ... elif ... else ... 這麼寫會有邏輯錯誤。你想想三選一的分支如果 w是一也符合二也符合,那它執行哪個分支才是正確的?

if w < 500 : ...
elif w < 1000 : ...
else : ...

如果他是:

if w < 500 : ...
if w < 1000 : ...

這樣就沒問題,每個 if 是各自獨立的。

re.Zero iT邦研究生 5 級 ‧ 2023-03-11 19:51:46 檢舉

@ccutmis:

在這段中:

if   w < 500  : print('RunA')
elif w < 1000 : print('RunB')
else : print('RunC')

w = 10 時,的確會使 w < 500w < 1000 這兩項為真。
但因為其 if-elif-else 執行流程的關係,僅會執行 w < 500 的內容。
這樣就算「w = 10 時會使 w < 500w < 1000 這兩項為真」也不會有問題啊。
對我來說,只要程式流程、結果符合邏輯與要求就好了,我不懂去探討「那它執行哪個分支才是正確的?」有啥意義?
倒是感覺你忽視 Python 的 if-elif-else 陳述式之執行流程的依序性。
還是其實 Python 的 if-elif-else 陳述式的執行流程是亂序?(大驚!)

ccutmis iT邦高手 2 級 ‧ 2023-03-11 20:03:46 檢舉

有啥意義? 其實這也只是個人編程習慣,畢竟嚴謹一點比凡事差不多好… 當然也可能是我被舊習慣誤導了,就像有的人廻圈堅持要用for i in range()嘛^^"

re.Zero iT邦研究生 5 級 ‧ 2023-03-11 20:31:14 檢舉

@ccutmis: 我也贊同嚴謹是好事~
就算是舊習慣也好,只要不會搞到畫地自限等詭異情況就好~
(我就有很多我自己都覺得詭異的習慣~)
畢竟在最佳化與除錯等作業之間,常常就是在嚴謹的思慮間徘徊、取捨。
每人的思慮、習慣不同,會有各種誤解很正常;我也偶爾會失智而一時各種神奇誤解~ XD

alien663 iT邦研究生 5 級 ‧ 2023-03-13 16:07:18 檢舉

我看這邊討論的還挺有趣的,分享一下我的看法,兩種寫法結果截然不同:
sample code 1:

w=30
a=b=c=1
if w < 500 : a=2
elif w < 1000 : b=2
else : c=3
print(a, b, c)

sample code 2:

w=30
a=b=1
if w < 500 : a=2
if w < 1000 : b=2
print(a, b)

sample code 1的狀況其實就等於以下狀況,以前看別人寫的C常常有這種寫法,可以少判斷一個w >= 500,是一種偷懶的寫法,但會嚴重增加程式碼閱讀困難所以不推薦。

if w < 500 : a=2
elif w >= 500 and w < 1000 : b=2
else : c=3
re.Zero iT邦研究生 5 級 ‧ 2023-03-13 17:44:35 檢舉

@ alien663:
為何要拿 C 的壞習慣與 Python 正常語法比較? 那是個人程式設計上的問題吧??

可以少判斷一個w >= 500,是一種偷懶的寫法,但會嚴重增加程式碼閱讀困難所以不推薦。

這反倒讓我閱讀困難……
因為我會用上 if-elif-else 語法,必然會考慮一整個流程。
用上兩個獨立的 if,就會把兩個流程分別考慮。
if-elif-else 流程上做多餘判斷,這反而讓我困惑……

alien663 iT邦研究生 5 級 ‧ 2023-03-14 10:18:27 檢舉

我想是我解釋不明確讓你誤會了,我指的是sample 1的寫法讓人閱讀困難,事實上我個人更偏好sample 1的等價寫法,跟你是一樣的觀點。
至於sample 2,變數b的結果可是被修改到了,跟sample 1的邏輯完全不一樣。
我個人不會說sample 2的寫法就是錯,雖然少見,但說不定需求就長那樣子。
我以前看別人寫的C語言有很多精妙設計,為了少宣告一個變數或是少判斷一個條件等等的,會有很多閱讀上會增加難度的寫法,只是作為一個分享而已。我認為現今如果不是在嵌入式系統上的話,簡單、清楚又明瞭的程式碼反而會是更重要的事情。

re.Zero iT邦研究生 5 級 ‧ 2023-03-14 19:44:16 檢舉

@alien663: 閱讀困難狀況是看個人啦,畢竟各人有各人的身心靈等各方面上的習慣;我也只是說我困擾的感想。
說到 C 語言的精妙, 我只怕不懂裝懂而亂用的同事(不是同事就沒差~ XD), 一整個恐怖。
我也喜歡簡單、清楚又明瞭的程式碼;但我,個人而言,比較看重程式註解的內容。
因為很多時候,我都會有「至少請他喵的讓我知道你到底想做啥好嗎?」這種感嘆~

0
jesse99
iT邦見習生 ‧ 2024-02-05 17:43:49

You can double-check directory permissions, run as administrator if needed, and adjust your wordle unlimited code to handle potential permission issues gracefully.

我要發表回答

立即登入回答