npm 스크립트로 명령 행 인수 보내기


818

scripts내 부분 package.json현재는 다음과 같습니다 :

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

... npm start서버를 시작하기 위해 실행할 수 있음을 의미 합니다. 여태까지는 그런대로 잘됐다.

그러나 나는 비슷한 것을 실행 npm start 8080하고 인수 script.js(예 npm start 8080:> node ./script.js server 8080)를 전달 하고 싶습니다 . 이것이 가능한가?

답변:


1130

2014.10.30 편집 : npm 2.0.0 부터 args를 전달할 수 있습니다npm run

구문은 다음과 같습니다.

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

필요한 것을 유의하십시오 --. npm명령 자체에 전달 된 매개 변수와 스크립트에 전달 된 매개 변수를 분리 해야합니다.

당신이 있다면 package.json

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

다음 명령은 동일합니다.

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

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

매개 변수 값을 얻으려면 이 질문을 참조하십시오 . 명명 된 매개 변수를 읽으 려면 yargs 또는 minimist 와 같은 구문 분석 라이브러리를 사용하는 것이 가장 좋습니다 . nodejs는 process.argv명령 행 매개 변수 값을 포함하여 전체적으로 노출 되지만 이는 저수준 API (운영 체제에서 노드 실행 파일에 제공하는 공백으로 구분 된 문자열 배열)입니다.


편집 2013.10.03 : 현재 직접 불가능합니다. 그러나 원하는 동작을 구현하기 위해 관련된 GitHub 문제가 열렸습니다npm . 컨센서스가 이것을 구현하는 것으로 보이지만 이전에 해결 된 다른 문제에 달려 있습니다.


원래 답변 : 일종의 해결 방법으로 (매우 편리하지는 않지만) 다음과 같이 할 수 있습니다.

에서 패키지 이름을 말 package.jsonIS myPackage당신은 또한이

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

그런 다음 추가하십시오 package.json:

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

그리고 당신의 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

이렇게하면 기본적으로 npm start8080이 사용됩니다. 그러나 구성 할 수 있습니다 (값은 npm내부 저장소에 저장 됨).

npm config set myPackage:myPort 9090

그런 다음을 호출 npm start하면 9090이 사용됩니다 (기본값 package.json은 재정의 됨).


1
이것은 또한 다음과 같은 패키지와 완벽하게 작동합니다 yargs. 이후의 모든 매개 변수는 --스크립트에서 완벽하게 구문 분석 할 수 있습니다.
토마스

11
AFAIKS, 스크립트 끝에 매개 변수를 추가하는 것만 가능합니다. 중간에 매개 변수가 필요한 경우 어떻게해야합니까?
Spock

108
-- --args이상하지만 괜찮은 거룩한 쓰레기
aug

9
@Spock 쉘 기능을 사용할 수 있습니다. 다음은 "npm run lint--f unix"를 통해 커스텀 인수를 eslint에 전달하기 위해 사용하는 eslint + tslint 설정입니다. "lint": "f () {eslint -f codeframe $ @. && npm run tslint && echo 'lint clean!';}; f "
ecmanaut

3
"myPackage : myPort 9090"값을 설정하는 더 좋은 방법은 "--myPackage : myPort = 9090"명령에 대한 구성 플래그를 사용하는 것입니다.- keithcirkel.co.uk
chrismarx

222

와 같은 것을 실행할 수 있도록 요청했습니다 npm start 8080. script.js다음과 같이 파일 을 수정 하거나 구성 할 필요가 없습니다 .

예를 들어, "scripts"JSON 값에 다음을 포함하십시오.

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

그런 다음 명령 줄에서

$ PORT=8080 npm start

나는 이것이 bash와 npm 1.4.23을 사용하여 작동한다는 것을 확인했다. 이 해결 방법으로 GitHub npm 문제 # 3494 를 해결할 필요는 없습니다 .


19
이것은 정말 잘 작동합니다. 당신은 또한 같은 것을 할 수있는 node ./script.js server ${PORT:-8080}이 옵션 만드는합니다.
graup

7
git bash가있는 Windows에서는이 작업을 수행 할 수없는 것 같습니다. 누구든지 아마 일하고 있습니까? (같은 명령이 우분투에서 작동합니다)
Karolis Šarapnickis

