Envoi d'arguments de ligne de commande au script npm

819

La scriptspartie de mon package.jsonressemble actuellement à ceci:

"scripts": {
    "start": "node ./script.js server"
}

... ce qui signifie que je peux exécuter npm startpour démarrer le serveur. Jusqu'ici tout va bien.

Cependant, je voudrais pouvoir exécuter quelque chose comme npm start 8080et avoir les arguments passés script.js(par exemple npm start 8080=> node ./script.js server 8080). Est-ce possible?

arnemart
la source

Réponses:

1132

Edit 2014.10.30: Il est possible de passer des arguments à npm runpartir de npm 2.0.0

La syntaxe est la suivante:

npm run <command> [-- <args>]

Notez le nécessaire --. Il est nécessaire de séparer les paramètres passés à la npmcommande elle-même et les paramètres passés à votre script.

Donc, si vous avez package.json

"scripts": {
    "grunt": "grunt",
    "server": "node server.js"
}

Les commandes suivantes seraient alors équivalentes:

grunt task:target => npm run grunt -- task:target

node server.js --port=1337 => npm run server -- --port=1337

Pour obtenir la valeur du paramètre, consultez cette question . Pour lire les paramètres nommés, il est probablement préférable d'utiliser une bibliothèque d'analyse comme yargs ou minimist ; nodejs expose process.argvglobalement, contenant des valeurs de paramètres de ligne de commande, mais il s'agit d'une API de bas niveau (tableau de chaînes séparées par des espaces, tel que fourni par le système d'exploitation à l'exécutable du nœud).


Edit 2013.10.03: Ce n'est actuellement pas possible directement. Mais un problèmenpm lié à GitHub est ouvert pour implémenter le comportement que vous demandez. Il semble que le consensus est de mettre cela en œuvre, mais cela dépend d'un autre problème résolu auparavant.


Réponse originale: En guise de solution de contournement (mais pas très pratique), vous pouvez procéder comme suit:

Dites que le nom de votre colis package.jsonest myPackageet vous avez également

"scripts": {
    "start": "node ./script.js server"
}

Ajoutez ensuite package.json:

"config": {
    "myPort": "8080"
}

Et dans votre script.js:

// defaulting to 8080 in case if script invoked not via "npm run-script" but directly
var port = process.env.npm_package_config_myPort || 8080

De cette façon, par défaut npm startutilisera 8080. Vous pouvez cependant le configurer (la valeur sera stockée par npmdans sa mémoire interne):

npm config set myPackage:myPort 9090

Ensuite, lors de l'appel npm start, 9090 sera utilisé (la valeur par défaut de package.jsonest remplacée).

jakub.g
la source
1
Cela fonctionne également parfaitement avec des packages comme yargs; tous les paramètres après le --peuvent être parfaitement analysés dans votre script.
Thomas
11
AFAIKS, cela ne permet que d'ajouter des paramètres à la fin de vos scripts. Et si vous avez besoin de paramètres au milieu?
Spock
109
-- --argsmerde sacrée qui est bizarre mais ok
août
9
@Spock Vous pouvez utiliser les fonctions shell. Voici une configuration eslint + tslint que j'utilise pour permettre le passage d'arguments personnalisés à eslint, par exemple, via "npm run lint - -f unix": "lint": "f () {eslint -f codeframe $ @. && npm run tslint && echo 'lint clean!';}; f "
ecmanaut
3
La meilleure façon de définir la valeur "myPackage: myPort 9090" consiste à utiliser un indicateur de configuration pour la commande "--myPackage: myPort = 9090" - keithcirkel.co.uk/how-to-use-npm-as-a-build -tool
chrismarx
223

Vous avez demandé à pouvoir exécuter quelque chose comme npm start 8080 . Ceci est possible sans avoir à modifier script.jsou à configurer les fichiers comme suit.

Par exemple, dans votre "scripts"valeur JSON, incluez--

"start": "node ./script.js server $PORT"

Et puis à partir de la ligne de commande:

$ PORT=8080 npm start

J'ai confirmé que cela fonctionne en utilisant bash et npm 1.4.23. Notez que cette solution de contournement ne nécessite pas la résolution du problème n ° 3494 de GitHub npm .

