Je crée une application NW.js sur Mac et je souhaite exécuter l'application en mode dev en double-cliquant sur une icône. Première étape, j'essaie de faire fonctionner mon script Shell.
En utilisant VSCode sous Windows (je voulais gagner du temps), j'ai créé un run-nw
fichier à la racine de mon projet, contenant ceci:
#!/bin/bash
cd "src"
npm install
cd ..
./tools/nwjs-sdk-v0.17.3-osx-x64/nwjs.app/Contents/MacOS/nwjs "src" &
mais j'obtiens cette sortie:
$ sh ./run-nw
: command not found
: No such file or directory
: command not found
: No such file or directory
Usage: npm <command>
where <command> is one of: (snip commands list)
(snip npm help)
[email protected] /usr/local/lib/node_modules/npm
: command not found
: No such file or directory
: command not found
Je ne comprends vraiment pas:
\r\n
avec \n
(dans le cas où le \r
crée des problèmes) mais cela ne change rien.dirname
), ou peut-être qu'il ne connaît pas la commande cd
?install
à npm
npm install
manuellement)...Incapable de le faire fonctionner correctement, et soupçonnant quelque chose de bizarre avec le fichier lui-même, j'en ai créé un nouveau directement sur le Mac, en utilisant vim cette fois. J'ai entré exactement les mêmes instructions, et ... maintenant cela fonctionne sans aucun problème.
Un diff sur les deux fichiers révèle une différence exactement nulle.
Quelle peut être la différence? Qu'est-ce qui peut empêcher le premier script de fonctionner? Comment le savoir?
En suivant les recommandations de la réponse acceptée, après le retour des mauvaises fins de ligne, j'ai vérifié plusieurs choses. Il s'avère que depuis que j'ai copié mon ~/.gitconfig
depuis ma machine Windows, j'avais autocrlf=true
, donc chaque fois que je modifiais le fichier bash sous Windows, il redéfinissait les fins de ligne sur \r\n
.
Donc, en plus d'exécuter dos2unix (que vous devrez installer avec Homebrew sur mac), si vous utilisez Git, vérifiez votre configuration.
Oui. Les scripts bash sont sensibles aux fins de ligne, à la fois dans le script lui-même et dans les données qu'il traite. Ils doivent avoir des fins de ligne de style Unix, c'est-à-dire que chaque ligne se termine par un caractère de saut de ligne (décimal 10, hex 0A en ASCII).
Avec les fins de ligne de type Windows ou DOS, chaque ligne se termine par un retour chariot suivi d'un caractère de saut de ligne. Si un fichier de script a été enregistré avec des fins de ligne Windows, Bash voit le fichier comme
#!/bin/bash^M
^M
cd "src"^M
npm install^M
^M
cd ..^M
./tools/nwjs-sdk-v0.17.3-osx-x64/nwjs.app/Contents/MacOS/nwjs "src" &^M
Remarque: J'ai utilisé la notation caret pour représenter les caractères non imprimables, c'est-à-dire ^M
est utilisé pour représenter les caractères de retour chariot (représentés par \r
dans d'autres contextes); il s'agit de la même technique utilisée par cat -v
et Vim.
Dans ce cas, le retour chariot (^M
ou \r
) n'est pas traité comme un espace. Bash interprète la première ligne après le Shebang (consistant en un seul caractère de retour chariot) comme le nom d'une commande/d'un programme à exécuter.
^M
, il imprime : command not found
"src"^M
(ou src^M
), il imprime : No such file or directory
install^M
au lieu de install
comme argument de npm
, ce qui fait que npm
se plaint.Comme ci-dessus, si vous avez un fichier d'entrée avec des retours chariot:
hello^M
world^M
alors il semblera tout à fait normal dans les éditeurs et lors de l'écriture à l'écran, mais les outils peuvent produire des résultats étranges. Par exemple, grep
ne trouvera pas de lignes qui sont évidemment là:
$ grep 'hello$' file.txt || grep -x "hello" file.txt
(no match because the line actually ends in ^M)
Le texte ajouté remplacera plutôt la ligne car le retour chariot déplace le curseur au début de la ligne:
$ sed -e 's/$/!/' file.txt
!Ello
!orld
La comparaison des chaînes semblera échouer, même si les chaînes semblent être les mêmes lors de l'écriture sur l'écran:
$ a="hello"; read b < file.txt
$ if [[ "$a" = "$b" ]]
then echo "Variables are equal."
else echo "Sorry, $a is not equal to $b"
fi
Sorry, hello is not equal to hello
La solution consiste à convertir le fichier pour utiliser des fins de ligne de style Unix. Il existe plusieurs façons de procéder:
Cela peut être fait en utilisant le dos2unix
programme:
dos2unix filename
Ouvrez le fichier dans un éditeur de texte capable (Sublime, Notepad ++, not Notepad) et configurez-le pour enregistrer des fichiers avec des fins de ligne Unix, par exemple, avec Vim, exécutez la commande suivante avant de (re) sauvegarder:
:set fileformat=unix
Si vous disposez d'une version de l'utilitaire sed
qui prend en charge -i
ou --in-place
option, par exemple, GNU sed
, vous pouvez exécuter la commande suivante pour supprimer les retours chariot finaux:
sed -i 's/\r$//' filename
Avec d'autres versions de sed
, vous pouvez utiliser la redirection de sortie pour écrire dans un nouveau fichier. Veillez à utiliser un nom de fichier différent pour la cible de redirection (il peut être renommé ultérieurement).
sed 's/\r$//' filename > filename.unix
De même, le filtre de traduction tr
peut être utilisé pour supprimer les caractères indésirables de son entrée:
tr -d '\r' <filename >filename.unix
Avec le port Bash pour Cygwin, il existe une option igncr
personnalisée qui peut être définie pour ignorer les retours de ligne en fin de ligne (probablement parce que beaucoup de ses utilisateurs utilisent des programmes Windows natifs pour modifier leurs fichiers texte). Ceci peut être activé pour le shell actuel en exécutant set -o igncr
.
La définition de cette option s'applique uniquement au processus shell en cours , ce qui peut être utile lorsque sourcing fichiers avec retours de chariot superflus. Si vous rencontrez régulièrement des scripts Shell avec des fins de ligne DOS et que vous souhaitez que cette option soit définie de manière permanente, vous pouvez définir une variable d'environnement appelée SHELLOPTS
(toutes les majuscules) pour inclure igncr
. Cette variable d'environnement est utilisée par Bash pour définir les options du shell au démarrage (avant de lire les fichiers de démarrage).
L'utilitaire file
est utile pour voir rapidement quelles fins de ligne sont utilisées dans un fichier texte. Voici ce qu'il imprime pour chaque type de fichier:
Bourne-Again Shell script, ASCII text executable
Bourne-Again Shell script, ASCII text executable, with CR line terminators
Bourne-Again Shell script, ASCII text executable, with CRLF line terminators
La version GNU de l'utilitaire cat
a un -v, --show-nonprinting
option qui affiche les caractères non imprimables.
Le dos2unix
L'utilitaire est spécialement conçu pour convertir des fichiers texte entre les fins de ligne Unix, Mac et DOS.
Wikipedia a un excellent article couvrant les nombreuses façons différentes de marquer la fin d'une ligne de texte, l'historique de ces encodages et la façon dont les nouvelles lignes sont traitées dans différents systèmes d'exploitation, langages de programmation et protocoles Internet (par exemple, FTP).
Avec Mac OS classique (pré-OS X), chaque ligne se terminait par un retour chariot (décimal 13, hex 0D en ASCII). Si un fichier de script était enregistré avec de telles fins de ligne, Bash ne verrait qu'une seule longue ligne comme ceci:
#!/bin/bash^M^Mcd "src"^Mnpm install^M^Mcd ..^M./tools/nwjs-sdk-v0.17.3-osx-x64/nwjs.app/Contents/MacOS/nwjs "src" &^M
Puisque cette longue ligne unique commence par un octothorpe (#
), Bash traite la ligne (et le fichier entier) comme un seul commentaire.
Remarque: En 2001, Apple a lancé Mac OS X qui était basé sur le système d'exploitation dérivé de BSD NeXTSTEP . Par conséquent, OS X utilise également LF de style Unix -seulement les fins de ligne et depuis lors, les fichiers texte terminés par un CR sont devenus extrêmement rares. Néanmoins, je pense qu'il vaut la peine de montrer comment Bash tenterait d'interpréter de tels fichiers.
Une autre façon de se débarrasser du caractère CR ('\ r') indésirable est d'exécuter la commande tr
, par exemple:
$ tr -d '\r' < dosScript.py > nixScript.py
Venant d'un doublon, si le problème est que vous avez des fichiers dont names contiennent ^M
à la fin, vous pouvez les renommer avec
for f in *$'\r'; do
mv "$f" "${f%$'\r'}"
done
Vous voulez corriger correctement ce qui a causé la rupture de ces noms en premier lieu (probablement un script qui les a créés devrait être dos2unix
ed puis relancez?) mais parfois ce n'est pas possible.
La manière la plus simple sur MAC/Linux - créez un fichier en utilisant la commande 'touch', ouvrez ce fichier avec VI ou VIM, collez votre code et enregistrez. Cela supprimerait automatiquement les caractères Windows.