3
안녕하세요 @graup 이것은 나를 위해 일 NODE_PORT=${PORT=8080}했지만 (동등한 것은 아니지만) :-구문
MaieonBrix

14
이것은 크로스 플랫폼에서 작동하지 않습니다! 예를 들어 Windows에서 명령은이어야 node ./script.js server %PORT%합니다. cross-varcross-env 사용을 고려하십시오 .
Stijn de Witt

cli args로 env vars를 사용하는 유스 케이스는 꽤 제한적입니까? 환경 설정 모듈, 기본값 및 구성을 처리하기 위해 구성 모듈과 같은 것을 사용할 수도 있습니다.
Mr5o1

93

당신은 또한 그것을 할 수 있습니다 :

에서 package.json:

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

에서 cool.js:

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

CLI에서 :

npm --myVar=something run-script cool

출력해야합니다 :

{ myVar: 'something' }

업데이트 : npm 3.10.3을 사용하면 process.env.npm_config_변수가 소문자로 표시 됩니까? 나는 또한 사용 better-npm-run하고 있기 때문에 이것이 바닐라 기본 동작인지 확실하지 않지만이 대답 은 효과가 있습니다. 대신 process.env.npm_config_myVar, 시도process.env.npm_config_myvar


4
고마워 이것은 나를 위해 일했다! 내가 구체적으로 누락 한 것은 명령 줄에서 지정하는 변수 이름 앞에 "npm_config_"접두사입니다.
jp093121

2
이것은 잘못이다. process.env.npm_config_myVar값이 아닌 true를 반환합니다.
Karl Morrison

1
npm 버전 6.8.0에서 작동하지만 변수 이름에 소문자를 사용한 경우에만 가능합니다. 그것은 소문자로 lilke NPM 변화를 보이는
피르 메 구리에게

훌륭한 솔루션, npm
6.5.0

77

jakub.g 의 답변은 정확하지만 grunt를 사용하는 예제는 약간 복잡해 보입니다.

그래서 내 간단한 대답 :

-npm 스크립트에 명령 행 인수 보내기

명령 행 인수를 npm 스크립트로 보내는 구문 :

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

webpack dev 서버를 시작하기 위해 package.json에 npm 시작 작업이 있다고 상상해보십시오.

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

우리는 이것을 커맨드 라인에서 실행합니다. npm start

이제 npm 스크립트에 포트를 전달하려면 다음을 수행하십시오.

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

이것을 실행하고 포트를 5000 번 명령 줄을 통해 전달하면 다음과 같습니다

npm start --port:5000

-package.json 구성 사용 :

에서 언급 한 바와 같이 jakub.g , 당신은 다른 방법으로 당신의 설정에 PARAMS을 설정할 수 있습니다 package.json

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

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

npm start 구성에 지정된 포트를 사용하거나 대체 할 수 있습니다

npm config set myPackage:myPort 3000

-npm 스크립트에서 매개 변수 설정

npm 스크립트에서 변수 세트를 읽는 예입니다. 이 예에서NODE_ENV

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

에 NODE_ENV을 읽을 server.js 중 하나를 찌르다 또는 DEV

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

if(env === 'dev'){
    var app = require("./serverDev.js");
} else {
    var app = require("./serverProd.js");
}

5
같은 그 구문을 참고 "start:prod": "NODE_ENV=prod node server.js"package.jsonWindows에서하지 않습니다 작업, 당신은 사용하지 않는 크로스 ENV를
jakub.g

2
수정? : 이 튜토리얼에서 설명 된대로 "start": "webpack-dev-server --port process.env.npm_package_config_myPort || 8080" },사용해야합니다 . 프로세스 참조는 분명히 자바 스크립트 내에서 사용될 수 있습니다. "start": "webpack-dev-server --port $npm_package_config_myPort || 8080" },
Aaron Roller


34

process.argv코드에서 사용 하고 $*스크립트 값 입력에 후행 을 제공 하십시오.

예를 들어 제공된 인수를 표준 출력에 기록하는 간단한 스크립트로 시도하십시오 echoargs.js.

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