cjerdonek
la source
19
Cela fonctionne vraiment bien. Vous pouvez également faire quelque chose comme node ./script.js server ${PORT:-8080}le rendre facultatif.
graup
7
Il me semble que je ne peux pas le faire dans Windows avec git bash. Quelqu'un l'a fait fonctionner peut-être? (la même commande fonctionne sur ubuntu)
Karolis Šarapnickis
3
Hey @graup cela a fonctionné pour moi NODE_PORT=${PORT=8080}(notez l'égal) mais pas le: - syntaxe
MaieonBrix
14
Cela ne fonctionne pas multi-plateforme! Par exemple, sous Windows, la commande devrait être node ./script.js server %PORT%. Pensez à utiliser cross-var et cross-env .
Stijn de Witt
L'utilisation de vars env comme cli args me semble-t-elle assez limitée? Peut aussi bien utiliser quelque chose comme le module de configuration pour gérer les variables env, les valeurs par défaut et la configuration en général.
Mr5o1
93

Vous pouvez également faire cela:

Dans package.json:

"scripts": {
    "cool": "./cool.js"
}

Dans cool.js:

 console.log({ myVar: process.env.npm_config_myVar });

En CLI:

npm --myVar=something run-script cool

Devrait produire:

{ myVar: 'something' }

Mise à jour: En utilisant npm 3.10.3, il semble qu'il minuscule les process.env.npm_config_variables? J'utilise aussi better-npm-run, donc je ne sais pas si ce comportement est par défaut vanille ou non, mais cette réponse est active. Au lieu de process.env.npm_config_myVar, essayezprocess.env.npm_config_myvar

francoisrv
la source
4
Merci, cela a fonctionné pour moi! Ce qui me manquait spécifiquement le préfixe "npm_config_" au nom de variable que vous spécifiez sur la ligne de commande.
jp093121
2
C'est faux. process.env.npm_config_myVarrenvoie vrai, pas la valeur.
Karl Morrison
1
Fonctionne avec la version 6.8.0 de npm mais uniquement lorsque j'ai utilisé des minuscules pour le nom de variable. il semble que lilke npm le change en minuscule
Ofir Meguri
Excellente solution, fonctionne avec les paramètres en minuscules sur npm 6.5.0
GavinBelson
78

La réponse de jakub.g est correcte, cependant un exemple utilisant grunt semble un peu complexe.

Donc, ma réponse la plus simple:

- Envoi d'un argument de ligne de commande à un script npm

Syntaxe pour l'envoi d'arguments de ligne de commande à un script npm:

npm run [command] [-- <args>]

Imaginez que nous ayons une tâche de démarrage npm dans notre package.json pour lancer le serveur de développement webpack:

"scripts": {
  "start": "webpack-dev-server --port 5000"
},

Nous l'exécutons à partir de la ligne de commande avec npm start

Maintenant, si nous voulons passer un port au script npm:

"scripts": {
  "start": "webpack-dev-server --port process.env.port || 8080"
},

exécuter cela et passer le port par exemple 5000 via la ligne de commande serait comme suit:

npm start --port:5000

- Utilisation de la configuration de package.json:

Comme mentionné par jakub.g , vous pouvez également définir des paramètres dans la configuration de votre package.json

"config": {
  "myPort": "5000"
}

"scripts": {
  "start": "webpack-dev-server --port process.env.npm_package_config_myPort || 8080"
},

npm start utilisera le port spécifié dans votre configuration, ou bien vous pouvez le remplacer

npm config set myPackage:myPort 3000

- Définition d'un paramètre dans votre script npm

Un exemple de lecture d'un ensemble de variables dans votre script npm. Dans cet exempleNODE_ENV

"scripts": {
  "start:prod": "NODE_ENV=prod node server.js",
  "start:dev": "NODE_ENV=dev node server.js"
},

lire NODE_ENV en server.js soit prod ou dev

var env = process.env.NODE_ENV || 'prod'

if(env === 'dev'){
    var app = require("./serverDev.js");
} else {
    var app = require("./serverProd.js");
}
svnm
la source
5
notez que la syntaxe comme "start:prod": "NODE_ENV=prod node server.js"dans package.jsonne fonctionnera pas sur Windows, sauf si vous utilisez cross-env
jakub.g
2
Correction?: "start": "webpack-dev-server --port process.env.npm_package_config_myPort || 8080" },Devrait être en "start": "webpack-dev-server --port $npm_package_config_myPort || 8080" },fonction de mon utilisation expliquée par ce tutoriel . La référence de processus peut apparemment être utilisée dans le javascript.
Aaron Roller
35

