Ви запускаєте скрипт. Файл ніби прямо тут. Ви можете ls його, доповнювати табом, дивитися на нього, ніби він вам винен.
А WSL відповідає: Немає такого файлу або каталогу.
Ця помилка — улюблений фокус WSL: вона змушує реальний файл «зникнути», атакуючи шлях на іншому рівні, ніж той, який ви переглядаєте.
Виправлення рідко полягає в «створенні файлу». Виправлення — у розумінні, в якій ви файловій системі, який синтаксис шляху застосовується і який транслятор мовчки переписує ваші припущення.
Справжня проблема: ваш шлях дійсний… але в іншому всесвіті
WSL — це не стільки «Linux на Windows», скільки «Linux поруч із Windows з пактом». Цей пакт включає трансляцію шляхів, шари монтування,
семантику імен файлів та міжплатформений міст, який намагається бути корисним, поки раптом таким не стає.
Коли WSL каже «Немає такого файлу або каталогу», це може означати:
- Файл існує, але інтерпретатор, вказаний у шебензі, не існує (або його не вдається знайти).
- Файл існує, але ви на
/mnt/c, і відображення прав/метаданих робить його фактично невиконуваним. - Файл існує, але шлях містить проблеми з екрануванням у Windows (пробіли, дужки, зворотні слеші), а ваш shell вас обманює.
- Файл існує, але це CRLF, тож шлях інтерпретатора насправді
/bin/bash\r, якого справді не існує. - Файл існує, але ви змішали UNC-шляхи, симліни або поведінку регістру, і опинилися не там.
- Файл існує, але ви викликаєте його з процесу Windows, який «допомагає», переписуючи аргументи.
Ваше завдання — визначити, який рівень породжує «відсутність» файлу. Не вгадуйте. Діагностуйте.
Швидкий план діагностики
Ось порядок перевірок, який я використовую, коли на виклику повідомляють «pipeline падає у WSL, немає файлу, файл є».
Він оптимізований для швидкого знаходження кореня, а не для навчання Linux-фундаментам.
Перш за все: це проблема шляху чи інтерпретатора?
- Якщо це скрипт: перевірте шебенг і кінці рядків (
head -n1,file). - Якщо це бінарник: перевірте архітектуру та завантажувач (
file,ldd).
Друге: де саме розташований файл?
/home(ext4 всередині WSL2 VHDX) поводиться як Linux./mnt/c(DrvFs) поводиться як «Linux, що говорить з NTFS через сумісний шар».- Мережеві шейри додають ще один шар сумісності. Саме там радості йдуть на пенсію.
Третє: хто кого викликає?
- Linux → Linux зазвичай адекватно.
- Linux → Windows EXE через інтероп має підводні камені з цитуванням/шляхами.
- Windows → WSL (
wsl.exe) може переписувати робочий каталог і аргументи.
Четверте: перевірте точні байти шляху
- Шукайте приховані символи: CR, неперервні пропуски, «розумні» лапки, нормалізацію Unicode.
- Підтвердіть, що запис каталогу існує і доступний (
stat), а не лише виводиться в списку.
П’яте: визначте опції монтування та поведінку метаданих
- Опції монтування DrvFs вирішують, чи означає щось
chmod +x. - Чутливість до регістру може бути на рівні директорії в NTFS; поведінка може відрізнятися між машинами.
Виконуйте завдання в наступному розділі в порядку, поки щось не виглядатиме неправильно. Воно виглядатиме. Саме для цього це й написано.
Модель шляхів WSL, пояснено як для продакшену
WSL2 дає реальне ядро Linux — і потім підсовує купу трансляторів
У WSL2 ваша дистрибуція живе на ext4-файловій системі всередині віртуального диска (VHDX). Це «реальний Linux».
Ваші диски Windows робляться видимими в Linux через DrvFs-монти, зазвичай під /mnt/c, /mnt/d тощо.
Ці два світи відрізняються в способах, що важливі для «файлу не знайдено»:
- Синтаксис шляху: Linux використовує
/, Windows —\та літери дисків. - Поведінка регістру: Linux чутливий до регістру; Windows історично — ні (але можна ввімкнути на рівні директорії).
- Семантика виконуваності: Linux використовує біти режиму та інтерпретатори; Windows — розширення файлів і PE-заголовки.
- Символьні посилання: Linux-симлінки рідні; Windows-симлінки — це… політичне питання.
Коли WSL каже, що не може знайти файл, це може означати, що він не може знайти запис каталогу — або не може його виконати
Linux має просту, але жорстку таксономію помилок. Кілька різних проблем зливаються в схожі повідомлення:
ENOENT: немає такого файлу або каталогу (також викидається, коли інтерпретатор або динамічний завантажувач відсутні).EACCES: доступ заборонено (часто саме те, що ви хотіли б побачити, але не побачили).ENOTDIR: компонент шляху не був директорією (може статися через дивні цілі симлінків).
WSL додає вигин: шари трансляції можуть повертати ENOENT, коли Linux-запит просить того, що Windows-сторона не може представити.
Результат — технічно коректна, але операційно дратівлива помилка.
Інтероп потужний, але це мінне поле цитування й робочих каталогів
WSL дозволяє запускати Windows-екзешки з Linux і Linux-команди з Windows. Це добре, поки ви не передасте:
- Шляхи з пробілами або дужками
- Шляхи, що виглядають як опції (провідний
-) - UNC-шляхи (
\\server\share) - Аргументи зі зворотними слешами, які ваш shell «допомагає» інтерпретувати
З точки зору інцидентів: інтероп збільшує вашу «зону ураження». Зручно в розробці; крихко в автоматизації, якщо ви не дисципліновані.
Цікаві факти та історія (бо минуле досі ламає збірку)
- Факт 1: WSL1 транслював виклики системи Linux в виклики Windows. WSL2 перейшов на реальне ядро Linux у легковаговій VM, що радикально змінило компроміси продуктивності файлової системи.
- Факт 2: За замовчуванням монти дисків Windows живуть під
/mnt, бо ранній WSL намагався нагадувати типовий Linux-монтовий вигляд без колізій з директоріями дистрибуції. - Факт 3: NTFS історично поводився нечутливо до регістру, але зберігав регістр; сучасний Windows може ввімкнути чутливість до регістру на рівні директорії, і це робить поведінку у крос-інструментах… гострою.
- Факт 4: Класична проблема CRLF передує WSL на десятиліття; це артефакт терміналів і інструментів від DOS/Windows, що зустріли Unix-конвенції тексту.
- Факт 5: Linux повідомляє ENOENT не тільки коли відсутній цільовий файл, а й коли не вдається знайти ELF-интерпретатор або динамічний завантажувач для бінарника.
- Факт 6: Windows-шляхи легально можуть містити символи, які синтаксично важливі для shell (пробіли, дужки, амперсанд). У shell-пайплайні це вже не «просто шлях», це інцидент.
- Факт 7: Відображення прав DrvFs раніше було джерелом плутанини; опції монтування на кшталт
metadataіснують саме для збереження Linux-бітів режиму у розширених атрибутах NTFS. - Факт 8: Налаштування Git для закінчень рядків (autocrlf) — одна з найпоширеніших причин, чому скрипт стає «невиконуваним» у Linux, хоч у Windows-редакторах виглядає нормально.
- Факт 9:
wsl.exeзмінював поведінку між релізами Windows щодо робочого каталогу за замовчуванням і прапорців трансляції шляхів; скрипти, що припускають поведінку однієї версії, можуть падати в іншій.
Практичні завдання: команди, виходи та що вони означають
Нижче — реальні діагностики, які я застосовую. Кожне завдання містить (1) команду, (2) що ви можете побачити, і (3) рішення, яке ви приймаєте.
Не запускайте їх усіх бездумно у продакшен-пайплайні — запускайте у середовищі, де відтворюється помилка.
Завдання 1: Підтвердіть, WSL1 чи WSL2 (це змінює поведінку файлової системи)
cr0x@server:~$ uname -a
Linux server 5.15.146.1-microsoft-standard-WSL2 #1 SMP Fri Jan 12 18:22:05 UTC 2024 x86_64 GNU/Linux
Значення: microsoft-standard-WSL2 вказує на WSL2. Якщо бачите WSL1 — семантика трансляції та файлової системи відрізнятиметься.
Рішення: Якщо це WSL1, очікуйте більше дивних випадків трансляції системних викликів; якщо WSL2 — зосередьтеся на DrvFs-монтах і перехресній поведінці метаданих/продуктивності.
Завдання 2: Визначте, де файл реально знаходиться (Linux ext4 vs Windows-диск)
cr0x@server:~$ df -T /home /mnt/c | sed -n '1,3p'
Filesystem Type 1K-blocks Used Available Use% Mounted on
/dev/sdc ext4 263174212 93481244 156278972 38% /
C:\ 9p 976762876 512331244 464431632 53% /mnt/c
Значення: ext4 — це WSL2 VHDX-файлова система; 9p тут позначає канал монту Windows-диска.
Рішення: Якщо проблемний файл під /mnt/c, ставтеся до нього як до «чужого». Переносьте артефакти збірки/запуску в $HOME, якщо хочете менше несподіванок.
Завдання 3: Перевірте, чи шлях не брешеться (stat не бреше, як ваш термінал)
cr0x@server:~$ stat "/mnt/c/Users/alex/Projects/demo/run.sh"
File: /mnt/c/Users/alex/Projects/demo/run.sh
Size: 231 Blocks: 0 IO Block: 4096 regular file
Device: 33h/51d Inode: 0 Links: 1
Access: (0777/-rwxrwxrwx) Uid: ( 1000/ cr0x) Gid: ( 1000/ cr0x)
Access: 2026-02-05 11:34:40.000000000 +0000
Modify: 2026-02-05 11:34:39.000000000 +0000
Change: 2026-02-05 11:34:39.000000000 +0000
Значення: Якщо stat завершується з ENOENT, з Linux-перспективи шлях дійсно недоступний. Якщо вдається — файл існує; виконання все ще може впасти з інших причин.
Рішення: Якщо stat працює, але виконання падає з «Немає такого файлу або каталогу», переходьте негайно до перевірки shebang і кінців рядків.
Завдання 4: Якщо виконання скрипта падає — перевірте перший рядок (shebang)
cr0x@server:~$ head -n1 /mnt/c/Users/alex/Projects/demo/run.sh | cat -A
#!/bin/bash^M$
Значення: Те ^M — це повернення каретки. Шлях інтерпретатора фактично /bin/bash\r, якого не існує.
Рішення: Конвертуйте кінці рядків у LF (dos2unix) або налаштуйте Git/редактор, щоб він не повертав CRLF.
Завдання 5: Підтвердіть кінці рядків і тип файлу за допомогою file
cr0x@server:~$ file /mnt/c/Users/alex/Projects/demo/run.sh
/mnt/c/Users/alex/Projects/demo/run.sh: Bourne-Again shell script, ASCII text, with CRLF line terminators
Значення: Це класичний режим «є, але не можна запустити».
Рішення: Виправте кінці рядків і зафіксуйте зміни, інакше доведеться періодично їх фіксити знову.
Завдання 6: Конвертуйте CRLF у LF безпечно (і перевірте)
cr0x@server:~$ dos2unix /mnt/c/Users/alex/Projects/demo/run.sh
dos2unix: converting file /mnt/c/Users/alex/Projects/demo/run.sh to Unix format...
cr0x@server:~$ file /mnt/c/Users/alex/Projects/demo/run.sh
/mnt/c/Users/alex/Projects/demo/run.sh: Bourne-Again shell script, ASCII text
Значення: Шлях інтерпретатора тепер чистий.
Рішення: Якщо скрипт іще падає, перевірте біт виконання та опції монтування наступними кроками.
Завдання 7: Перевірте права на виконання та поведінку метаданих монтування
cr0x@server:~$ ls -l /mnt/c/Users/alex/Projects/demo/run.sh
-rwxrwxrwx 1 cr0x cr0x 231 Feb 5 11:34 /mnt/c/Users/alex/Projects/demo/run.sh
Значення: На DrvFs без metadata права можуть бути синтетичними; усе може виглядати виконуваним, хоча політика каже інакше. Або навпаки: ви робите chmod +x, і він не зберігається.
Рішення: Якщо біти виконання не зберігаються або поведінка різниться між машинами, перевірте /etc/wsl.conf і поточні опції монтування.
Завдання 8: Перегляньте опції монтування DrvFs (корінь багатьох «вчора працювало» випадків)
cr0x@server:~$ mount | grep -E ' /mnt/c '
C:\ on /mnt/c type 9p (rw,relatime,dirsync,aname=drvfs;path=C:\;uid=1000;gid=1000;metadata;umask=22;fmask=11;case=off)
Значення: metadata присутній, отже біт режиму Linux може зберігатися в розширених атрибутах NTFS. case=off вказує на нечутливу до регістру поведінку.
Рішення: Якщо metadata відсутній і ви залежите від бітів виконання або симлінків — увімкніть його в /etc/wsl.conf і перезапустіть WSL.
Завдання 9: Підтвердіть існування інтерпретатора, вказаного в shebang (це не завжди /bin/bash)
cr0x@server:~$ head -n1 ./run.sh
#!/usr/bin/env bash
cr0x@server:~$ command -v env
/usr/bin/env
Значення: Використання /usr/bin/env портативне, але лише якщо env існує (зазвичай існує) і цільовий інтерпретатор є в PATH.
Рішення: Якщо command -v bash не знаходить bash — встановіть його або виправте PATH. Якщо env заражений CRLF — ви повертаєтесь до Завдання 4.
Завдання 10: Якщо бінарник падає з ENOENT — перевірте динамічний завантажувач/архітектуру
cr0x@server:~$ file ./tool
./tool: ELF 64-bit LSB pie executable, x86-64, dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, not stripped
cr0x@server:~$ ldd ./tool | sed -n '1,6p'
linux-vdso.so.1 (0x00007ffc6c5f8000)
libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007fda1b0d0000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fda1aeb0000)
/lib64/ld-linux-x86-64.so.2 (0x00007fda1b2f0000)
Значення: Якщо шлях інтерпретатора в file вказує на відсутнє, виконання бінарника може дати «Немає такого файлу або каталогу».
Рішення: Якщо ldd показує «not found» для бібліотеки — встановіть залежності. Якщо шлях інтерпретатора відсутній, ймовірно, ви запускаєте бінар для іншої дистрибуції/архітектури.
Завдання 11: Підтвердіть поточний робочий каталог і розв’язання симлінків
cr0x@server:~$ pwd -P
/mnt/c/Users/alex/Projects/demo
cr0x@server:~$ readlink -f .
/mnt/c/Users/alex/Projects/demo
Значення: Якщо ваше туло залежить від симлінків або відносних шляхів, інший pwd, ніж ви очікували, може зламати складання шляхів.
Рішення: В автоматизації одразу розв’язуйте абсолютні шляхи і логгируйте їх. Якщо ви на /mnt/c, подумайте про переміщення виконання у файлову систему Linux.
Завдання 12: Перевірте «розумні лапки» Windows або Unicode-особливості в іменах файлів
cr0x@server:~$ ls | cat -A
run.sh$
deploy–prod.sh$
Значення: Це ім’я файлу містить en dash (–), а не дефіс (-). Ваша скопійована команда використовує -, тож файл «відсутній».
Рішення: Перейменуйте файл у ASCII-безпечні імена або принаймні припиніть копіювати з форматованого тексту. Автоматизація ненавидить типографію.
Завдання 13: Протестуйте виклик з Windows у WSL та трансляцію шляхів явно
cr0x@server:~$ wsl.exe -e bash -lc 'pwd; ls -l ./run.sh'
/mnt/c/Users/alex/Projects/demo
-rwxrwxrwx 1 cr0x cr0x 231 Feb 5 11:34 ./run.sh
Значення: Коли Windows запускає WSL, робочий каталог може бути не тим, що ваш скрипт очікує, якщо ви його явно не встановите.
Рішення: У CI або Task Scheduler завжди встановлюйте робочий каталог явно або передавайте абсолютні шляхи.
Завдання 14: Перетворіть Windows-шляхи в WSL-шляхи (і перевірте)
cr0x@server:~$ wslpath "C:\Users\alex\Projects\demo\run.sh"
/mnt/c/Users/alex/Projects/demo/run.sh
cr0x@server:~$ test -e "$(wslpath 'C:\Users\alex\Projects\demo\run.sh')" && echo OK || echo MISSING
OK
Значення: wslpath — найменш поганий спосіб транслювати шляхи, особливо коли викликають із контекстів Windows.
Рішення: Стандартизуйте використання хелперів для трансляції шляхів у скриптах замість ручних замін рядків.
Завдання 15: Діагностуйте проблеми з цитуванням для пробілів і дужок (класична інтероп-пастка)
cr0x@server:~$ p="/mnt/c/Program Files (x86)/Vendor Tool/bin/tool.exe"; test -e "$p" && echo EXISTS || echo MISSING
EXISTS
cr0x@server:~$ "$p" --version
VendorTool 4.2.1
Значення: Якщо не цитувати — shell розіб’є рядок по пробілах, а дужки стануть синтаксисом. Тоді «Немає такого файлу» — найбільш м’яка реакція.
Рішення: Котируйте кожен шлях. На code review ставте некотовані шляхи в один ряд із некоректним SQL: не завжди експлуатовано, але завжди ризиковано.
Завдання 16: Перевірте /etc/wsl.conf на конфігурацію монтування (той самий тихий політичний файл)
cr0x@server:~$ sed -n '1,120p' /etc/wsl.conf
[automount]
enabled = true
options = "metadata,umask=22,fmask=11"
mountFsTab = false
Значення: Це керує тим, як монтуються диски Windows. metadata — головна опція для прав; umask/fmask впливають на за замовчуванням режими.
Рішення: Якщо це відсутнє або непослідовне між машинами — уніфікуйте. Розбіжності тут породжують «працює на моєму ноуті» як сервіс.
Типові помилки: симптом → корінна причина → виправлення
1) Симптом: «Немає такого файлу або каталогу» при запуску скрипта, який очевидно існує
Корінна причина: CRLF у шебензі, що призводить до /bin/bash^M.
Виправлення: Конвертуйте в LF (dos2unix file), примусьте LF у Git attributes і налаштуйте редактори, щоб вони не «допомагали».
2) Симптом: «Немає такого файлу або каталогу» лише при виконанні з Windows (Task Scheduler, VS Code task, CI runner)
Корінна причина: Неправильний робочий каталог або зміни в цитуванні аргументів при перетині кордону Windows↔WSL.
Виправлення: Використовуйте wsl.exe -e bash -lc з явним cd, передавайте абсолютні шляхи і транслюйте Windows-шляхи через wslpath.
3) Симптом: chmod +x «працює», але виконання все одно поводиться непослідовно на /mnt/c
Корінна причина: Опції монтування DrvFs без metadata (або непослідовна політика між машинами).
Виправлення: Налаштуйте /etc/wsl.conf для ввімкнення metadata; перезапустіть WSL. Переважно запускайте збірки на файловій системі Linux ($HOME), коли це можливо.
4) Симптом: Файл існує, але ./tool повертає «Немає такого файлу або каталогу»
Корінна причина: Відсутній динамічний завантажувач або залежність спільної бібліотеки; ENOENT може бути викинутий через відсутній ELF-інтерпретатор.
Виправлення: file tool і ldd tool. Встановіть потрібні бібліотеки або перебудуйте під цільове середовище/дистро WSL.
5) Симптом: Шлях працює в одному репозиторії, але не в іншому; такий самий файл, різна поведінка
Корінна причина: Невідповідність чутливості до регістру. Одна директорія може бути чутливою на NTFS; інша — ні. Або інструменти використовують різні регістри в імпортах/шляхах.
Виправлення: Нормалізуйте регістр у коді та скриптах збірки. Уникайте покладання на нечутливість до регістру. Обробляйте репозиторії на Windows як джерело хаосу регістру, поки не доведено протилежне.
6) Симптом: «Немає такого файлу або каталогу» зі шляхами, що містять Unicode-дефіси або «розумні» лапки
Корінна причина: Байти імені файлу не співпадають з тим, що ви вводите. Гарна пунктуація виглядає однаково, поки не зіпсує вам вечір.
Виправлення: Перейменуйте файли на ASCII-безпечні імена або копіюйте точне ім’я з ls. Для автоматизації краще використовувати машино-генеровані шляхи.
7) Симптом: Скрипти падають лише на мережевих шейрах, змонтованих у Windows-диски
Корінна причина: Додатковий шар трансляції (SMB/CIFS, Windows-редиректор, потім DrvFs). Права й семантика змін файлів можуть відрізнятися.
Виправлення: Копіюйте або синхронізуйте на файлову систему Linux перед виконанням; уникайте виконання скриптів прямо з мережевих шейрів.
8) Симптом: «Немає такого файлу або каталогу» при використанні відносних шляхів у makefile або npm-скриптах
Корінна причина: Інструмент змінює робочий каталог всередині, або ви припустили, що $PWD стабільний при викликах через інтероп.
Виправлення: Розв’язуйте шляхи за допомогою виявлення кореня репозиторію, використовуйте абсолютні шляхи і логгируйте їх. У shell-скриптах: script_dir="$(cd "$(dirname "$0")" && pwd -P)".
Три корпоративні міні-історії з шляхових окопів
Міні-історія 1: Інцидент через неправильне припущення
Середня компанія мала Windows-ґрунтований CI-раннер, який виконував кроки збірки Linux через WSL.
Все працювало місяцями, бо команда рідко чіпала образи раннера.
Потім з’явився новий репозиторій з «простим» bootstrap-скриптом, який закомітив хтось, хто жив у Windows-редакторах.
Скрипт був. Логи пайплайна показували його в списку директорії. Але кожна спроба запустити його закінчувалась:
«Немає такого файлу або каталогу.» Перший респондент зробив те, що роблять перші респонденти: повторив job. Те ж саме.
Потім спробували абсолютні шляхи. Те ж саме. Потім копіювали файл туди-сюди, як пересаджували вазон, щоб перевірити, чи перестане він в’янути.
Неправильне припущення було тонким: всі думали, що ENOENT означає «файл відсутній». Файл не був відсутній.
Шлях інтерпретатора був. CRLF у shebang перетворив /usr/bin/env у /usr/bin/env\r.
Ядро не могло знайти цей інтерпретатор, тому повідомило ENOENT при виконанні скрипта.
Вони виправили це через примусову перевірку кінців рядків на межі репозиторію, а не словесні попередження розробникам.
Передкомітний хук допоміг, але справжній виграш — крок CI, що швидко падав, якщо шебенг містив CR.
Це перетворило привидну помилку на зрозумілу.
Постмортем-урок: нічого не припускайте про «файл не знайдено», поки не перевірили рядок інтерпретатора.
Комп’ютери буквальні. Люди оптимістичні. Оптимізм не проходить збірки.
Міні-історія 2: Оптимізація, що відкотилася назад
Інша команда хотіла прискорити WSL-середовище. Вони тримали репо на /mnt/c,
щоб Windows-інструменти (індексатори IDE, виключення антивірусу, file watcher’и) мали до нього доступ.
Але збірки були повільні, тож вони зробили «розумно»: запускали компіляцію в WSL, а артефакти зберігали на /mnt/c,
щоб Windows міг їх забирати без копіювань.
Перший тиждень виглядав чудово. Деякі робочі навантаження стали швидшими, здебільшого через кеші та рідкі крайні випадки.
Потім кандидат у реліз провалився при пакуванні. Бандлер скаржився, що згенерований скрипт відсутній.
Скрипт був, але періодично «не знаходився» під час паралельних кроків.
Відкот не був містичним. Вони поклали високо-паралельне навантаження на файловий міст, що не поводився як рідний ext4.
Оновлення метаданих, нотифікації файлів і семантика перейменувань під паралелізмом відкрили тимчасові вікна.
Логіка повторної спроби в пайплайні лише маскувала ранні симптоми.
Вони виправили це нудним, але правильним рішенням: перемістили репо й результати збірки у файлову систему Linux ($HOME)
і копіювали фінальні артефакти у Windows лише за потреби. Продуктивність стала стабільною. Збої перестали бути «періодичними».
Урок: «менше копій» не завжди означає «швидше». Іноді це просто «ближче до дивної частини системи».
Міні-історія 3: Сумна, але правильна практика, що врятувала день
Регульоване підприємство мало змішаний парк: дехто використовував WSL, дехто — Linux-ноутбуки, CI працював на Linux.
Їхня модель загроз і вимоги аудиту робили їх нетерпимими до «чарів розробника». Це було дратівливо, але змусило дисципліну.
Вони стандартизували невеликий набір точок входу збірки зі строгими правилами:
скрипти повинні використовувати #!/usr/bin/env bash, бути в LF, не припускати розкладку /bin і логгувати свої розв’язані шляхи.
Кожен репо мав перевірку коректності шляхів, що працювала менш ніж за секунду.
Якось, оновлення Windows змінило щось дрібне у способі запуску WSL з IDE розробника.
Відносний шлях допоміжного скрипта зламався — лише на машині цього розробника.
Перевірка коректності одразу вивела: «Робочий каталог /mnt/c/…; очікувався корінь репо. Перериваюся.»
Вони не втратили півдня на тривожні пошуки. Вони не «виправили» це перевстановленням.
Вони змінили конфігурацію IDE, щоб робочий каталог явно задавався і рухались далі.
Урок: явні робочі каталоги і рання валідація нудні. Ось чому вони працюють.
Чеклісти / покроковий план
Чекліст A: Коли «Немає такого файлу або каталогу» при виконанні скрипта
- Запустіть
stat "path". Якщо він падає — це справді проблема шляху. Якщо ні — продовжуйте. - Запустіть
head -n1 file | cat -A. Якщо бачите^M— виправте кінці рядків. - Запустіть
file file. Підтвердіть, що це скрипт з LF-термінаторами. - Переконайтеся, що інтерпретатор існує:
command -v bashабоls -l /bin/bash. - Перевірте біт виконання:
ls -l file. Якщо на/mnt/c, підтвердіть, що опції монтування включаютьmetadata, якщо ви на нього спираєтесь. - Якщо викликається з Windows — явно задайте робочий каталог і передавайте абсолютні шляхи.
Чекліст B: Коли «Немає такого файлу або каталогу» при виконанні бінарника
stat ./binaryіls -l, щоб переконатися, що він існує і має права на виконання.file ./binary, щоб підтвердити архітектуру і шлях інтерпретатора.ldd ./binaryі шукайте «not found».- Якщо інтерпретатор відсутній — ймовірно, бінар зібрано для іншої дистрибуції/середовища; перебудуйте у WSL або підберіть образ.
- Перенесіть бінар на файлову систему Linux, якщо він на
/mnt/cі поведінка нестабільна.
Чекліст C: Уніфікуйте монти WSL, щоб зменшити флаттер шляхів
- Створіть/перевірте
/etc/wsl.confз опціями automount, відповідними для вашої організації. - Вирішіть, чи потрібне вам
metadata. Якщо ви виконуєте скрипти з/mnt/c, ймовірно, так. - Перезапустіть WSL (закрийте дистрибуції, потім перезапустіть службу WSL з боку Windows).
- Задокументуйте єдине «благословенне» місце для репозиторіїв: або файлову систему Linux для збірки/запуску, або Windows-файлову систему для редагування — але не прикидайтеся, що вони однакові.
Одна цитата, бо ми не тут, щоб шпалерами покривати проблему мудрими плакатами:
«Hope is not a strategy.» — Gene Kranz
Жарт 1: Баги з шляхами в WSL — як шкарпетки в сушці: зрештою все зникає, і ніхто не визнає, що тиснув налаштування.
FAQ
1) Чому Linux каже «Немає такого файлу або каталогу», коли файл існує?
Тому що ENOENT може стосуватися відсутнього інтерпретатора (shebang), відсутнього динамічного завантажувача для ELF-бінарника або відсутнього компонента шляху після трансляції.
У WSL шари трансляції і поведінка монту додають ще способів породити ENOENT.
2) Як швидко визначити, чи проблема в CRLF?
Запустіть head -n1 script | cat -A і дивіться на ^M, або file script і перевірте «with CRLF line terminators».
3) Чи погано тримати код під /mnt/c?
Це не моральне питання. Це операційний ризик для Linux-орієнтованих робочих навантажень. Якщо ви компілюєте, виконуєте багато дрібних файлових операцій або залежите від Unix-прав — віддавайте перевагу $HOME (файлова система Linux).
Тримайте /mnt/c для сумісності й обміну файлами, а не як рушійну силу виконання.
4) Чому в терміналі WSL працює, а при запуску з Windows — не працює?
Різний робочий каталог, різні змінні середовища, різні правила цитування і інколи різна трансляція шляхів.
Запускачі Windows часто стартують WSL у каталозі за замовчуванням, якого ви не очікували.
5) Який найнадійніший спосіб передавати Windows-шлях у WSL?
Використовуйте wslpath для трансляції і потім цитуйте результат. Не замінюйте вручну \ на / і не вважайте це інженерією.
6) Чому бінарники інколи падають з «Немає такого файлу», але ls їх показує?
Відсутній ELF-інтерпретатор або спільні бібліотеки можуть кинути ENOENT при exec. Перевірте file на шлях інтерпретатора і ldd на відсутні залежності.
7) Чи справді опції монтування мають значення для цієї помилки?
Так. Опції монтування впливають на відображення прав і збереження метаданих. Без metadata ви отримаєте конфузні «виглядає виконуваним» випадки, що не відповідають вашим намірам.
Це не завжди прямий причинник ENOENT, але часто сприяє ланцюжку плутанини.
8) Чому шляхи з пробілами частіше ламаються у WSL?
Вони не «ламаться в WSL». Вони ламаються у shell’ах і на межі інтеропу, де правила цитування відрізняються.
Якщо цитувати кожен шлях — ви драматично зменшите кількість помилок.
9) Як припинити це відбуватися в команді?
Примусьте LF у репозиторіях, додайте швидку перевірку, що валідовує shebang і очікування шляхів, стандартизуйтe конфіг WSL-монту і виберіть одне «місце для запуску» (файлова система Linux).
Також: логгируйте абсолютні шляхи в скриптах збірки. Дебаг без шляхів — як інцидент-реакція без часових міток.
10) WSL — це проблема чи мої скрипти?
Зазвичай ваші скрипти. WSL просто дає більше способів випадково поєднати припущення Windows і семантику Linux в одному командному рядку.
Сприймайте межу як мережевий хоп: валідируйте, нормалізуйте і логгируйте.
Жарт 2: Якщо ваша збірка залежить від некотованих шляхів, у вас не пайплайн — у вас детективний роман.
Наступні кроки, які ви реально можете зробити цього тижня
- Додайте перевірку shebang і кінців рядків: крок CI, що відхиляє CRLF у виконуваних скриптах і виводить проблемний рядок.
- Виберіть політику «де запускається збірка»: або у файловій системі Linux (
$HOME), або у Windows-файловій системі; задокументуйте компроміси й забезпечте послідовність. - Уніфікуйте
/etc/wsl.conf: впевніться, що опції монту (особливоmetadata) послідовні на машинах розробників, якщо ви виконуєте з/mnt/c. - Зробіть шляхи нудними: уникайте Unicode-пунктуації і покладайтеся на ASCII-безпечні імена для критичних файлів автоматизації.
- Логгуйте абсолютні шляхи: кожна точка входу збірки має надрукувати
pwd -Pі розв’язану директорію скрипта перед виконанням будь-яких дій.
Повідомлення «Немає такого файлу або каталогу» не є неправильним. Воно просто не каже, який саме файл воно не може знайти.
Коли ви почнете перевіряти інтерпретатори, типи монту і межі інтеропу в цьому порядку — помилка перестане бути містерією і стане пунктом у чеклісті.