package.json :

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

예 :

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

process.argv[0]실행 파일 (노드)이며 process.argv[1]스크립트입니다.

npm v5.3.0 및 노드 v8.4.0으로 테스트


추가 한 후 작동하지 않습니다 --예를 들어, 인수에 - npm run demo.js --skip여분을 추가 한 경우, 그것을 작동 --예를 들어, -npm run demo.js -- --skip
Shreyas

별도의 echoargs.js스크립트 파일 없이이 방법을 사용할 수 있습니까 ?
Joshua Pinter

@JoshuaPinter echoargs.js는 단지 예를위한 것입니다.이를 명확하게하기 위해 답을 편집하겠습니다
Peter

@Peter 맞지만 스크립트 파일이어야합니다. 파일을 Android 에뮬레이터 adb에 푸시하고 .db파일의 로컬 경로에 대한 매개 변수를 수락 하는 스크립트를 작성하려고합니다 .이 .db매개 변수는의 첫 번째 매개 변수입니다 adb push. 이와 같은 것 : "db:push": "adb push process.argv.slice(2) /data/data/com.cntral.app/databases/database.db"그리고로 전화하고 싶습니다 npm run db:push /Users/joshuapinter/Downloads/updated.db. 이견있는 사람?
Joshua Pinter

21

인수를 끝에 추가하는 대신 npm 스크립트의 중간에 인수를 전달하려면 인라인 환경 변수가 잘 작동하는 것 같습니다.

"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",
},

여기 npm run dev에서 -wwatch 플래그를 babel에 전달 하지만 npm run start일반 빌드를 한 번만 실행하십시오.


이것을 CLI에서 어떻게 호출합니까?
bwobst

@dresdin npm run dev,npm start
TJ

2
Windows에서 사용 하려면 교차 환경 을 사용해야합니다.
fracz

8

나는 과거 에이 한 줄짜리 라이너를 사용하고 있었고 Node.js에서 조금 떨어진 후에 최근에 그것을 재발견해야했습니다. @francoisrv가 언급 한 솔루션과 유사하게 node_config_*변수를 사용합니다 .

다음과 같은 최소 package.json파일을 작성 하십시오.

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

다음 명령을 실행하십시오.

npm run argument --foo=bar

다음 출력을 관찰하십시오.

--foo의 값은 'bar'입니다.

이 모든 것이 npm 공식 문서에 잘 정리되어 있습니다.

참고 : 환경 변수는 제목 스크립트 내부 변수는 문서에 정의 된 것과 다르게 행동 할 것을 설명합니다. 이에 관해서 마찬가지입니다 경우 감도 는 인수가 정의되어뿐만 아니라 여부, 공간 또는 등호 .

참고 : 하이픈과 함께 인수를 사용하는 경우 해당 환경 변수에서 밑줄로 바뀝니다. 예를 들어에 npm run example --foo-bar=baz해당합니다 ${npm_config_foo_bar}.

주 : 비 WSL Windows 사용자의 경우, 아래 ... TL @Doctor 블루의 의견을 참조; DR 교체 ${npm_config_foo}와 함께 %npm_config_foo%.


안녕하세요. 나는 당신의 모범을 사용하려고 노력하고 있지만 그것이 나를 위해 작동하지 않을까 걱정됩니다. "인수"스크립트를 복사하여 붙여 넣은 다음 명령을 실행하기 위해 동일한 작업을 수행 npm run argument --foo=bar했지만 ( ) 변수가 바뀌지 않았습니다 "The value of --foo is '${npm_config_foo}'". 중요한 경우 NPM 버전 6.9.0으로 Windows 10에서 실행됩니다.
닥터 블루

@DoctorBlue 아 오른쪽 노드 및 Windows는 항상 좋은 재생되지 않는 ...이 문서는 NPM 스크립트에서 환경 변수에 도움이 되거 수 있습니다 (TL을, DR 명령이 다른 쉘에서 시작하더라도, 호스트 OS로 바로 이동) 블로그 .risingstack.com / node-js-windows-10-tutorial / ... 설정이 확실하지 않지만 Git Bash를 사용하여 노드를 실행하는 경우 WSL을 통해 실행하는 것이 좋습니다
Andrew Odri