npm 2.x support cli args

Commander

npm run-script start -- --foo=3

Package.json

"start": "node ./index.js"

Index.js

console.log('process.argv', process.argv);

Gregory Houllier
la source
1
La réponse acceptée (donnée un an plus tôt que celle-ci) mentionne déjà cette technique.
Dan Dascalescu
34

Utilisez-le process.argvdans votre code, puis fournissez simplement un suivi $*de l'entrée de valeur de vos scripts.

À titre d'exemple, essayez-le avec un script simple qui enregistre simplement les arguments fournis pour standardiser echoargs.js:

console.log('arguments: ' + process.argv.slice(2));

package.json:

"scripts": {
    "start": "node echoargs.js $*"
}

Exemples:

> npm start 1 2 3
arguments: 1,2,3

process.argv[0]est l'exécutable (nœud), process.argv[1]est votre script.

Testé avec npm v5.3.0 et node v8.4.0

Peter
la source
Ne fonctionne pas après avoir ajouté --des arguments, par exemple - npm run demo.js --skip, cela fonctionne si ajouté un supplément --, par exemple -npm run demo.js -- --skip
Shreyas
Pouvez-vous utiliser cette méthode sans avoir de echoargs.jsfichier de script séparé ?
Joshua Pinter
@JoshuaPinter echoargs.js est juste un exemple, je vais modifier ma réponse pour que cela soit clair
Peter
@Peter Oui, mais doit-il s'agir d'un fichier de script. J'essaie de créer un script qui utilise adbpour pousser un .dbfichier vers l'émulateur Android et accepte un paramètre pour le chemin local du .dbfichier à pousser, qui est le premier paramètre de adb push. Quelque chose comme ça: "db:push": "adb push process.argv.slice(2) /data/data/com.cntral.app/databases/database.db"et je veux l'appeler avec npm run db:push /Users/joshuapinter/Downloads/updated.db. Des pensées?
Joshua Pinter le
21

Si vous souhaitez passer des arguments au milieu d'un script npm, au lieu de les avoir simplement ajoutés à la fin, les variables d'environnement en ligne semblent fonctionner correctement:

"scripts": {
  "dev": "BABEL_ARGS=-w npm run build && cd lib/server && nodemon index.js",
  "start": "npm run build && node lib/server/index.js",
  "build": "mkdir -p lib && babel $BABEL_ARGS -s inline --stage 0 src -d lib",
},

Ici, npm run devpasse le -wdrapeau de surveillance à babel, mais npm run startexécute simplement une génération régulière une fois.

Dan Ross
la source
Comment est-ce appelé depuis la CLI?
bwobst
@dresdin npm run dev,npm start
TJ
2
Besoin d'utiliser cross-env pour l'utiliser sur Windows.
fracz
8

J'avais utilisé ce one-liner dans le passé, et après un peu de temps loin de Node.js a dû essayer de le redécouvrir récemment. Semblable à la solution mentionnée par @francoisrv, il utilise les node_config_*variables.

Créez le package.jsonfichier minimal suivant :

{
  "name": "argument",
  "version": "1.0.0",
  "scripts": {
    "argument": "echo \"The value of --foo is '${npm_config_foo}'\""
  }
}

Exécutez la commande suivante:

npm run argument --foo=bar

Observez la sortie suivante:

La valeur de --foo est 'bar'

Tout cela est bien documenté dans la documentation officielle de npm:

Remarque: L'en- tête Variables d'environnement explique que les variables à l'intérieur des scripts se comportent différemment de ce qui est défini dans la documentation. Cela est vrai en ce qui concerne la casse , que l'argument soit défini avec un espace ou un signe égal .

