OpenCode et le PATH incomplet : pourquoi vos outils n'étaient pas dans le shell de l'agent
Δημοσιεύτηκε 21 April 2026
- Η σκηνή: ένας τυφλός πράκτορας σε ένα κόσμο εργαλείων
- Ανατομή του bug: δύο shells, δύο κόσμους
- Οι ένοχοι : .zshrc vs .zshenv
- Βήμα βήμα διάγνωση
- Η λύση : .zshenv + οι σωστοί διαδρομές
- Η περίπτωση NVM: όταν ο διαχειριστής εκδόσεων ξεχάνει να αφήσει ίχνος
- Πίνακας συνοπτικός των διαδρομών
- Παγίδες και μέτρα μείωσης
- Μάθηματα που μάθαμε
- τελική επαλήθευση
- Σύνδεσμοι
Εκκινείτε το OpenCode στο τερματικό σας, όλο λειτουργεί. Ο πράκτορας προσπαθεί ένα`gh`— δεν βρέθηκε. Ένας`java -version`— μη παρών. Ένας`node`— πουθενά. Ωθιμε, αυτά τα εργαλεία είναι εδώ στο σου κέλυφος. Το πρόβλημα; OpenCode εκκινεί κέλυφαμη-αλληλεπιδραστικάπου δεν διαβάζουν ποτέ το σας`.zshrc`. Αυτή είναι η διαδικασία για να το diagnosticάρεις και να το διορθώσεις σωστά.
Η σκηνή: ένας τυφλός πράκτορας σε ένα κόσμο εργαλείων
Ηταν μια Τρίτη το βράδυ. Έχα μόλις εγκαταστήσειhttps://opencode.ai[OpenCode], ο πράκτορας AI που υποσχήντηκε να μεταμορφώσει τον τρόπο μου να γράφω κώδικα. Πρώτος τεστ: του ζητώ να καταγράψει τα αποθετήριά μου στο GitHub.
$ opencode
> Utilise gh pour lister mes repos
❌ bash: gh: command not found
Ξένο.`gh`λειτουργούσε τέλεια στο τερματικό μου. Δοκιμάζω κάτι άλλο:
> Vérifie la version de Java
❌ bash: java: command not found
Τότε:
> Lance le build Gradle
❌ bash: gradle: command not found
Java, Gradle, Node,gh— τίποτα δεν ήταν ορατό για τον πράκτορα. Ο τερματικός μου, όμως, βλέπτε όλα. Ως να ζούμε ο πράκτορας και εγώ σε δύο παράλληλα κόσμους.
Έχρησα ώρες για να καταλάβω. Ωρες του`echo $PATH`, de which java, της αυξανόμενης απογοήveurσης. Τέλος κατάλαβα ότι το πρόβλημα δεν ήταν τα εργαλεία — ήταν τοκέλυφος. OpenCode, όπως κάθε παράγοντας που εκκινεί υποδιαδικασίες, δουλεύει σε κέλυφημη-αλληλεπιδραστικό. Και το Zsh, σε αυτά τα shell, αγνοεί απλώς και απλά το`.zshrc`.
Το εξής είναι η πλήρης περιγραφή αυτού του διαγνωστικού, της επίλυσης, και του μαθήματος που έμαθα. Αν χρησιμοποιείτε ένα AI-agent — OpenCode, Aider, Cursor, ή ακόμα και scripts cron — αυτό το πρόβλημα θα σε ασχοληθεί κάποια στιγμή.
Ανατομή του bug: δύο shells, δύο κόσμους
Όταν ανοίγετε ένα τερματικό, το Zsh το αντιμετωπίζει ως ένα κέλυφος.διαλογικός. Φορτώνει`.zshrc`, αρχικοποιεί tudo : SDKMAN, NVM, pnpm, οι ψευδωνύμες, το ωραίο σας prompt. Το περιβάλλον σας είναι ολοκληρωμένο.
Αλλά όταν το OpenCode εκτελεί μια εντολή, δεν ξεκινάει ένα διαλογικό τερματικό. Ξεκινάει ένα κέλυφοςμη-αλληλεπιδραστικό— ένα shell εργασίας, χωρίς άνθρωπο πίσω από την οθόνη. Και το Zsh, σε αυτό το πλαίσιο, σκαί`.zshrc`. Διαβάζει μόνο`.zshenv`.
Γιατί υπάρχει αυτή η διαφορά; Επειδή ένας μη διαλογικός shell είναι σχεδιασμένος να εκτελεί scripts, όχι για να εξυπηρετεί έναν ανθρώπινο χρήστη. Η φόρτωση των aliases, του prompt και των αυτοπληρώσεων σε ένα script που τρέχει στο background θα ήταν μια απώλεια. Το πρόβλημα είναι ότι το PATH σας — η πιο κρίσιμη πληροφορία για να βρείτε τα εκτελέσιμα — είναι συχνά αρχικοποιημένο σε`.zshrc`, δεν στο`.zshenv`.
@startuml skinparam backgroundColor #FEFEFE actor "Εσείς" as user actor "OpenCode\n(πράκτορας)" as agent participant "Διαδραστικό\nShell\n(με φόρτωση .zshrc)" as ishell participant "Μη διαδραστικό\nShell\n(αγνόηση .zshrc)" as nshell user -> ishell : gh, java, node ✅ agent -> nshell : gh, java, node ❌ note right of ishell Πηγή .zshrc : - SDKMAN init - NVM init - ~/apps στο PATH - pnpm στο PATH end note note right of nshell Το .zshrc αγνοήθηκε! PATH = /usr/bin:/bin + ορισμένοι προεπιλεγμένοι κατάλογοι end note @enduml
Η ρίζα του προβλήματος:Το Zsh κάνει source το αρχείο .zshrc μόνο για αλληλεπιδραστικά κέλυφη. OpenCode, όπως ο καθένας πράκτορας AI που εκκινεί υποδιαδικασίες, χρησιμοποιεί μη-αλληλεπιδραστικά κελύφη. Αυτά τα κελύφη διαβάζουν μόνο`.zshenv`.
Οι ένοχοι : .zshrc vs .zshenv
Το Zsh έχειτέσσερα αρχεία αρχικοποίησης, καθένας με έναν συγκεκριμένο ρόλο. Είναι ένα ελκυστικό σχέδιο — αλλά αυτό είναι και το κεντρό του προβλήματα:
| Αρχείο | προμηθευμένο όταν | Ρόλος | Αλλάξτε το PATH; |
|---|---|---|---|
|
πάντα(διαλογικό + μη διαλογικό + σύνδεση) |
Βασικές μεταβλητές περιβάλλοντος, PATH |
✅ Ναι — είναι η θέση του SA |
|
shells eisόdos uovo |
Αργές εντολές (μία φορά ανά periodo) |
Πιθανό |
|
Μόνο αλληλεπδραστικά shells |
Ψευδώνυμο, prompt, αυτόματη συμπλήρωση, διαλογικά εργαλεία |
❌ Όχι για τις κ riesi μ ε σ μεταβλητές |
|
Κελύφη σύνδεσης (μετά το .zshrc) |
Μηνύματα καλωσορίσματος, ολοκλήρωση |
σπάνια |
Ο πίνακας δίνει μια ένδειξη, αλλά πρέπει να τη καταλάβουμε βαθιά. Σκεφτείτε το ως τα δωμάτια ενός σπιτού :
-
`.zshenv`είναι οαίθουσα εισόδου— Όλοι περνάνε από εδώ, επισκέπτης ή κατοίκος. Αν βάλετε κάτι εδώ, όλο το shell θα το δει.
-
`.zshrc`είναι τοσαλόνι— μόνοι οι κάτοικοι (αξυμβάτα κελύφη) μπαίνουν εκεί. Οι επισκέπτες (μη αξυμβάτα κελύφη) μένουν στην αίθουσα.
-
.zprofileet `.zlogin`είναι εξειδικευμένα τμήματα για τα login shells (όπως όταν συνδέεστε μέσω SSH).
Το δράμα είναι ότι η Majority από εμάς βάζουμε το PATH στο σαλονί. Και ο παράγοντας IA, αυτός, δεν έχει ποτέ το δικαίωμα να μπεί εκεί.
Το πρόβλημα σε ένα διάγραμμα :
@startuml
skinparam backgroundColor #FEFEFE
start
if (Shell interactif ?) then (Oui)
:Source .zshenv;
:Source .zprofile;
:Source .zshrc;
:Source .zlogin;
note right
SDKMAN ✅
NVM ✅
~/apps ✅
pnpm ✅
end note
else (Non — shell non-interactif)
:Source .zshenv uniquement;
note right
SDKMAN ❌
NVM ❌
~/apps ❌
pnpm ❌
end note
endif
stop
@enduml
Όταν ανοίγετε ένα τερματικό, το Zsh είναι διαλογικό: διαβάζει`.zshrc`, όλα λειτουργούν. Όταν το OpenCode εκκινεί ένα shell για να εκτελέσει μια εντολή, το Zsh είναι μη-αλληλεπιδραστικό: το παραλείπει..zshrc, διαβάζει μόνο`.zshenv`. Et si `.zshenv`Δεν υπάρχει ή δεν περιέχει το PATH — είναι η έρημη.
|
Γιατί το Zsh κάνει αυτό;Αυτή είναι μια επιλογή σχεδιασμού που κληρονομήθηκε από το Unix. Ένας μη διαλογιστικός shell πρέπει να είναιγρήγορος et αναπαραγώσιμος. Η φόρτωση των ψευδώνυμων, των χρωματικών prompts και των βαρείων αρχικοποιήσεων του SDKMAN σε ένα script cron ή ένα πρόγραμμα AI θα ήταν αργό και αδύναμο. Η διαχωριστική είναι άρα λογική:`.zshenv`για το ουσιώδες (μεταβλητές, PATH),`.zshrc`για τον άνεση (aliases, prompt, complétons). Το πρόβλημα εμφανίζεται όταν βάζουμε το βασικό στον άνεση. |
Βήμα βήμα διάγνωση
Πριν από τη διόρθωση, πρέπει να κατανοήσετε ακριβώς τι λείπεται. Αυτή είναι μια μέθοδος επαναληψιμαίου διαγνωστικού — κρατήστε την στα αγαπημένα αν εργάζεστε με πρόξενες ΤΝ.
1. Ελέγξτε το PATH του αjánτη
Συγκρίνουμε τι βλέπει το διαλογικό τερματικό σας με τι βλέπει ένα μη διαλογικό shell — δηλαδή τι βλέπει το OpenCode.
# Dans votre terminal interactif (tout fonctionne)
echo $PATH | tr ':' '\n' | grep -v '^/usr' | sort
Τυπικό αποτέλεσμα :
/home/cheroliv/apps
/home/cheroliv/.nvm/versions/node/v22.19.0/bin
/home/cheroliv/.sdkman/candidates/java/current/bin
/home/cheroliv/.sdkman/candidates/gradle/current/bin
/home/cheroliv/.local/share/pnpm
/home/cheroliv/.local/bin
Τώρα, ας προσομοιούμε τι βλέπει ο πράκτορας — ένα μη αλληλεπιδραστικό κέλυφος :
# Shell non-interactif : pas de .zshrc
zsh -c 'echo $PATH' | tr ':' '\n' | grep -v '^/usr' | sort
Αποτέλεσμα :
/home/cheroliv/.local/bin
Πέντε από έξι δρόμοι έχουν εξαφανιστεί.Ο πράκτορας είναι αποκομμένος 83% από το περιβάλλον του. Θα ήταν σαν να σας ζητούσαμε να μαγειρέψετε χωρίς το μισό σας τα σκεύη — θα μπορούσατε να βράσετε νερό, αλλά όχι και κάτι άλλο.
|
Η παραγγελία`zsh -c 'echo $PATH'`είναι το σαςδιαγνωστικό εργαλείο αριθμός 1. Αν ένα εργαλείο που χρησιμοποιείτε καθημερινά λείπει από το αποτέλεσμα, ο agentes σας AI δεν θα μπορούσε επίσης να το δει. Δοκιμάστε την πριν και μετά από κάθε αλλαγή de`.zshenv`. |
2. Να εντοπίσετε τι είναι στο .zshrc αλλά όχι στο `.zshenv
Τώρα, ψάχνουμε τον ένοχο στο`.zshrc`. Φιλτράρουμε τις γραμμές που αναφέρουν τα εργαλεία μας:
grep -n 'apps\|SDKMAN\|NVM\|PNPM\|PATH' ~/.zshrc
Συνήθως βρίσκουμε :
119: PATH="/usr/bin/python3:$HOME/apps:$PATH" # ← pas exporté !
171: export NVM_DIR="$HOME/.nvm"
172: [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # ← NVM init
176: nvm use --lts --silent # ← active une version
179: export PNPM_HOME="/home/cheroliv/.local/share/pnpm"
187: export SDKMAN_DIR="$HOME/.sdkman"
188: [[ -s "$HOME/.sdkman/bin/sdkman-init.sh" ]] && source "$HOME/.sdkman/bin/sdkman-init.sh"
Όλα είναιανόρατοςγια OpenCode. Και ο`PATH`της γραμμής 119 δεν είναι καν`export`é — δεν φεύγει ποτέ από το τρέχον shell. Είναι μια κρίσιμη τεχνική λεπτομέρεια: μια μεταβλητή χωρίς`export`Αυτό παραμένει τοπικό στον κέλυφος που το καθορίζει. Τα υπο-κέλυφα — όπως αυτά που εκκινεί το OpenCode — δεν το κληρονομούν ποτέ.
3. Ελέγξτε αν το .zshenv υπάρχει
cat ~/.zshenv 2>/dev/null || echo "FICHIER ABSENT"
Αν η απάντηση είναι « ΑΡΧΕΙΟ ΛΕΙΠΕΙ », εκεί είναι όπου όλα παίζουν.
Η λύση : .zshenv + οι σωστοί διαδρομές
Η στρατηγική είναι απλή, αλλά ζητάει ακρίβεια : να το βάλει μέσα`.zshenv` μόνοτα βασικά διαδρόμια, χωρίς να φορτώνουμε τα βαρέα scripts αρχικοποίησης. Δεν μετακινούμε`.zshrc`σε`.zshenv`— εξαγούμε το ουσιώδες.
Δημιουργήστε .zshenv με όλες τις απαραίτητες διαδρομές
`.zshenv`είναι ομόνο αρχείοότι το Zsh εγγυάται να πηγαίνει μέσαόλοιΟι contextes. Εδώ είναι όπου πρέπει να πάει ο PATH.
export SDKMAN_DIR="$HOME/.sdkman"
export NVM_DIR="$HOME/.nvm"
export PNPM_HOME="$HOME/.local/share/pnpm"
export PATH="$HOME/apps:$HOME/.sdkman/candidates/java/current/bin:$HOME/.sdkman/candidates/gradle/current/bin:$HOME/.nvm/current/bin:$PNPM_HOME:$HOME/.local/bin:$HOME/.local/share/JetBrains/Toolbox/scripts:/usr/bin/python3:$PATH"
|
χρ ε σ ι ο π ο ι → ή τ ε ι`$HOME/.nvm/current/bin`και όχι`$HOME/.nvm/versions/node/v22.19.0/bin`. Ο λόγος: Οι έκδοσεις του Node αλλάζουν. Ένας σκληροκωδικοποιημένος δρόμος γίνεται λάθος στον επόμενο. |
Γιαติ όχι source sdkman-init.sh στο .zshenv ?
Η πιο ελκυστική προσέγγιση θα ήταν απλώς να το αναπαραγάγουμε σε`.zshenv`το που κάνουμε στο`.zshrc` — sourcer les scripts d’initialisation. On θα μπορούσενα είσαι πειρασμένο να κάνεις:
# ❌ MAUVAISE IDÉE
[[ -s "$HOME/.sdkman/bin/sdkman-init.sh" ]] && source "$HOME/.sdkman/bin/sdkman-init.sh"
Προβλήματα:
-
Αργός:`sdkman-init.sh`κάνει διαδικτυακές αναλογισμούς και ελέγχους σε κάθε κέλυφος. Σε ένα μη διαλογικό κέλυφος, αυτό είναι ένα περιττευτικό κόστος.
-
Ευπτορός: Η SDKMAN αναμένει έναν αλληλεπιδραστικό πλαίσιο. Η αρχικοποίηση της μπορεί να αποτύχει σιωπηλώς σε έναν αγωγό ή έναν υπο-κέλυφος.
-
αχρήσιμο: SDKMAN τοποθετεί τους υποψηφίους του σε`~/.sdkman/candidates/<tool>/current/bin`— σταθερά symlinks που δείχνουν προς την ενεργούσα έκδοση. Μπορούμε να τα χρησιμοποιήσουμεευθείως.
Η σωστή προσέγγιση: παρακάμπτουν την αρχικοποίηση του SDKMAN και δείχνουν άμεσα στους συμβολικούς σύνδεσμους`current`. Το κλειδί όλης της λύσης:χρησιμοποιήστε τη δομή των αρχείων ως σύμβαση, αντί για τον κώδικα αρχικοποίησης.
@startuml
skinparam backgroundColor #FEFEFE
rectangle "Approche LENTE\n(source sdkman-init.sh)" as slow {
card ".zshenv source sdkman-init.sh\n2-3 secondes par shell\nVérifications réseau\nRisque d'échec" as s1 #FDEDEC
}
rectangle "Approche RAPIDE\n(symlinks directs)" as fast {
card ".zshenv export PATH=...current/bin\n0 ms\nPas de réseau\nPas d'initialisation" as s2 #E8F8E8
}
slow --> fast : Même résultat final\nLe PATH pointe sur current/bin\ndans les deux cas
@enduml
Η περίπτωση NVM: όταν ο διαχειριστής εκδόσεων ξεχάνει να αφήσει ίχνος
Το πρόβλημα NVM
Αυτό είναι όπου η έρευνα με έφερε πιο μακριά. Ο SDKMAN έχει ένα ελεγαντο σχεδιασμό : όταν κάνουμε`sdk install java 25.0.2-tem`, δημιουργεί έναν συμβολικό σύνδεσμο :
~/.sdkman/candidates/java/current -> ~/.sdkman/candidates/java/25.0.2-tem
Αυτό το symlink είναιπάντα ενημερωμένο. δείξτε το σε`.zshenv`είστε καλυμμένοι, όσο και να είναι το shell. Beautiful.
NVM, αυτόν, δεν oluşturείτίποτατου τηλεφώνου. Δεν υπάρχει συμβολικός δεσμός`current`. Δεν υπάρχει σταθερό σημείο άγκυρας. Πρέπει να δείξουμε σε μια στιβαρή διαδρομή με έκδοση, όπως αυτό :
~/.nvm/versions/node/v22.19.0/bin/node
Στον επόμενο`nvm install 24`, αυτός ο δρόμος είναι νεκρός. δικό σας`.zshenv`θα δείχνει σε μια έκδοση που δεν είναι πια η ενεργή έκδοση. Είναι μια βόμβα με καθυστέρηση.
|
Γιατί το NVM κάνει αυτό;NVM λειτουργεί τροποποιώντας δυναμικά το PATH σε κάθε`nvm use`. Το είναι σχεδιασμένο για προγραμματιστές που αλλάχουν συχνά έκδοση, σε ένα αλληλεπδραστικό κέλυφος. Το συμβολικό σύνδεσμο`current`Δεν था στις αρχικές προδιαγραφές — είναι ένα έλλειμμα σχεδιασμού που θα το διόρθώσουμε εμείς. |
@startuml
skinparam backgroundColor #FEFEFE
package "SDKMAN ✅" {
[~/.sdkman/candidates/java/current] as sdk_current
[~/.sdkman/candidates/java/25.0.2-tem/] as java_25
[~/.sdkman/candidates/java/21.0.7-tem/] as java_21
sdk_current --> java_25 : symlink
}
package "NVM ❌ (πριν τη διόρθωση)" as nvm_before {
[~/.nvm/versions/node/v22.19.0/] as node_22
[~/.nvm/versions/node/v20.16.0/] as node_20
note right of node_22
Δεν υπάρχει symlink current!
Το `.zshenv` πρέπει να δείχνει απευθείας
Θα σπάσει στο επόμενο nvm install
end note
}
package "NVM ✅ (μετά τη διόρθωση)" as nvm_after {
[~/.nvm/current] as nvm_current
[~/.nvm/versions/node/v22.19.0/] as node_22b
[~/.nvm/versions/node/v20.16.0/] as node_20b
nvm_current --> node_22b : symlink\n(mis à jour auto)
}
@enduml
Η λύση: δημιουργήστε το symlink current για το NVM
Επειδή το NVM δεν το κάνει, το κάνουμε εμείς. Η αρχή είναι η ίδια με το SDKMAN — ένας συμβολικός σύνδεσμος.`current`που πάντα δείχνει στην ενεργό έκδοση. Το δημιουργούμε μια φορά, στη συνέχεια το αυτοματοποιούμε ώστε να ενημερώνεται αυτόματα.
# Créer le symlink initial
ln -sfn "$HOME/.nvm/versions/node/v22.19.0" "$HOME/.nvm/current"
Τώρα, στο`.zshenv`, χρησιμοποιείται :
$HOME/.nvm/current/bin
Αντί να :
# ❌ Chemin en dur — cassé au prochain changement de version
$HOME/.nvm/versions/node/v22.19.0/bin
Αυτοματοποίηση της ενημέρωσης του symlink
Ένας συμβολικός σύνδεσμος δεν αξίζει τίποτα αν δεν ενημερώνεται. Ο συμβολικός σύνδεσμος`current`πρέπει να ενημερώνεται όταν κάνεις`nvm use` ou nvm install. Η λύση: έναεμπακέτο— μια συνάρτηση που περικλείει την πραγματική εντολή`nvm`και ενημερώνει το συμβολικό σύνδεσμο μετά από κάθε κλήση.
# À la fin de .zshrc, APRÈS le chargement de NVM
nvm use --lts --silent
ln -sfn "$(nvm_version_path "$(nvm current)")" "$NVM_DIR/current"
_nvm() {
command nvm "$@"
local rc=$?
ln -sfn "$(nvm_version_path "$(nvm current)")" "$NVM_DIR/current"
return $rc
}
alias nvm='_nvm'
Πώς λειτουργεί, λεπτομερώς:
@startuml skinparam backgroundColor #FEFEFE actor Développeur participant "nvm wrapper\n(_nvm)" as wrapper participant "πραγματικό nvm\n(εντολή nvm)" as realnvm participant "~/.nvm/current\n(συμβολικός σύνδεσμος)" as symlink Développeur -> wrapper : nvm use 20 wrapper -> realnvm : command nvm use 20 realnvm --> wrapper : version activée wrapper -> symlink : ln -sfn .../v20.16.0 ~/.nvm/current wrapper --> Développeur : retour note right of symlink Πάντα ενημερωμένο! Χρησιμοποιείται από το .zshenv → προσβάσιμο από το OpenCode end note @enduml
ο wrapper`_nvm`καλεί την πραγματική εντολή`nvm`, και έπειτα ενημερώνει τον συμβολικό σύνδεσμο. Το ψευδώνυμο`nvm='_nvm'`Κάνε ώστε όταν πληκτρολογείς`nvm`, περνάμε μέσω του wrapper. Και στην αρχικοποίηση του shell, κάνουμε τα ίδια έπειτα`nvm use --lts`.
|
`nvm_version_path`είναι μια εσωτερική συνάρτηση του NVM που λύνει το πλήρες μονοπάτι μιας έκδοσης. Αποτρέπει την ανάγκη να αναπαιράτε το μονοπάτι με τον χέρι. |
Πίνακας συνοπτικός των διαδρομών
Πριν μεταβείτε στα παγίδια, μια οπτική σύνοψη της μετασχηματισμού. Στο αριστερό μέρος, αυτό που είχατε (όλων στο`.zshrc`, αόρατο για τον πράκτορα). στα δεξιά, το που έχετε τώρα (οι βασικοί δρόμοι στο`.zshenv`, ορατές παντού).
| Εργαλείο | Πριν (.zshrc μόνο) | Μετά (.zshenv + symlink) | Ορατό από το OpenCode ? |
|---|---|---|---|
|
PATH δεν εξάγεται στο .zshrc |
`$HOME/apps`sto .zshenv |
✅ |
SDKMAN Java |
`sdkman-init.sh`φορτωμένο από .zshrc |
`$HOME/.sdkman/candidates/java/current/bin`σε το .zshenv |
✅ |
SDKMAN Gradle |
`sdkman-init.sh`φορτωμένο στο .zshrc |
`$HOME/.sdkman/candidates/gradle/current/bin`σε .zshenv |
✅ |
NVM Node |
`nvm use --lts`σε το .zshrc |
`$HOME/.nvm/current/bin`σε .zshenv (σύνδεσμος) |
✅ |
pnpm |
`$PNPM_HOME`σε .zshrc |
`$PNPM_HOME`σε .zshenv |
✅ |
JetBrains Toolbox |
Προστέθηκε αυτόματα από το Toolbox στο .zshrc |
`$HOME/.local/share/JetBrains/Toolbox/scripts`σε .zshenv |
✅ |
Python 3 |
`/usr/bin/python3`στο PATH .zshrc |
Εκφραστικά στο .zshenv |
✅ |
Παγίδες και μέτρα μείωσης
Δεν είναι τέλεια αυτή η προσέγγιση. Αυτά είναι τα προβλήματα που συνάντησα, και πώς να τα αποφύγω.
| Παγίδα | Περιγραφή | μείωση |
|---|---|---|
PATH διπλωμένο |
Αν το .zshenv και το .zshrc προσθέσουν την ίδια διαδρομή, εμφανίζεται δύο φορές. |
|
NVM : σκληρά κωδικοποιημένη έκδοση στο .zshenv |
να βάλω`~/.nvm/versions/node/v22.19.0/bin`Το σκληρόκωδικό γίνεται ψευδές στον επόmeno`nvm install` |
Χρησιμοποίησε το symlink`$HOME/.nvm/current/bin`+ το wrapper`_nvm`στο .zshrc |
SDKMAN : πηγα sdkman-init.sh στο .zshenv |
Αργός, ευαίσθητος, άχρηστος σε ένα μη διαλογικό κέλυφος |
Χρησιμοποιείτε τα symlinks`candidates/<tool>/current/bin`ευθεία |
Λείπων ψευδώνυμο μετά την τροποποίηση |
Το περιβλήμα`nvm='_nvm'`στο .zshrc δεν παίρνει εφαρμογή μέχρι να φορτωθεί ξανά |
Επανεκκίνηση του κελυφού ή`source ~/.zshrc` |
Το OpenCode δεν βλέπει τις αλλαγές |
Ο πράκτορας έχει ήδη εκκινήσει τα υπο-κέλυθρα του με το παλαιό .zshenv |
Επανεκκίνησε το OpenCode μετά την τροποποίηση του .zshenv |
.zshenv υπερφορτωμένο |
Βάλτε βαρείς λειτουργίες ή αλληλεπδραστικές αρχικοποιήσεις στο .zshenv |
.zshenv = μεταβλητές περιβάλλοντος + μόνο PATH. Δεν`source`, χωρίς βαρείς λειτουργίες, χωρίς προτροπές. |
Μάθηματα που μάθαμε
-
.zshrc` είναι διαλογικό,
.zshenvείναι καθολικό— Αν μια μεταβλητή πρέπει να υπάρχει σε όλους τους κελύφη (πρόξενες AI, cron, σενάρια, IDE), αυτή πάει σε`.zshenv`. Το σαλόνι είναι άνετο, αλλά το έσοδος είναι το μοναδικό μέρος όπου όλοι περνούν. -
Οι διαχειριστές εκδόσεων δεν είναι ίσοι— SDKMAN δημιουργεί symlinks`current`κατὰ σχέδιο. NVM όχι. Πρέπει να γεμίσουμε αυτό το έλλειμμα χειροκίνητα. Αυτό είναι ένα σημαντικό μάθημα: πριν ρυθμίσετε τη μεταβίβαση PATH σας, ελέγξτε αν ο διαχειριστής σας προσφέρει ένα σταθερό σημείο αγκύρωσης.
-
Μην φορτώσετε τα σενάρια αρχικοποίησης στο .zshenv—
sdkman-init.shet `nvm.sh`είναι σχεδιασμένοι για έναν αλληλεπιδραστικό shell. Είναι αργοί και απρόσμενες σε ένα μη-αλληλεπιδραστικό περιβάλλον. Τα symlinks`current`αρκούν και είναι άμεσα. -
Πάντα να δοκιμάζετε σε μη-διαλογικό shell—`zsh -c 'echo $PATH'`Προσομοιώνει ακριβώς τι βλέπει ο πράκτορας. Αυτό είναι ο έλεγχος επικύρωσης. Χωρίς αυτόν τον έλεγχο, δεν ξέρετε αν η ρύθμιση σας λειτουργεί για τους πράκτορες.
-
Το μοτίβο wrapper είναι επαναχρησιμοποιήσιμο— Το ίδιο μοτίβο`_nvm`+`alias nvm='_nvm'`Εφαρμόζεται σε κάθε εργαλείο που τροποποιεί το PATH δυναμικά, χωρίς να αφήνει μια σταθερή ίχνη. Είναι ένα εργαλείο περισσότερο στο κουτί σου των ιδεών.
@startuml
skinparam backgroundColor #FEFEFE
rectangle "ΠΡΙΝ" as avant {
card "Διαδραστικό shell : ✅\nΜη διαδραστικό shell : ❌\nOpenCode : ❌\nCron : ❌\nScripts : ❌" as av1 #FDEDEC
}
rectangle "ΜΕΤΆ" as apres {
card "Διαδραστικό shell : ✅\nΜη διαδραστικό shell : ✅\nOpenCode : ✅\nCron : ✅\nScripts : ✅" as ap1 #E8F8E8
}
avant --> apres : .zshenv +\nsymlink ~/.nvm/current
@enduml
τελική επαλήθευση
Μετά που το δημιούργησε`.zshenv`και το symlink NVM, επαληθεύστε ότι όλα λειτουργούν — και στους δύο περιπτώσεις :
# Shell interactif (votre terminal)
gh --version && java -version && node --version
# Shell non-interactif (simulation OpenCode)
zsh -c 'gh --version && java -version && node --version'
Οι δύο πρέπει να επιτυχουν. Αν ναι, οπρόσωπος AI σας θα βλέπει τα ίδια εργαλεία που βλέπετε εσείς. Αν όχι, επιστρέψτε στο βήμα-βήμα διαγνωστικό — πιθανότατα ξεχάσατε ένα δρόμο ή ο symlink NVM δεν είναι ενημερωμένος.
|
Αυτοματοποιήστε αυτό το τεστ.Προσθέστε αυτόν τον έλεγχο σε ένα script healthcheck που τρέχετε μετά από κάθε ενημέρωση των εργαλείων σας. Ένας`zsh -c 'which java && which node && which gh'`Στην CI, είναι μια ασφάλιση εναντίον των εκπλήψεων. |
_ Ένας μη αλληλεπιδραστικός shell είναι σαν έναν σιωπηλό κλήρο: διαβάζει μόνο αυτό που εμφανίζεται στην πόρτα της εισόδου. Αν το PATH βρίσκεται στο σαλόνι, το δεν θα το βλέπει ποτέ. _
Σύνδεσμοι
Επίσημη τεκμηρίωση
-
Τεκμηρίωση Zsh : Αρχεία εκκίνησης— Η επίσημη αναφορά για τα αρχεία αρχικοποίησης του Zsh. Εκεί εξηγείται όλο, ακόμα κι αν το ξεχάσουμε συχνά.
-
OpenCode : ρύθμιση— Πώς να ρυθμίσετε το OpenCode και το περιβάλλον εκτέλεσης του;
Εργαλεία που αναφέρθηκαν
-
NVM στο GitHub— Node Version Manager. Διαχειριστής εκδόσεων Node.js.
-
SDKMAN — επίσημη ιστοσελίδα— SDKMAN! Ο διαχειριστής εκδόσεων για τη JVM (Java, Kotlin, Gradle…)
-
SDKMAN : εγκατάσταση— Οδηγός εγκατάστασης του SDKMAN.
-
gh` — GitHub CLI— Το εργαλείο γραμμής εντολών GitHub, απαραίτητο για κάθε προγραμματιστή.
-
Gradle — επίσημη ιστοσελίδα— Το σύστημα κατασκευής που εγκαθιστάμε μέσω του SDKMAN.
-
pnpm — επίσημη ιστοσελίδα— Ο διαχειριστής πακέτων Node.js γρήγορος και αποδοτικός στο χώρο.
-
JetBrains Toolbox— Ο διαχειριστής IDE JetBrains, που προσθέτει τα σενάρια του στο PATH.
Για να πάτε περαιτέρω
-
Zsh dotfiles : ένας πλήρης οδηγός— Βαθύ άρθρο για τη διαχείριση των dotfiles Zsh.
-
Zsh στο ArchWiki— Μια από τις καλύτερες τεκμηριώσεις της κοινότητας για το Zsh.
-
NVM issues GitHub— Για να δείτε τις συζητήσεις γύρω από το symlink`current`και προβλήματα με το PATH.