1
나는 그것을 알아. %npm_config_foo%대신 사용해야 했습니다. 순수한 Windows 명령 행 / powershell은 여기에 있습니다. (또한 선택의 여지가 없습니다.)
의사 Blue

6

이것은 실제로 귀하의 질문에 대답하지는 않지만 항상 환경 변수를 대신 사용할 수 있습니다.

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

그런 다음 server.js 파일에서

var port = process.env.PORT || 3000;

1
이것은 유닉스 플랫폼에있는 한 좋습니다. 불행히도 자체 규칙이 있으므로 Windows에서는 작동하지 않습니다.
Juho Vepsäläinen

6

위의 대부분의 답변은 인수를 NodeJS 스크립트에 전달하는 것입니다. 내 솔루션은 일반적인 용도입니다.

npm 스크립트를 쉘 인터프리터 (예 :) sh호출로 감싸고 평소와 같이 인수를 전달하십시오. 유일한 예외는 첫 번째 인수 번호가 0입니다.

예를 들어, NPM 스크립트를 추가하려면 someprogram --env=<argument_1>, someprogram단지의 값 출력 env인수를 :

package.json

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

실행할 때 :

% npm run -s command my-environment
my-environment

감사! 이것은 완벽했다!
펠리페 Desiderati

간단하고 우아한! MS DOS 셸에서는 작동하지 않습니다.
n370

3

내가 본 것에서 사람들은보다 간단한 방법으로 스크립트를 실행하려고 할 때 package.json 스크립트를 사용합니다. 예를 들어 nodemon로컬 node_modules에 설치된 것을 사용 nodemon하려면 cli에서 직접 호출 할 수 없지만 을 사용하여 호출 할 수 있습니다 ./node_modules/nodemon/nodemon.js. 이 긴 타이핑을 단순화하기 위해 이것을 넣을 수 있습니다 ...

    ...

    스크립트 : {
      'start': 'nodemon app.js'
    }

    ...

... 그런 다음 npm startapp.js를 첫 번째 인수로 사용하는 'nodemon'을 사용하도록 호출 하십시오.

내가 말하려는 것은 node명령으로 서버를 시작하려는 경우 사용할 필요가 없다고 생각합니다 scripts. 타이핑 npm start하거나 node app.js같은 노력을합니다.

그러나을 사용 nodemon하고 동적 인수를 전달하려면 script둘 중 하나를 사용하지 마십시오 . 대신 symlink를 사용하십시오.

예를 들어으로 마이그레이션을 사용합니다 sequelize. 심볼릭 링크를 만듭니다 ...

ln -s node_modules/sequelize/bin/sequelize sequelize

그리고 나는 그것을 부를 때 어떤 논쟁도 통과시킬 수 있습니다 ...

./sequlize -h /* show help */

./sequelize -m /* upgrade migration */

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

기타...

이 시점에서 symlink를 사용하는 것이 가장 좋은 방법이지만 실제로는 이것이 최선의 방법이라고 생각하지 않습니다.

또한 귀하의 의견에 대한 귀하의 의견이 있기를 바랍니다.


1
이것은 전혀 질문에 대답하지 않습니다. 나는 6 upvotes을 가지고 방법을 알고 있지만, 축하 :)하지 않습니다
댄 Dascalescu

2

참고 : 이 방법 package.json은 즉시 수정 하므로 대안이없는 경우 사용하십시오.

나는 다음과 같은 스크립트에 명령 줄 인수를 전달해야했습니다.

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

따라서 이것은로 앱을 시작한다는 의미입니다 npm run start.

이제 몇 가지 인수를 전달하려면 다음과 같이 시작하십시오.

npm run start -- --config=someConfig

이것이하는 일은 : npm run build && npm run watch -- --config=someConfig. 문제는 항상 스크립트 끝에 인수를 추가한다는 것입니다. 이것은 모든 연쇄 스크립트가 이러한 인수를 얻지 못한다는 것을 의미합니다 (아르가 아마도 필요하지 않을 수도 있지만 다른 이야기입니다). 또한 연결된 스크립트가 호출되면 해당 스크립트는 전달 된 인수를 얻지 못합니다. 즉, watch스크립트는 전달 된 인수를 얻지 못합니다.