Remarque: Si vous utilisez un argument avec des tirets, ceux-ci seront remplacés par des traits de soulignement dans la variable d'environnement correspondante. Par exemple, npm run example --foo-bar=bazcorrespondrait à ${npm_config_foo_bar}.

Remarque: Pour les utilisateurs non-WSL de Windows, voir les commentaires de @Doctor Blue ci-dessous ... TL; DR remplacer ${npm_config_foo}par %npm_config_foo%.

Andrew Odri
la source
Bonjour. J'essaie d'utiliser votre exemple, mais je crains que cela ne fonctionne pas pour moi. Je copie-collé votre script « argument », et fait la même chose pour la commande à exécuter ( npm run argument --foo=bar), mais la variable n'est pas remplacé: "The value of --foo is '${npm_config_foo}'". Fonctionnant sur Windows 10 si cela est important, avec la version 6.9.0 de NPM.
Docteur Blue
@DoctorBlue Ahh à droite, Node et Windows ne jouent pas toujours bien ... Cet article pourrait éclairer les variables d'environnement dans les scripts npm: (TL; les commandes DR vont directement au système d'exploitation hôte, même si elles sont lancées à partir d'un autre shell) blog .risingstack.com / node-js-windows-10-tutorial /… Je ne suis pas sûr de votre configuration, mais si vous utilisez Git Bash pour exécuter Node, vous voudrez peut-être envisager de l'exécuter via les documents WSL :) . microsoft.com/en-us/windows/nodejs/setup-on-wsl2
Andrew Odri
1
Je l'ai compris. Je devais juste utiliser à la %npm_config_foo%place. Ligne de commande Windows pure / PowerShell ici. (Vous n'avez pas le choix non plus.)
Docteur Blue
6

Cela ne répond pas vraiment à votre question, mais vous pouvez toujours utiliser des variables d'environnement à la place:

"scripts": {
    "start": "PORT=3000 node server.js"
}

Puis dans votre fichier server.js:

var port = process.env.PORT || 3000;
DrunkenBeetle
la source
1
C'est bien tant que vous êtes sur une plate-forme Unix. Malheureusement, cela ne fonctionne pas avec Windows car cela a sa propre convention.
Juho Vepsäläinen
6

La plupart des réponses ci-dessus couvrent simplement le passage des arguments dans votre script NodeJS, appelé par npm. Ma solution est à usage général.

Enveloppez simplement le script npm avec un shappel d' interpréteur de shell (par exemple ) et passez les arguments comme d'habitude. La seule exception est que le premier numéro d'argument est 0.

Par exemple, vous souhaitez ajouter le script npm someprogram --env=<argument_1>, où someprogramimprime simplement la valeur de l' envargument:

package.json

"scripts": {
  "command": "sh -c 'someprogram --env=$0'"
}

Lorsque vous l'exécutez:

% npm run -s command my-environment
my-environment
Alexandr Priezzhev
la source
Merci! C'était parfait!
Felipe Desiderati
Simple et élégant! ne fonctionnera pas sur un shell MS DOS.
n370
3

D'après ce que je vois, les gens utilisent des scripts package.json lorsqu'ils souhaitent exécuter le script de manière plus simple. Par exemple, pour utiliser nodemoncelui installé dans les node_modules locaux, nous ne pouvons pas appeler nodemondirectement à partir du cli, mais nous pouvons l'appeler en utilisant ./node_modules/nodemon/nodemon.js. Donc, pour simplifier ce long typage, on peut mettre ça ...

    ...

    scripts: {
      'start': 'nodemon app.js'
    }

    ...

... puis appelez npm startpour utiliser 'nodemon' qui a app.js comme premier argument.

Ce que j'essaie de dire, si vous voulez juste démarrer votre serveur avec la nodecommande, je ne pense pas que vous ayez besoin de l'utiliser scripts. Taper npm startou node app.jsavoir le même effort.

Mais si vous voulez utiliser nodemonet passer un argument dynamique, n'utilisez pas non scriptplus. Essayez plutôt d'utiliser un lien symbolique.

Par exemple, utiliser la migration avec sequelize. Je crée un lien symbolique ...