내 응용 프로그램의 프로덕션 사용법은로 사용 .exe되므로 exe의 인수를 전달하면 정상적으로 작동하지만 개발 중에이 작업을 수행하려면 비참합니다.

나는 이것을 달성 할 수있는 적절한 방법을 찾을 수 없으므로 이것이 내가 시도한 것입니다.

Javascript 파일을 만들었습니다. start-script.js응용 프로그램의 상위 수준에 "default.package.json"이 있고 "package.json"을 유지 관리하는 대신 "default.package.json"을 유지 관리합니다. 의 목적은 start-script.json읽을 수 default.package.json의 추출 scripts및 찾아 npm run scriptname이러한 스크립트에 다음 APPEND 전달 된 인수입니다. 그런 다음 새 package.json스크립트를 작성하고 수정 된 스크립트를 사용하여 default.package.json에서 데이터를 복사 한 다음을 호출 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);
}

자, 대신 일을 npm run start, 내가 할node start-script.js --c=somethis --r=somethingElse

초기 실행은 괜찮아 보이지만 철저히 테스트되지 않았습니다. 앱 개발을 원한다면 사용하십시오.


1

sequelize seed : generate cli 명령 실행과 관련된 문제를 해결하는 동안이 질문을 발견했습니다.

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

요점을 알려 드리겠습니다. package.json 파일 에 짧은 스크립트 명령 을 넣고 동시에 --name 인수를 제공하고 싶었 습니다.

답은 몇 가지 실험 후에 나왔습니다. package.json의 명령은 다음과 같습니다.

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

... 그리고 여기에 터미널에서 실행하여 사용자 의 시드 파일을 생성하는 예가 있습니다.

> yarn seed:generate --name=user

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

참고로

yarn -v
1.6.0

npm -v
5.6.0

2
이것은 2013 년에 수락 된 답변에서 설명한 것과 동일한 기술 -- --arg1, ...입니까?
Dan Dascalescu

2
그렇다면 왜 대답을 반복합니까?
Dan Dascalescu

나는 사용법의 독특한 예를 공유했습니다. 분명하지 않습니까?
Sletskyy Serge

2
다른 답변에서 이미 설명한 기술에 대한 다른 예를 공유하려면 해당 예에 대한 설명으로 내 예를 추가하십시오.
Dan Dascalescu

1
다음 시간에 그렇게 할 것입니다
Serge Seletskyy

0

npm run script_target-<argument> 기본적으로 이것은 명령 줄 인수를 전달하는 방법이지만 스크립트가 명령을 실행하는 것처럼 명령이 하나만 실행되는 경우에만 작동합니다. 즉 npm run start-4200

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

명령 줄 매개 변수를 전달하기 위해 실행되지만 npm run build c : / workspace / file 과 같이 둘 이상의 명령을 함께 실행하면 어떻게 됩니까?

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

그러나 복사 c : / file && ng build c : / work space / file 을 실행하는 동안 이와 같은 인터프리터 가 될 것입니다.이 사본 c : / file c : / work space / file && ng build

참고 :-따라서 명령 행 매개 변수는 스크립트에서 명령이 하나만있는 경우에만 작동합니다.

위의 답변 중 일부는 $ 기호를 사용하여 명령 줄 매개 변수에 액세스 할 수 있다고 쓰고 있지만 작동하지 않습니다.


0

이미 승인 된 답변이 있다는 것을 알고 있지만이 JSON 방식과 비슷합니다.

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

일반적으로 프로젝트 이름과 같은 1 var가 필요 하므로이 빠른 n '단순을 찾으십시오.

또한 나는 종종 내 패키지에 이와 같은 것을 가지고있다.

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

욕심 많은 나는 "모든 것", NODE_ENV CMD 라인 인수를 원한다 .

당신은 단순히 파일 (이 경우 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);

이 비트 위에 있어야합니다 (v10.16.0 btw를 실행 중입니다)

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

Anyhoo, 질문에 이미 답변했습니다. 이 방법을 많이 사용하면서 공유하고 싶다고 생각했습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.