ln -s node_modules/sequelize/bin/sequelize sequelize

... Et je peux passer tout argument quand je l'appelle ...

./sequlize -h /* show help */

./sequelize -m /* upgrade migration */

./sequelize -m -u /* downgrade migration */

etc...

À ce stade, l'utilisation de symlink est la meilleure façon de comprendre, mais je ne pense pas vraiment que ce soit la meilleure pratique.

J'espère également avoir votre avis sur ma réponse.

Gamin
la source
1
Cela ne répond pas du tout à la question. Je ne sais pas comment il a obtenu 6 votes positifs, mais félicitations :)
Dan Dascalescu
2

Remarque: Cette approche modifie votre package.jsonà la volée, utilisez-la si vous n'avez pas d'alternative.

J'ai dû passer des arguments de ligne de commande à mes scripts qui ressemblaient à:

"scripts": {
    "start": "npm run build && npm run watch",
    "watch": "concurrently  \"npm run watch-ts\" \"npm run watch-node\"",
    ...
}

Donc, cela signifie que je démarre mon application avec npm run start.

Maintenant, si je veux passer quelques arguments, je commencerais par peut-être:

npm run start -- --config=someConfig

Ce que cela fait est: npm run build && npm run watch -- --config=someConfig. Le problème, c'est qu'il ajoute toujours les arguments à la fin du script. Cela signifie que tous les scripts chaînés n'obtiennent pas ces arguments (Args peut ou non être requis par tous, mais c'est une autre histoire.). De plus, lorsque les scripts liés sont appelés, ces scripts n'obtiendront pas les arguments passés. ie le watchscript n'obtiendra pas les arguments passés.

L'utilisation de la production de mon application est en tant que .exe, donc passer les arguments dans l'exe fonctionne bien mais si vous voulez le faire pendant le développement, cela devient problématique.

Je n'ai pas trouvé de moyen approprié pour y parvenir, c'est donc ce que j'ai essayé.

J'ai créé un fichier javascript: start-script.jsau niveau parent de l'application, j'ai un "default.package.json" et au lieu de maintenir "package.json", je maintiens "default.package.json". Le but de start-script.jsonest de lire default.package.json, d'extraire scriptset de rechercher npm run scriptnamepuis d'ajouter les arguments passés à ces scripts. Après cela, il en créera un nouveau package.jsonet copiera les données de default.package.json avec les scripts modifiés, puis appellera npm run start.

const fs = require('fs');
const { spawn } = require('child_process');

// open default.package.json
const defaultPackage = fs.readFileSync('./default.package.json');
try {
    const packageOb = JSON.parse(defaultPackage);
    // loop over the scripts present in this object, edit them with flags
    if ('scripts' in packageOb && process.argv.length > 2) {

        const passedFlags = ` -- ${process.argv.slice(2).join(' ')}`;
        // assuming the script names have words, : or -, modify the regex if required.
        const regexPattern = /(npm run [\w:-]*)/g;
        const scriptsWithFlags = Object.entries(packageOb.scripts).reduce((acc, [key, value]) => {
            const patternMatches = value.match(regexPattern);
            // loop over all the matched strings and attach the desired flags.
            if (patternMatches) {
                for (let eachMatchedPattern of patternMatches) {
                    const startIndex = value.indexOf(eachMatchedPattern);
                    const endIndex = startIndex + eachMatchedPattern.length;
                    // save the string which doen't fall in this matched pattern range.
                    value = value.slice(0, startIndex) + eachMatchedPattern + passedFlags + value.slice(endIndex);
                }
            }
            acc[key] = value;
            return acc;
        }, {});
        packageOb.scripts = scriptsWithFlags;
    }

    const modifiedJSON = JSON.stringify(packageOb, null, 4);
    fs.writeFileSync('./package.json', modifiedJSON);

    // now run your npm start script
    let cmd = 'npm';
    // check if this works in your OS
    if (process.platform === 'win32') {
        cmd = 'npm.cmd';    // https://github.com/nodejs/node/issues/3675
    }
    spawn(cmd, ['run', 'start'], { stdio: 'inherit' });

} catch(e) {
    console.log('Error while parsing default.package.json', e);
}

Maintenant, au lieu de faire npm run start, je faisnode start-script.js --c=somethis --r=somethingElse

L'exécution initiale semble bonne, mais n'a pas été testée de manière approfondie. Utilisez-le si vous le souhaitez pour le développement d'applications.

Ashish Ranjan
la source
1

J'ai trouvé cette question alors que j'essayais de résoudre mon problème avec l'exécution de la commande sequelize seed: generate cli:

node_modules/.bin/sequelize seed:generate --name=user

Permettez-moi d'aller droit au but. Je voulais avoir une courte commande de script dans mon fichier package.json et fournir l'argument --name en même temps

La réponse est venue après quelques expériences. Voici ma commande dans package.json

"scripts: {
  "seed:generate":"NODE_ENV=development node_modules/.bin/sequelize seed:generate"
}

... et voici un exemple de l'exécuter dans le terminal pour générer un fichier de départ pour un utilisateur

> yarn seed:generate --name=user

> npm run seed:generate -- --name=user

FYI

yarn -v
1.6.0

npm -v
5.6.0
Serge Seletskyy
la source
2
Est-ce la même technique que celle expliquée dans la réponse acceptée en 2013, pour réussir -- --arg1, ...?
Dan Dascalescu
2
OK, alors pourquoi répéter la réponse?
Dan Dascalescu
J'ai partagé un exemple distinct d'utilisation, n'est-ce pas évident?
Serge Seletskyy
2
Si je voulais partager un autre exemple pour une technique déjà expliquée dans une réponse différente, j'ajouterais mon exemple comme commentaire à cette réponse.
Dan Dascalescu
1
gotcha, fera comme ça la prochaine fois
Serge Seletskyy
0

npm run script_target - <argument> Fondamentalement, c'est la façon de passer les arguments de la ligne de commande, mais cela ne fonctionnera que si le script n'a qu'une seule commande en cours d'exécution comme j'exécute une commande, c'est-à-dire npm run start - 4200

"script":{
       "start" : "ng serve --port="
 }

Cela s'exécutera pour transmettre les paramètres de ligne de commande, mais que se passe-t-il si nous exécutons plus d'une commande ensemble, comme npm run build c: / workspace / file

"script":{
       "build" : "copy c:/file <arg> && ng build"
 } 

mais il interprétera comme ceci lors de l'exécution de la copie c: / fichier && ng build c: / espace de travail / fichier et nous attendons quelque chose comme cette copie c: / fichier c: / espace de travail / fichier && ng build

Remarque: - le paramètre de ligne de commande ne fonctionne donc que si une seule commande d'un script est attendue

J'ai lu quelques réponses ci-dessus dans lesquelles certains écrivent que vous pouvez accéder au paramètre de ligne de commande en utilisant $ symbol mais cela ne fonctionnera pas

Sunny Goel
la source
0

Je sais qu'il y a déjà une réponse approuvée, mais j'aime un peu cette approche JSON.

npm start '{"PROJECT_NAME_STR":"my amazing stuff", "CRAZY_ARR":[0,7,"hungry"], "MAGICAL_NUMBER_INT": 42, "THING_BOO":true}';

Habituellement, j'ai comme 1 var dont j'ai besoin, comme un nom de projet, donc je trouve cela rapide et simple.

J'ai aussi souvent quelque chose comme ça dans mon package.json

"scripts": {
    "start": "NODE_ENV=development node local.js"
}

Et étant gourmand, je veux "tout ça", NODE_ENV et les trucs d'argument de la ligne CMD.

Vous accédez simplement à ces choses comme ça dans votre fichier (dans mon cas local.js)

console.log(process.env.NODE_ENV, starter_obj.CRAZY_ARR, starter_obj.PROJECT_NAME_STR, starter_obj.MAGICAL_NUMBER_INT, starter_obj.THING_BOO);

Vous avez juste besoin d'avoir ce bit au-dessus (j'utilise v10.16.0 btw)

var starter_obj = JSON.parse(JSON.parse(process.env.npm_config_argv).remain[0]);

Anyhoo, la question a déjà répondu. Je pensais partager, car j'utilise beaucoup cette méthode.

fullstacklife
la source