├── .documentup.json ├── .gitignore ├── .jshintrc ├── .npmignore ├── .travis.yml ├── LICENSE ├── README.md ├── RELEASE.md ├── bin └── shjs ├── global.js ├── make.js ├── package.json ├── scripts ├── generate-docs.js └── run-tests.js ├── shell.js ├── src ├── cat.js ├── cd.js ├── chmod.js ├── common.js ├── cp.js ├── dirs.js ├── echo.js ├── error.js ├── exec.js ├── find.js ├── grep.js ├── ln.js ├── ls.js ├── mkdir.js ├── mv.js ├── popd.js ├── pushd.js ├── pwd.js ├── rm.js ├── sed.js ├── tempdir.js ├── test.js ├── to.js ├── toEnd.js └── which.js └── test ├── .gitignore ├── cat.js ├── cd.js ├── chmod.js ├── common.js ├── config.js ├── cp.js ├── dirs.js ├── echo.js ├── env.js ├── exec.js ├── find.js ├── grep.js ├── ln.js ├── ls.js ├── make.js ├── mkdir.js ├── mv.js ├── popd.js ├── pushd.js ├── pwd.js ├── resources ├── a.txt ├── badlink ├── chmod │ ├── a │ │ └── b │ │ │ └── c │ │ │ └── .gitignore │ ├── b │ │ └── a │ │ │ └── b │ │ │ └── .gitignore │ ├── c │ │ └── a │ │ │ └── b │ │ │ └── .gitignore │ ├── file1 │ └── xdir │ │ ├── deep │ │ └── file │ │ └── file ├── cp-mode-bits │ └── executable ├── cp │ ├── a │ ├── b │ ├── dir_a │ │ └── z │ └── dir_b │ │ └── dir_b_a │ │ └── dir_b_a_a │ │ └── z ├── external │ └── node_script.js ├── file1 ├── file1.js ├── file1.txt ├── file2 ├── file2.js ├── file2.txt ├── find │ ├── .hidden │ ├── a │ ├── b │ ├── broken_link │ ├── dir1 │ │ ├── a_dir1 │ │ └── dir11 │ │ │ └── a_dir11 │ └── dir2 │ │ └── a_dir1 ├── issue44 │ └── main.js ├── link ├── ls │ ├── .hidden_dir │ │ └── nada │ ├── .hidden_file │ ├── a_dir │ │ ├── .hidden_dir │ │ │ └── nada │ │ ├── b_dir │ │ │ └── z │ │ └── nada │ ├── file1 │ ├── file1.js │ ├── file2 │ ├── file2.js │ └── filename(with)[chars$]^that.must+be-escaped ├── pushd │ ├── a │ │ └── dummy │ └── b │ │ └── c │ │ └── dummy └── rm │ ├── a_dir │ └── a_file │ └── link_to_a_dir ├── rm.js ├── sed.js ├── tempdir.js ├── test.js ├── to.js ├── toEnd.js └── which.js /.documentup.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ShellJS", 3 | "twitter": [ 4 | "r2r" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "loopfunc": true, 3 | "sub": true, 4 | "undef": true, 5 | "unused": true, 6 | "node": true 7 | } -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | test/ 2 | tmp/ -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "0.10" 4 | - "0.11" 5 | - "0.12" 6 | - "iojs-1" 7 | - "iojs-2" 8 | - "iojs-3" 9 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012, Artur Adib 2 | All rights reserved. 3 | 4 | You may use this project under the terms of the New BSD license as follows: 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | * Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | * Redistributions in binary form must reproduce the above copyright 11 | notice, this list of conditions and the following disclaimer in the 12 | documentation and/or other materials provided with the distribution. 13 | * Neither the name of Artur Adib nor the 14 | names of the contributors may be used to endorse or promote products 15 | derived from this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | ARE DISCLAIMED. IN NO EVENT SHALL ARTUR ADIB BE LIABLE FOR ANY 21 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 22 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 23 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 24 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26 | THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ShellJS - Unix shell commands for Node.js [![Build Status](https://secure.travis-ci.org/arturadib/shelljs.png)](http://travis-ci.org/arturadib/shelljs) 2 | 3 | ShellJS is a portable **(Windows/Linux/OS X)** implementation of Unix shell commands on top of the Node.js API. You can use it to eliminate your shell script's dependency on Unix while still keeping its familiar and powerful commands. You can also install it globally so you can run it from outside Node projects - say goodbye to those gnarly Bash scripts! 4 | 5 | The project is [unit-tested](http://travis-ci.org/arturadib/shelljs) and battled-tested in projects like: 6 | 7 | + [PDF.js](http://github.com/mozilla/pdf.js) - Firefox's next-gen PDF reader 8 | + [Firebug](http://getfirebug.com/) - Firefox's infamous debugger 9 | + [JSHint](http://jshint.com) - Most popular JavaScript linter 10 | + [Zepto](http://zeptojs.com) - jQuery-compatible JavaScript library for modern browsers 11 | + [Yeoman](http://yeoman.io/) - Web application stack and development tool 12 | + [Deployd.com](http://deployd.com) - Open source PaaS for quick API backend generation 13 | 14 | and [many more](https://npmjs.org/browse/depended/shelljs). 15 | 16 | Connect with [@r2r](http://twitter.com/r2r) on Twitter for questions, suggestions, etc. 17 | 18 | ## Installing 19 | 20 | Via npm: 21 | 22 | ```bash 23 | $ npm install [-g] shelljs 24 | ``` 25 | 26 | If the global option `-g` is specified, the binary `shjs` will be installed. This makes it possible to 27 | run ShellJS scripts much like any shell script from the command line, i.e. without requiring a `node_modules` folder: 28 | 29 | ```bash 30 | $ shjs my_script 31 | ``` 32 | 33 | You can also just copy `shell.js` into your project's directory, and `require()` accordingly. 34 | 35 | 36 | ## Examples 37 | 38 | ### JavaScript 39 | 40 | ```javascript 41 | require('shelljs/global'); 42 | 43 | if (!which('git')) { 44 | echo('Sorry, this script requires git'); 45 | exit(1); 46 | } 47 | 48 | // Copy files to release dir 49 | mkdir('-p', 'out/Release'); 50 | cp('-R', 'stuff/*', 'out/Release'); 51 | 52 | // Replace macros in each .js file 53 | cd('lib'); 54 | ls('*.js').forEach(function(file) { 55 | sed('-i', 'BUILD_VERSION', 'v0.1.2', file); 56 | sed('-i', /.*REMOVE_THIS_LINE.*\n/, '', file); 57 | sed('-i', /.*REPLACE_LINE_WITH_MACRO.*\n/, cat('macro.js'), file); 58 | }); 59 | cd('..'); 60 | 61 | // Run external tool synchronously 62 | if (exec('git commit -am "Auto-commit"').code !== 0) { 63 | echo('Error: Git commit failed'); 64 | exit(1); 65 | } 66 | ``` 67 | 68 | ### CoffeeScript 69 | 70 | ```coffeescript 71 | require 'shelljs/global' 72 | 73 | if not which 'git' 74 | echo 'Sorry, this script requires git' 75 | exit 1 76 | 77 | # Copy files to release dir 78 | mkdir '-p', 'out/Release' 79 | cp '-R', 'stuff/*', 'out/Release' 80 | 81 | # Replace macros in each .js file 82 | cd 'lib' 83 | for file in ls '*.js' 84 | sed '-i', 'BUILD_VERSION', 'v0.1.2', file 85 | sed '-i', /.*REMOVE_THIS_LINE.*\n/, '', file 86 | sed '-i', /.*REPLACE_LINE_WITH_MACRO.*\n/, cat('macro.js'), file 87 | cd '..' 88 | 89 | # Run external tool synchronously 90 | if (exec 'git commit -am "Auto-commit"').code != 0 91 | echo 'Error: Git commit failed' 92 | exit 1 93 | ``` 94 | 95 | ## Global vs. Local 96 | 97 | The example above uses the convenience script `shelljs/global` to reduce verbosity. If polluting your global namespace is not desirable, simply require `shelljs`. 98 | 99 | Example: 100 | 101 | ```javascript 102 | var shell = require('shelljs'); 103 | shell.echo('hello world'); 104 | ``` 105 | 106 | ## Make tool 107 | 108 | A convenience script `shelljs/make` is also provided to mimic the behavior of a Unix Makefile. In this case all shell objects are global, and command line arguments will cause the script to execute only the corresponding function in the global `target` object. To avoid redundant calls, target functions are executed only once per script. 109 | 110 | Example (CoffeeScript): 111 | 112 | ```coffeescript 113 | require 'shelljs/make' 114 | 115 | target.all = -> 116 | target.bundle() 117 | target.docs() 118 | 119 | target.bundle = -> 120 | cd __dirname 121 | mkdir 'build' 122 | cd 'lib' 123 | (cat '*.js').to '../build/output.js' 124 | 125 | target.docs = -> 126 | cd __dirname 127 | mkdir 'docs' 128 | cd 'lib' 129 | for file in ls '*.js' 130 | text = grep '//@', file # extract special comments 131 | text.replace '//@', '' # remove comment tags 132 | text.to 'docs/my_docs.md' 133 | ``` 134 | 135 | To run the target `all`, call the above script without arguments: `$ node make`. To run the target `docs`: `$ node make docs`. 136 | 137 | You can also pass arguments to your targets by using the `--` separator. For example, to pass `arg1` and `arg2` to a target `bundle`, do `$ node make bundle -- arg1 arg2`: 138 | 139 | ```javascript 140 | require('shelljs/make'); 141 | 142 | target.bundle = function(argsArray) { 143 | // argsArray = ['arg1', 'arg2'] 144 | /* ... */ 145 | } 146 | ``` 147 | 148 | 149 | 150 | 151 | 152 | ## Command reference 153 | 154 | 155 | All commands run synchronously, unless otherwise stated. 156 | 157 | 158 | ### cd('dir') 159 | Changes to directory `dir` for the duration of the script 160 | 161 | 162 | ### pwd() 163 | Returns the current directory. 164 | 165 | 166 | ### ls([options ,] path [,path ...]) 167 | ### ls([options ,] path_array) 168 | Available options: 169 | 170 | + `-R`: recursive 171 | + `-A`: all files (include files beginning with `.`, except for `.` and `..`) 172 | 173 | Examples: 174 | 175 | ```javascript 176 | ls('projs/*.js'); 177 | ls('-R', '/users/me', '/tmp'); 178 | ls('-R', ['/users/me', '/tmp']); // same as above 179 | ``` 180 | 181 | Returns array of files in the given path, or in current directory if no path provided. 182 | 183 | 184 | ### find(path [,path ...]) 185 | ### find(path_array) 186 | Examples: 187 | 188 | ```javascript 189 | find('src', 'lib'); 190 | find(['src', 'lib']); // same as above 191 | find('.').filter(function(file) { return file.match(/\.js$/); }); 192 | ``` 193 | 194 | Returns array of all files (however deep) in the given paths. 195 | 196 | The main difference from `ls('-R', path)` is that the resulting file names 197 | include the base directories, e.g. `lib/resources/file1` instead of just `file1`. 198 | 199 | 200 | ### cp([options ,] source [,source ...], dest) 201 | ### cp([options ,] source_array, dest) 202 | Available options: 203 | 204 | + `-f`: force 205 | + `-r, -R`: recursive 206 | 207 | Examples: 208 | 209 | ```javascript 210 | cp('file1', 'dir1'); 211 | cp('-Rf', '/tmp/*', '/usr/local/*', '/home/tmp'); 212 | cp('-Rf', ['/tmp/*', '/usr/local/*'], '/home/tmp'); // same as above 213 | ``` 214 | 215 | Copies files. The wildcard `*` is accepted. 216 | 217 | 218 | ### rm([options ,] file [, file ...]) 219 | ### rm([options ,] file_array) 220 | Available options: 221 | 222 | + `-f`: force 223 | + `-r, -R`: recursive 224 | 225 | Examples: 226 | 227 | ```javascript 228 | rm('-rf', '/tmp/*'); 229 | rm('some_file.txt', 'another_file.txt'); 230 | rm(['some_file.txt', 'another_file.txt']); // same as above 231 | ``` 232 | 233 | Removes files. The wildcard `*` is accepted. 234 | 235 | 236 | ### mv(source [, source ...], dest') 237 | ### mv(source_array, dest') 238 | Available options: 239 | 240 | + `f`: force 241 | 242 | Examples: 243 | 244 | ```javascript 245 | mv('-f', 'file', 'dir/'); 246 | mv('file1', 'file2', 'dir/'); 247 | mv(['file1', 'file2'], 'dir/'); // same as above 248 | ``` 249 | 250 | Moves files. The wildcard `*` is accepted. 251 | 252 | 253 | ### mkdir([options ,] dir [, dir ...]) 254 | ### mkdir([options ,] dir_array) 255 | Available options: 256 | 257 | + `p`: full path (will create intermediate dirs if necessary) 258 | 259 | Examples: 260 | 261 | ```javascript 262 | mkdir('-p', '/tmp/a/b/c/d', '/tmp/e/f/g'); 263 | mkdir('-p', ['/tmp/a/b/c/d', '/tmp/e/f/g']); // same as above 264 | ``` 265 | 266 | Creates directories. 267 | 268 | 269 | ### test(expression) 270 | Available expression primaries: 271 | 272 | + `'-b', 'path'`: true if path is a block device 273 | + `'-c', 'path'`: true if path is a character device 274 | + `'-d', 'path'`: true if path is a directory 275 | + `'-e', 'path'`: true if path exists 276 | + `'-f', 'path'`: true if path is a regular file 277 | + `'-L', 'path'`: true if path is a symbolic link 278 | + `'-p', 'path'`: true if path is a pipe (FIFO) 279 | + `'-S', 'path'`: true if path is a socket 280 | 281 | Examples: 282 | 283 | ```javascript 284 | if (test('-d', path)) { /* do something with dir */ }; 285 | if (!test('-f', path)) continue; // skip if it's a regular file 286 | ``` 287 | 288 | Evaluates expression using the available primaries and returns corresponding value. 289 | 290 | 291 | ### cat(file [, file ...]) 292 | ### cat(file_array) 293 | 294 | Examples: 295 | 296 | ```javascript 297 | var str = cat('file*.txt'); 298 | var str = cat('file1', 'file2'); 299 | var str = cat(['file1', 'file2']); // same as above 300 | ``` 301 | 302 | Returns a string containing the given file, or a concatenated string 303 | containing the files if more than one file is given (a new line character is 304 | introduced between each file). Wildcard `*` accepted. 305 | 306 | 307 | ### 'string'.to(file) 308 | 309 | Examples: 310 | 311 | ```javascript 312 | cat('input.txt').to('output.txt'); 313 | ``` 314 | 315 | Analogous to the redirection operator `>` in Unix, but works with JavaScript strings (such as 316 | those returned by `cat`, `grep`, etc). _Like Unix redirections, `to()` will overwrite any existing file!_ 317 | 318 | 319 | ### 'string'.toEnd(file) 320 | 321 | Examples: 322 | 323 | ```javascript 324 | cat('input.txt').toEnd('output.txt'); 325 | ``` 326 | 327 | Analogous to the redirect-and-append operator `>>` in Unix, but works with JavaScript strings (such as 328 | those returned by `cat`, `grep`, etc). 329 | 330 | 331 | ### sed([options ,] search_regex, replacement, file) 332 | Available options: 333 | 334 | + `-i`: Replace contents of 'file' in-place. _Note that no backups will be created!_ 335 | 336 | Examples: 337 | 338 | ```javascript 339 | sed('-i', 'PROGRAM_VERSION', 'v0.1.3', 'source.js'); 340 | sed(/.*DELETE_THIS_LINE.*\n/, '', 'source.js'); 341 | ``` 342 | 343 | Reads an input string from `file` and performs a JavaScript `replace()` on the input 344 | using the given search regex and replacement string or function. Returns the new string after replacement. 345 | 346 | 347 | ### grep([options ,] regex_filter, file [, file ...]) 348 | ### grep([options ,] regex_filter, file_array) 349 | Available options: 350 | 351 | + `-v`: Inverse the sense of the regex and print the lines not matching the criteria. 352 | 353 | Examples: 354 | 355 | ```javascript 356 | grep('-v', 'GLOBAL_VARIABLE', '*.js'); 357 | grep('GLOBAL_VARIABLE', '*.js'); 358 | ``` 359 | 360 | Reads input string from given files and returns a string containing all lines of the 361 | file that match the given `regex_filter`. Wildcard `*` accepted. 362 | 363 | 364 | ### which(command) 365 | 366 | Examples: 367 | 368 | ```javascript 369 | var nodeExec = which('node'); 370 | ``` 371 | 372 | Searches for `command` in the system's PATH. On Windows looks for `.exe`, `.cmd`, and `.bat` extensions. 373 | Returns string containing the absolute path to the command. 374 | 375 | 376 | ### echo(string [,string ...]) 377 | 378 | Examples: 379 | 380 | ```javascript 381 | echo('hello world'); 382 | var str = echo('hello world'); 383 | ``` 384 | 385 | Prints string to stdout, and returns string with additional utility methods 386 | like `.to()`. 387 | 388 | 389 | ### pushd([options,] [dir | '-N' | '+N']) 390 | 391 | Available options: 392 | 393 | + `-n`: Suppresses the normal change of directory when adding directories to the stack, so that only the stack is manipulated. 394 | 395 | Arguments: 396 | 397 | + `dir`: Makes the current working directory be the top of the stack, and then executes the equivalent of `cd dir`. 398 | + `+N`: Brings the Nth directory (counting from the left of the list printed by dirs, starting with zero) to the top of the list by rotating the stack. 399 | + `-N`: Brings the Nth directory (counting from the right of the list printed by dirs, starting with zero) to the top of the list by rotating the stack. 400 | 401 | Examples: 402 | 403 | ```javascript 404 | // process.cwd() === '/usr' 405 | pushd('/etc'); // Returns /etc /usr 406 | pushd('+1'); // Returns /usr /etc 407 | ``` 408 | 409 | Save the current directory on the top of the directory stack and then cd to `dir`. With no arguments, pushd exchanges the top two directories. Returns an array of paths in the stack. 410 | 411 | ### popd([options,] ['-N' | '+N']) 412 | 413 | Available options: 414 | 415 | + `-n`: Suppresses the normal change of directory when removing directories from the stack, so that only the stack is manipulated. 416 | 417 | Arguments: 418 | 419 | + `+N`: Removes the Nth directory (counting from the left of the list printed by dirs), starting with zero. 420 | + `-N`: Removes the Nth directory (counting from the right of the list printed by dirs), starting with zero. 421 | 422 | Examples: 423 | 424 | ```javascript 425 | echo(process.cwd()); // '/usr' 426 | pushd('/etc'); // '/etc /usr' 427 | echo(process.cwd()); // '/etc' 428 | popd(); // '/usr' 429 | echo(process.cwd()); // '/usr' 430 | ``` 431 | 432 | When no arguments are given, popd removes the top directory from the stack and performs a cd to the new top directory. The elements are numbered from 0 starting at the first directory listed with dirs; i.e., popd is equivalent to popd +0. Returns an array of paths in the stack. 433 | 434 | ### dirs([options | '+N' | '-N']) 435 | 436 | Available options: 437 | 438 | + `-c`: Clears the directory stack by deleting all of the elements. 439 | 440 | Arguments: 441 | 442 | + `+N`: Displays the Nth directory (counting from the left of the list printed by dirs when invoked without options), starting with zero. 443 | + `-N`: Displays the Nth directory (counting from the right of the list printed by dirs when invoked without options), starting with zero. 444 | 445 | Display the list of currently remembered directories. Returns an array of paths in the stack, or a single path if +N or -N was specified. 446 | 447 | See also: pushd, popd 448 | 449 | 450 | ### ln(options, source, dest) 451 | ### ln(source, dest) 452 | Available options: 453 | 454 | + `s`: symlink 455 | + `f`: force 456 | 457 | Examples: 458 | 459 | ```javascript 460 | ln('file', 'newlink'); 461 | ln('-sf', 'file', 'existing'); 462 | ``` 463 | 464 | Links source to dest. Use -f to force the link, should dest already exist. 465 | 466 | 467 | ### exit(code) 468 | Exits the current process with the given exit code. 469 | 470 | ### env['VAR_NAME'] 471 | Object containing environment variables (both getter and setter). Shortcut to process.env. 472 | 473 | ### exec(command [, options] [, callback]) 474 | Available options (all `false` by default): 475 | 476 | + `async`: Asynchronous execution. Defaults to true if a callback is provided. 477 | + `silent`: Do not echo program output to console. 478 | 479 | Examples: 480 | 481 | ```javascript 482 | var version = exec('node --version', {silent:true}).output; 483 | 484 | var child = exec('some_long_running_process', {async:true}); 485 | child.stdout.on('data', function(data) { 486 | /* ... do something with data ... */ 487 | }); 488 | 489 | exec('some_long_running_process', function(code, output) { 490 | console.log('Exit code:', code); 491 | console.log('Program output:', output); 492 | }); 493 | ``` 494 | 495 | Executes the given `command` _synchronously_, unless otherwise specified. 496 | When in synchronous mode returns the object `{ code:..., output:... }`, containing the program's 497 | `output` (stdout + stderr) and its exit `code`. Otherwise returns the child process object, and 498 | the `callback` gets the arguments `(code, output)`. 499 | 500 | **Note:** For long-lived processes, it's best to run `exec()` asynchronously as 501 | the current synchronous implementation uses a lot of CPU. This should be getting 502 | fixed soon. 503 | 504 | 505 | ### chmod(octal_mode || octal_string, file) 506 | ### chmod(symbolic_mode, file) 507 | 508 | Available options: 509 | 510 | + `-v`: output a diagnostic for every file processed 511 | + `-c`: like verbose but report only when a change is made 512 | + `-R`: change files and directories recursively 513 | 514 | Examples: 515 | 516 | ```javascript 517 | chmod(755, '/Users/brandon'); 518 | chmod('755', '/Users/brandon'); // same as above 519 | chmod('u+x', '/Users/brandon'); 520 | ``` 521 | 522 | Alters the permissions of a file or directory by either specifying the 523 | absolute permissions in octal form or expressing the changes in symbols. 524 | This command tries to mimic the POSIX behavior as much as possible. 525 | Notable exceptions: 526 | 527 | + In symbolic modes, 'a-r' and '-r' are identical. No consideration is 528 | given to the umask. 529 | + There is no "quiet" option since default behavior is to run silent. 530 | 531 | 532 | ## Non-Unix commands 533 | 534 | 535 | ### tempdir() 536 | 537 | Examples: 538 | 539 | ```javascript 540 | var tmp = tempdir(); // "/tmp" for most *nix platforms 541 | ``` 542 | 543 | Searches and returns string containing a writeable, platform-dependent temporary directory. 544 | Follows Python's [tempfile algorithm](http://docs.python.org/library/tempfile.html#tempfile.tempdir). 545 | 546 | 547 | ### error() 548 | Tests if error occurred in the last command. Returns `null` if no error occurred, 549 | otherwise returns string explaining the error 550 | 551 | 552 | ## Configuration 553 | 554 | 555 | ### config.silent 556 | Example: 557 | 558 | ```javascript 559 | var sh = require('shelljs'); 560 | var silentState = sh.config.silent; // save old silent state 561 | sh.config.silent = true; 562 | /* ... */ 563 | sh.config.silent = silentState; // restore old silent state 564 | ``` 565 | 566 | Suppresses all command output if `true`, except for `echo()` calls. 567 | Default is `false`. 568 | 569 | ### config.fatal 570 | Example: 571 | 572 | ```javascript 573 | require('shelljs/global'); 574 | config.fatal = true; 575 | cp('this_file_does_not_exist', '/dev/null'); // dies here 576 | /* more commands... */ 577 | ``` 578 | 579 | If `true` the script will die on errors. Default is `false`. 580 | -------------------------------------------------------------------------------- /RELEASE.md: -------------------------------------------------------------------------------- 1 | # Release steps 2 | 3 | * Ensure master passes CI tests 4 | * Bump version in package.json. Any breaking change or new feature should bump minor (or even major). Non-breaking changes or fixes can just bump patch. 5 | * Update README manually if the changes are not documented in-code. If so, run `scripts/generate-docs.js` 6 | * Commit 7 | * `$ git tag ` (see `git tag -l` for latest) 8 | * `$ git push origin master --tags` 9 | * `$ npm publish .` 10 | -------------------------------------------------------------------------------- /bin/shjs: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | require('../global'); 3 | 4 | if (process.argv.length < 3) { 5 | console.log('ShellJS: missing argument (script name)'); 6 | console.log(); 7 | process.exit(1); 8 | } 9 | 10 | var args, 11 | scriptName = process.argv[2]; 12 | env['NODE_PATH'] = __dirname + '/../..'; 13 | 14 | if (!scriptName.match(/\.js/) && !scriptName.match(/\.coffee/)) { 15 | if (test('-f', scriptName + '.js')) 16 | scriptName += '.js'; 17 | if (test('-f', scriptName + '.coffee')) 18 | scriptName += '.coffee'; 19 | } 20 | 21 | if (!test('-f', scriptName)) { 22 | console.log('ShellJS: script not found ('+scriptName+')'); 23 | console.log(); 24 | process.exit(1); 25 | } 26 | 27 | args = process.argv.slice(3); 28 | 29 | for (var i = 0, l = args.length; i < l; i++) { 30 | if (args[i][0] !== "-"){ 31 | args[i] = '"' + args[i] + '"'; // fixes arguments with multiple words 32 | } 33 | } 34 | 35 | if (scriptName.match(/\.coffee$/)) { 36 | // 37 | // CoffeeScript 38 | // 39 | if (which('coffee')) { 40 | exec('coffee "' + scriptName + '" ' + args.join(' '), { async: true }); 41 | } else { 42 | console.log('ShellJS: CoffeeScript interpreter not found'); 43 | console.log(); 44 | process.exit(1); 45 | } 46 | } else { 47 | // 48 | // JavaScript 49 | // 50 | exec('node "' + scriptName + '" ' + args.join(' '), { async: true }); 51 | } 52 | -------------------------------------------------------------------------------- /global.js: -------------------------------------------------------------------------------- 1 | var shell = require('./shell.js'); 2 | for (var cmd in shell) 3 | global[cmd] = shell[cmd]; 4 | -------------------------------------------------------------------------------- /make.js: -------------------------------------------------------------------------------- 1 | require('./global'); 2 | 3 | global.config.fatal = true; 4 | global.target = {}; 5 | 6 | var args = process.argv.slice(2), 7 | targetArgs, 8 | dashesLoc = args.indexOf('--'); 9 | 10 | // split args, everything after -- if only for targets 11 | if (dashesLoc > -1) { 12 | targetArgs = args.slice(dashesLoc + 1, args.length); 13 | args = args.slice(0, dashesLoc); 14 | } 15 | 16 | // This ensures we only execute the script targets after the entire script has 17 | // been evaluated 18 | setTimeout(function() { 19 | var t; 20 | 21 | if (args.length === 1 && args[0] === '--help') { 22 | console.log('Available targets:'); 23 | for (t in global.target) 24 | console.log(' ' + t); 25 | return; 26 | } 27 | 28 | // Wrap targets to prevent duplicate execution 29 | for (t in global.target) { 30 | (function(t, oldTarget){ 31 | 32 | // Wrap it 33 | global.target[t] = function() { 34 | if (!oldTarget.done){ 35 | oldTarget.done = true; 36 | oldTarget.result = oldTarget.apply(oldTarget, arguments); 37 | } 38 | return oldTarget.result; 39 | }; 40 | 41 | })(t, global.target[t]); 42 | } 43 | 44 | // Execute desired targets 45 | if (args.length > 0) { 46 | args.forEach(function(arg) { 47 | if (arg in global.target) 48 | global.target[arg](targetArgs); 49 | else { 50 | console.log('no such target: ' + arg); 51 | } 52 | }); 53 | } else if ('all' in global.target) { 54 | global.target.all(targetArgs); 55 | } 56 | 57 | }, 0); 58 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "shelljs", 3 | "version": "0.5.3", 4 | "author": "Artur Adib ", 5 | "description": "Portable Unix shell commands for Node.js", 6 | "keywords": [ 7 | "unix", 8 | "shell", 9 | "makefile", 10 | "make", 11 | "jake", 12 | "synchronous" 13 | ], 14 | "repository": { 15 | "type": "git", 16 | "url": "git://github.com/arturadib/shelljs.git" 17 | }, 18 | "license": "BSD-3-Clause", 19 | "homepage": "http://github.com/arturadib/shelljs", 20 | "main": "./shell.js", 21 | "scripts": { 22 | "test": "node scripts/run-tests" 23 | }, 24 | "bin": { 25 | "shjs": "./bin/shjs" 26 | }, 27 | "dependencies": {}, 28 | "devDependencies": { 29 | "jshint": "~2.1.11" 30 | }, 31 | "optionalDependencies": {}, 32 | "engines": { 33 | "node": ">=0.8.0" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /scripts/generate-docs.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | require('../global'); 3 | 4 | echo('Appending docs to README.md'); 5 | 6 | cd(__dirname + '/..'); 7 | 8 | // Extract docs from shell.js 9 | var docs = grep('//@', 'shell.js'); 10 | 11 | docs = docs.replace(/\/\/\@include (.+)/g, function(match, path) { 12 | var file = path.match('.js$') ? path : path+'.js'; 13 | return grep('//@', file); 14 | }); 15 | 16 | // Remove '//@' 17 | docs = docs.replace(/\/\/\@ ?/g, ''); 18 | // Append docs to README 19 | sed('-i', /## Command reference(.|\n)*/, '## Command reference\n\n' + docs, 'README.md'); 20 | 21 | echo('All done.'); 22 | -------------------------------------------------------------------------------- /scripts/run-tests.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | require('../global'); 3 | 4 | var path = require('path'); 5 | 6 | var failed = false; 7 | 8 | // 9 | // Lint 10 | // 11 | JSHINT_BIN = './node_modules/jshint/bin/jshint'; 12 | cd(__dirname + '/..'); 13 | 14 | if (!test('-f', JSHINT_BIN)) { 15 | echo('JSHint not found. Run `npm install` in the root dir first.'); 16 | exit(1); 17 | } 18 | 19 | if (exec(JSHINT_BIN + ' *.js test/*.js').code !== 0) { 20 | failed = true; 21 | echo('*** JSHINT FAILED! (return code != 0)'); 22 | echo(); 23 | } else { 24 | echo('All JSHint tests passed'); 25 | echo(); 26 | } 27 | 28 | // 29 | // Unit tests 30 | // 31 | cd(__dirname + '/../test'); 32 | ls('*.js').forEach(function(file) { 33 | echo('Running test:', file); 34 | if (exec('node ' + file).code !== 123) { // 123 avoids false positives (e.g. premature exit) 35 | failed = true; 36 | echo('*** TEST FAILED! (missing exit code "123")'); 37 | echo(); 38 | } 39 | }); 40 | 41 | if (failed) { 42 | echo(); 43 | echo('*******************************************************'); 44 | echo('WARNING: Some tests did not pass!'); 45 | echo('*******************************************************'); 46 | exit(1); 47 | } else { 48 | echo(); 49 | echo('All tests passed.'); 50 | } 51 | -------------------------------------------------------------------------------- /shell.js: -------------------------------------------------------------------------------- 1 | // 2 | // ShellJS 3 | // Unix shell commands on top of Node's API 4 | // 5 | // Copyright (c) 2012 Artur Adib 6 | // http://github.com/arturadib/shelljs 7 | // 8 | 9 | var common = require('./src/common'); 10 | 11 | 12 | //@ 13 | //@ All commands run synchronously, unless otherwise stated. 14 | //@ 15 | 16 | //@include ./src/cd 17 | var _cd = require('./src/cd'); 18 | exports.cd = common.wrap('cd', _cd); 19 | 20 | //@include ./src/pwd 21 | var _pwd = require('./src/pwd'); 22 | exports.pwd = common.wrap('pwd', _pwd); 23 | 24 | //@include ./src/ls 25 | var _ls = require('./src/ls'); 26 | exports.ls = common.wrap('ls', _ls); 27 | 28 | //@include ./src/find 29 | var _find = require('./src/find'); 30 | exports.find = common.wrap('find', _find); 31 | 32 | //@include ./src/cp 33 | var _cp = require('./src/cp'); 34 | exports.cp = common.wrap('cp', _cp); 35 | 36 | //@include ./src/rm 37 | var _rm = require('./src/rm'); 38 | exports.rm = common.wrap('rm', _rm); 39 | 40 | //@include ./src/mv 41 | var _mv = require('./src/mv'); 42 | exports.mv = common.wrap('mv', _mv); 43 | 44 | //@include ./src/mkdir 45 | var _mkdir = require('./src/mkdir'); 46 | exports.mkdir = common.wrap('mkdir', _mkdir); 47 | 48 | //@include ./src/test 49 | var _test = require('./src/test'); 50 | exports.test = common.wrap('test', _test); 51 | 52 | //@include ./src/cat 53 | var _cat = require('./src/cat'); 54 | exports.cat = common.wrap('cat', _cat); 55 | 56 | //@include ./src/to 57 | var _to = require('./src/to'); 58 | String.prototype.to = common.wrap('to', _to); 59 | 60 | //@include ./src/toEnd 61 | var _toEnd = require('./src/toEnd'); 62 | String.prototype.toEnd = common.wrap('toEnd', _toEnd); 63 | 64 | //@include ./src/sed 65 | var _sed = require('./src/sed'); 66 | exports.sed = common.wrap('sed', _sed); 67 | 68 | //@include ./src/grep 69 | var _grep = require('./src/grep'); 70 | exports.grep = common.wrap('grep', _grep); 71 | 72 | //@include ./src/which 73 | var _which = require('./src/which'); 74 | exports.which = common.wrap('which', _which); 75 | 76 | //@include ./src/echo 77 | var _echo = require('./src/echo'); 78 | exports.echo = _echo; // don't common.wrap() as it could parse '-options' 79 | 80 | //@include ./src/dirs 81 | var _dirs = require('./src/dirs').dirs; 82 | exports.dirs = common.wrap("dirs", _dirs); 83 | var _pushd = require('./src/dirs').pushd; 84 | exports.pushd = common.wrap('pushd', _pushd); 85 | var _popd = require('./src/dirs').popd; 86 | exports.popd = common.wrap("popd", _popd); 87 | 88 | //@include ./src/ln 89 | var _ln = require('./src/ln'); 90 | exports.ln = common.wrap('ln', _ln); 91 | 92 | //@ 93 | //@ ### exit(code) 94 | //@ Exits the current process with the given exit code. 95 | exports.exit = process.exit; 96 | 97 | //@ 98 | //@ ### env['VAR_NAME'] 99 | //@ Object containing environment variables (both getter and setter). Shortcut to process.env. 100 | exports.env = process.env; 101 | 102 | //@include ./src/exec 103 | var _exec = require('./src/exec'); 104 | exports.exec = common.wrap('exec', _exec, {notUnix:true}); 105 | 106 | //@include ./src/chmod 107 | var _chmod = require('./src/chmod'); 108 | exports.chmod = common.wrap('chmod', _chmod); 109 | 110 | 111 | 112 | //@ 113 | //@ ## Non-Unix commands 114 | //@ 115 | 116 | //@include ./src/tempdir 117 | var _tempDir = require('./src/tempdir'); 118 | exports.tempdir = common.wrap('tempdir', _tempDir); 119 | 120 | 121 | //@include ./src/error 122 | var _error = require('./src/error'); 123 | exports.error = _error; 124 | 125 | 126 | 127 | //@ 128 | //@ ## Configuration 129 | //@ 130 | 131 | exports.config = common.config; 132 | 133 | //@ 134 | //@ ### config.silent 135 | //@ Example: 136 | //@ 137 | //@ ```javascript 138 | //@ var sh = require('shelljs'); 139 | //@ var silentState = sh.config.silent; // save old silent state 140 | //@ sh.config.silent = true; 141 | //@ /* ... */ 142 | //@ sh.config.silent = silentState; // restore old silent state 143 | //@ ``` 144 | //@ 145 | //@ Suppresses all command output if `true`, except for `echo()` calls. 146 | //@ Default is `false`. 147 | 148 | //@ 149 | //@ ### config.fatal 150 | //@ Example: 151 | //@ 152 | //@ ```javascript 153 | //@ require('shelljs/global'); 154 | //@ config.fatal = true; 155 | //@ cp('this_file_does_not_exist', '/dev/null'); // dies here 156 | //@ /* more commands... */ 157 | //@ ``` 158 | //@ 159 | //@ If `true` the script will die on errors. Default is `false`. 160 | -------------------------------------------------------------------------------- /src/cat.js: -------------------------------------------------------------------------------- 1 | var common = require('./common'); 2 | var fs = require('fs'); 3 | 4 | //@ 5 | //@ ### cat(file [, file ...]) 6 | //@ ### cat(file_array) 7 | //@ 8 | //@ Examples: 9 | //@ 10 | //@ ```javascript 11 | //@ var str = cat('file*.txt'); 12 | //@ var str = cat('file1', 'file2'); 13 | //@ var str = cat(['file1', 'file2']); // same as above 14 | //@ ``` 15 | //@ 16 | //@ Returns a string containing the given file, or a concatenated string 17 | //@ containing the files if more than one file is given (a new line character is 18 | //@ introduced between each file). Wildcard `*` accepted. 19 | function _cat(options, files) { 20 | var cat = ''; 21 | 22 | if (!files) 23 | common.error('no paths given'); 24 | 25 | if (typeof files === 'string') 26 | files = [].slice.call(arguments, 1); 27 | // if it's array leave it as it is 28 | 29 | files = common.expand(files); 30 | 31 | files.forEach(function(file) { 32 | if (!fs.existsSync(file)) 33 | common.error('no such file or directory: ' + file); 34 | 35 | cat += fs.readFileSync(file, 'utf8') + '\n'; 36 | }); 37 | 38 | if (cat[cat.length-1] === '\n') 39 | cat = cat.substring(0, cat.length-1); 40 | 41 | return common.ShellString(cat); 42 | } 43 | module.exports = _cat; 44 | -------------------------------------------------------------------------------- /src/cd.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs'); 2 | var common = require('./common'); 3 | 4 | //@ 5 | //@ ### cd('dir') 6 | //@ Changes to directory `dir` for the duration of the script 7 | function _cd(options, dir) { 8 | if (!dir) 9 | common.error('directory not specified'); 10 | 11 | if (!fs.existsSync(dir)) 12 | common.error('no such file or directory: ' + dir); 13 | 14 | if (!fs.statSync(dir).isDirectory()) 15 | common.error('not a directory: ' + dir); 16 | 17 | process.chdir(dir); 18 | } 19 | module.exports = _cd; 20 | -------------------------------------------------------------------------------- /src/chmod.js: -------------------------------------------------------------------------------- 1 | var common = require('./common'); 2 | var fs = require('fs'); 3 | var path = require('path'); 4 | 5 | var PERMS = (function (base) { 6 | return { 7 | OTHER_EXEC : base.EXEC, 8 | OTHER_WRITE : base.WRITE, 9 | OTHER_READ : base.READ, 10 | 11 | GROUP_EXEC : base.EXEC << 3, 12 | GROUP_WRITE : base.WRITE << 3, 13 | GROUP_READ : base.READ << 3, 14 | 15 | OWNER_EXEC : base.EXEC << 6, 16 | OWNER_WRITE : base.WRITE << 6, 17 | OWNER_READ : base.READ << 6, 18 | 19 | // Literal octal numbers are apparently not allowed in "strict" javascript. Using parseInt is 20 | // the preferred way, else a jshint warning is thrown. 21 | STICKY : parseInt('01000', 8), 22 | SETGID : parseInt('02000', 8), 23 | SETUID : parseInt('04000', 8), 24 | 25 | TYPE_MASK : parseInt('0770000', 8) 26 | }; 27 | })({ 28 | EXEC : 1, 29 | WRITE : 2, 30 | READ : 4 31 | }); 32 | 33 | //@ 34 | //@ ### chmod(octal_mode || octal_string, file) 35 | //@ ### chmod(symbolic_mode, file) 36 | //@ 37 | //@ Available options: 38 | //@ 39 | //@ + `-v`: output a diagnostic for every file processed//@ 40 | //@ + `-c`: like verbose but report only when a change is made//@ 41 | //@ + `-R`: change files and directories recursively//@ 42 | //@ 43 | //@ Examples: 44 | //@ 45 | //@ ```javascript 46 | //@ chmod(755, '/Users/brandon'); 47 | //@ chmod('755', '/Users/brandon'); // same as above 48 | //@ chmod('u+x', '/Users/brandon'); 49 | //@ ``` 50 | //@ 51 | //@ Alters the permissions of a file or directory by either specifying the 52 | //@ absolute permissions in octal form or expressing the changes in symbols. 53 | //@ This command tries to mimic the POSIX behavior as much as possible. 54 | //@ Notable exceptions: 55 | //@ 56 | //@ + In symbolic modes, 'a-r' and '-r' are identical. No consideration is 57 | //@ given to the umask. 58 | //@ + There is no "quiet" option since default behavior is to run silent. 59 | function _chmod(options, mode, filePattern) { 60 | if (!filePattern) { 61 | if (options.length > 0 && options.charAt(0) === '-') { 62 | // Special case where the specified file permissions started with - to subtract perms, which 63 | // get picked up by the option parser as command flags. 64 | // If we are down by one argument and options starts with -, shift everything over. 65 | filePattern = mode; 66 | mode = options; 67 | options = ''; 68 | } 69 | else { 70 | common.error('You must specify a file.'); 71 | } 72 | } 73 | 74 | options = common.parseOptions(options, { 75 | 'R': 'recursive', 76 | 'c': 'changes', 77 | 'v': 'verbose' 78 | }); 79 | 80 | if (typeof filePattern === 'string') { 81 | filePattern = [ filePattern ]; 82 | } 83 | 84 | var files; 85 | 86 | if (options.recursive) { 87 | files = []; 88 | common.expand(filePattern).forEach(function addFile(expandedFile) { 89 | var stat = fs.lstatSync(expandedFile); 90 | 91 | if (!stat.isSymbolicLink()) { 92 | files.push(expandedFile); 93 | 94 | if (stat.isDirectory()) { // intentionally does not follow symlinks. 95 | fs.readdirSync(expandedFile).forEach(function (child) { 96 | addFile(expandedFile + '/' + child); 97 | }); 98 | } 99 | } 100 | }); 101 | } 102 | else { 103 | files = common.expand(filePattern); 104 | } 105 | 106 | files.forEach(function innerChmod(file) { 107 | file = path.resolve(file); 108 | if (!fs.existsSync(file)) { 109 | common.error('File not found: ' + file); 110 | } 111 | 112 | // When recursing, don't follow symlinks. 113 | if (options.recursive && fs.lstatSync(file).isSymbolicLink()) { 114 | return; 115 | } 116 | 117 | var stat = fs.statSync(file); 118 | var isDir = stat.isDirectory(); 119 | var perms = stat.mode; 120 | var type = perms & PERMS.TYPE_MASK; 121 | 122 | var newPerms = perms; 123 | 124 | if (isNaN(parseInt(mode, 8))) { 125 | // parse options 126 | mode.split(',').forEach(function (symbolicMode) { 127 | /*jshint regexdash:true */ 128 | var pattern = /([ugoa]*)([=\+-])([rwxXst]*)/i; 129 | var matches = pattern.exec(symbolicMode); 130 | 131 | if (matches) { 132 | var applyTo = matches[1]; 133 | var operator = matches[2]; 134 | var change = matches[3]; 135 | 136 | var changeOwner = applyTo.indexOf('u') != -1 || applyTo === 'a' || applyTo === ''; 137 | var changeGroup = applyTo.indexOf('g') != -1 || applyTo === 'a' || applyTo === ''; 138 | var changeOther = applyTo.indexOf('o') != -1 || applyTo === 'a' || applyTo === ''; 139 | 140 | var changeRead = change.indexOf('r') != -1; 141 | var changeWrite = change.indexOf('w') != -1; 142 | var changeExec = change.indexOf('x') != -1; 143 | var changeExecDir = change.indexOf('X') != -1; 144 | var changeSticky = change.indexOf('t') != -1; 145 | var changeSetuid = change.indexOf('s') != -1; 146 | 147 | if (changeExecDir && isDir) 148 | changeExec = true; 149 | 150 | var mask = 0; 151 | if (changeOwner) { 152 | mask |= (changeRead ? PERMS.OWNER_READ : 0) + (changeWrite ? PERMS.OWNER_WRITE : 0) + (changeExec ? PERMS.OWNER_EXEC : 0) + (changeSetuid ? PERMS.SETUID : 0); 153 | } 154 | if (changeGroup) { 155 | mask |= (changeRead ? PERMS.GROUP_READ : 0) + (changeWrite ? PERMS.GROUP_WRITE : 0) + (changeExec ? PERMS.GROUP_EXEC : 0) + (changeSetuid ? PERMS.SETGID : 0); 156 | } 157 | if (changeOther) { 158 | mask |= (changeRead ? PERMS.OTHER_READ : 0) + (changeWrite ? PERMS.OTHER_WRITE : 0) + (changeExec ? PERMS.OTHER_EXEC : 0); 159 | } 160 | 161 | // Sticky bit is special - it's not tied to user, group or other. 162 | if (changeSticky) { 163 | mask |= PERMS.STICKY; 164 | } 165 | 166 | switch (operator) { 167 | case '+': 168 | newPerms |= mask; 169 | break; 170 | 171 | case '-': 172 | newPerms &= ~mask; 173 | break; 174 | 175 | case '=': 176 | newPerms = type + mask; 177 | 178 | // According to POSIX, when using = to explicitly set the permissions, setuid and setgid can never be cleared. 179 | if (fs.statSync(file).isDirectory()) { 180 | newPerms |= (PERMS.SETUID + PERMS.SETGID) & perms; 181 | } 182 | break; 183 | } 184 | 185 | if (options.verbose) { 186 | log(file + ' -> ' + newPerms.toString(8)); 187 | } 188 | 189 | if (perms != newPerms) { 190 | if (!options.verbose && options.changes) { 191 | log(file + ' -> ' + newPerms.toString(8)); 192 | } 193 | fs.chmodSync(file, newPerms); 194 | perms = newPerms; // for the next round of changes! 195 | } 196 | } 197 | else { 198 | common.error('Invalid symbolic mode change: ' + symbolicMode); 199 | } 200 | }); 201 | } 202 | else { 203 | // they gave us a full number 204 | newPerms = type + parseInt(mode, 8); 205 | 206 | // POSIX rules are that setuid and setgid can only be added using numeric form, but not cleared. 207 | if (fs.statSync(file).isDirectory()) { 208 | newPerms |= (PERMS.SETUID + PERMS.SETGID) & perms; 209 | } 210 | 211 | fs.chmodSync(file, newPerms); 212 | } 213 | }); 214 | } 215 | module.exports = _chmod; 216 | -------------------------------------------------------------------------------- /src/common.js: -------------------------------------------------------------------------------- 1 | var os = require('os'); 2 | var fs = require('fs'); 3 | var _ls = require('./ls'); 4 | 5 | // Module globals 6 | var config = { 7 | silent: false, 8 | fatal: false 9 | }; 10 | exports.config = config; 11 | 12 | var state = { 13 | error: null, 14 | currentCmd: 'shell.js', 15 | tempDir: null 16 | }; 17 | exports.state = state; 18 | 19 | var platform = os.type().match(/^Win/) ? 'win' : 'unix'; 20 | exports.platform = platform; 21 | 22 | function log() { 23 | if (!config.silent) 24 | console.log.apply(this, arguments); 25 | } 26 | exports.log = log; 27 | 28 | // Shows error message. Throws unless _continue or config.fatal are true 29 | function error(msg, _continue) { 30 | if (state.error === null) 31 | state.error = ''; 32 | state.error += state.currentCmd + ': ' + msg + '\n'; 33 | 34 | if (msg.length > 0) 35 | log(state.error); 36 | 37 | if (config.fatal) 38 | process.exit(1); 39 | 40 | if (!_continue) 41 | throw ''; 42 | } 43 | exports.error = error; 44 | 45 | // In the future, when Proxies are default, we can add methods like `.to()` to primitive strings. 46 | // For now, this is a dummy function to bookmark places we need such strings 47 | function ShellString(str) { 48 | return str; 49 | } 50 | exports.ShellString = ShellString; 51 | 52 | // Returns {'alice': true, 'bob': false} when passed a dictionary, e.g.: 53 | // parseOptions('-a', {'a':'alice', 'b':'bob'}); 54 | function parseOptions(str, map) { 55 | if (!map) 56 | error('parseOptions() internal error: no map given'); 57 | 58 | // All options are false by default 59 | var options = {}; 60 | for (var letter in map) 61 | options[map[letter]] = false; 62 | 63 | if (!str) 64 | return options; // defaults 65 | 66 | if (typeof str !== 'string') 67 | error('parseOptions() internal error: wrong str'); 68 | 69 | // e.g. match[1] = 'Rf' for str = '-Rf' 70 | var match = str.match(/^\-(.+)/); 71 | if (!match) 72 | return options; 73 | 74 | // e.g. chars = ['R', 'f'] 75 | var chars = match[1].split(''); 76 | 77 | chars.forEach(function(c) { 78 | if (c in map) 79 | options[map[c]] = true; 80 | else 81 | error('option not recognized: '+c); 82 | }); 83 | 84 | return options; 85 | } 86 | exports.parseOptions = parseOptions; 87 | 88 | // Expands wildcards with matching (ie. existing) file names. 89 | // For example: 90 | // expand(['file*.js']) = ['file1.js', 'file2.js', ...] 91 | // (if the files 'file1.js', 'file2.js', etc, exist in the current dir) 92 | function expand(list) { 93 | var expanded = []; 94 | list.forEach(function(listEl) { 95 | // Wildcard present on directory names ? 96 | if(listEl.search(/\*[^\/]*\//) > -1 || listEl.search(/\*\*[^\/]*\//) > -1) { 97 | var match = listEl.match(/^([^*]+\/|)(.*)/); 98 | var root = match[1]; 99 | var rest = match[2]; 100 | var restRegex = rest.replace(/\*\*/g, ".*").replace(/\*/g, "[^\\/]*"); 101 | restRegex = new RegExp(restRegex); 102 | 103 | _ls('-R', root).filter(function (e) { 104 | return restRegex.test(e); 105 | }).forEach(function(file) { 106 | expanded.push(file); 107 | }); 108 | } 109 | // Wildcard present on file names ? 110 | else if (listEl.search(/\*/) > -1) { 111 | _ls('', listEl).forEach(function(file) { 112 | expanded.push(file); 113 | }); 114 | } else { 115 | expanded.push(listEl); 116 | } 117 | }); 118 | return expanded; 119 | } 120 | exports.expand = expand; 121 | 122 | // Normalizes _unlinkSync() across platforms to match Unix behavior, i.e. 123 | // file can be unlinked even if it's read-only, see https://github.com/joyent/node/issues/3006 124 | function unlinkSync(file) { 125 | try { 126 | fs.unlinkSync(file); 127 | } catch(e) { 128 | // Try to override file permission 129 | if (e.code === 'EPERM') { 130 | fs.chmodSync(file, '0666'); 131 | fs.unlinkSync(file); 132 | } else { 133 | throw e; 134 | } 135 | } 136 | } 137 | exports.unlinkSync = unlinkSync; 138 | 139 | // e.g. 'shelljs_a5f185d0443ca...' 140 | function randomFileName() { 141 | function randomHash(count) { 142 | if (count === 1) 143 | return parseInt(16*Math.random(), 10).toString(16); 144 | else { 145 | var hash = ''; 146 | for (var i=0; i and/or '); 117 | } else if (arguments.length > 3) { 118 | sources = [].slice.call(arguments, 1, arguments.length - 1); 119 | dest = arguments[arguments.length - 1]; 120 | } else if (typeof sources === 'string') { 121 | sources = [sources]; 122 | } else if ('length' in sources) { 123 | sources = sources; // no-op for array 124 | } else { 125 | common.error('invalid arguments'); 126 | } 127 | 128 | var exists = fs.existsSync(dest), 129 | stats = exists && fs.statSync(dest); 130 | 131 | // Dest is not existing dir, but multiple sources given 132 | if ((!exists || !stats.isDirectory()) && sources.length > 1) 133 | common.error('dest is not a directory (too many sources)'); 134 | 135 | // Dest is an existing file, but no -f given 136 | if (exists && stats.isFile() && !options.force) 137 | common.error('dest file already exists: ' + dest); 138 | 139 | if (options.recursive) { 140 | // Recursive allows the shortcut syntax "sourcedir/" for "sourcedir/*" 141 | // (see Github issue #15) 142 | sources.forEach(function(src, i) { 143 | if (src[src.length - 1] === '/') 144 | sources[i] += '*'; 145 | }); 146 | 147 | // Create dest 148 | try { 149 | fs.mkdirSync(dest, parseInt('0777', 8)); 150 | } catch (e) { 151 | // like Unix's cp, keep going even if we can't create dest dir 152 | } 153 | } 154 | 155 | sources = common.expand(sources); 156 | 157 | sources.forEach(function(src) { 158 | if (!fs.existsSync(src)) { 159 | common.error('no such file or directory: '+src, true); 160 | return; // skip file 161 | } 162 | 163 | // If here, src exists 164 | if (fs.statSync(src).isDirectory()) { 165 | if (!options.recursive) { 166 | // Non-Recursive 167 | common.log(src + ' is a directory (not copied)'); 168 | } else { 169 | // Recursive 170 | // 'cp /a/source dest' should create 'source' in 'dest' 171 | var newDest = path.join(dest, path.basename(src)), 172 | checkDir = fs.statSync(src); 173 | try { 174 | fs.mkdirSync(newDest, checkDir.mode); 175 | } catch (e) { 176 | //if the directory already exists, that's okay 177 | if (e.code !== 'EEXIST') { 178 | common.error('dest file no such file or directory: ' + newDest, true); 179 | throw e; 180 | } 181 | } 182 | 183 | cpdirSyncRecursive(src, newDest, {force: options.force}); 184 | } 185 | return; // done with dir 186 | } 187 | 188 | // If here, src is a file 189 | 190 | // When copying to '/path/dir': 191 | // thisDest = '/path/dir/file1' 192 | var thisDest = dest; 193 | if (fs.existsSync(dest) && fs.statSync(dest).isDirectory()) 194 | thisDest = path.normalize(dest + '/' + path.basename(src)); 195 | 196 | if (fs.existsSync(thisDest) && !options.force) { 197 | common.error('dest file already exists: ' + thisDest, true); 198 | return; // skip file 199 | } 200 | 201 | copyFileSync(src, thisDest); 202 | }); // forEach(src) 203 | } 204 | module.exports = _cp; 205 | -------------------------------------------------------------------------------- /src/dirs.js: -------------------------------------------------------------------------------- 1 | var common = require('./common'); 2 | var _cd = require('./cd'); 3 | var path = require('path'); 4 | 5 | // Pushd/popd/dirs internals 6 | var _dirStack = []; 7 | 8 | function _isStackIndex(index) { 9 | return (/^[\-+]\d+$/).test(index); 10 | } 11 | 12 | function _parseStackIndex(index) { 13 | if (_isStackIndex(index)) { 14 | if (Math.abs(index) < _dirStack.length + 1) { // +1 for pwd 15 | return (/^-/).test(index) ? Number(index) - 1 : Number(index); 16 | } else { 17 | common.error(index + ': directory stack index out of range'); 18 | } 19 | } else { 20 | common.error(index + ': invalid number'); 21 | } 22 | } 23 | 24 | function _actualDirStack() { 25 | return [process.cwd()].concat(_dirStack); 26 | } 27 | 28 | //@ 29 | //@ ### pushd([options,] [dir | '-N' | '+N']) 30 | //@ 31 | //@ Available options: 32 | //@ 33 | //@ + `-n`: Suppresses the normal change of directory when adding directories to the stack, so that only the stack is manipulated. 34 | //@ 35 | //@ Arguments: 36 | //@ 37 | //@ + `dir`: Makes the current working directory be the top of the stack, and then executes the equivalent of `cd dir`. 38 | //@ + `+N`: Brings the Nth directory (counting from the left of the list printed by dirs, starting with zero) to the top of the list by rotating the stack. 39 | //@ + `-N`: Brings the Nth directory (counting from the right of the list printed by dirs, starting with zero) to the top of the list by rotating the stack. 40 | //@ 41 | //@ Examples: 42 | //@ 43 | //@ ```javascript 44 | //@ // process.cwd() === '/usr' 45 | //@ pushd('/etc'); // Returns /etc /usr 46 | //@ pushd('+1'); // Returns /usr /etc 47 | //@ ``` 48 | //@ 49 | //@ Save the current directory on the top of the directory stack and then cd to `dir`. With no arguments, pushd exchanges the top two directories. Returns an array of paths in the stack. 50 | function _pushd(options, dir) { 51 | if (_isStackIndex(options)) { 52 | dir = options; 53 | options = ''; 54 | } 55 | 56 | options = common.parseOptions(options, { 57 | 'n' : 'no-cd' 58 | }); 59 | 60 | var dirs = _actualDirStack(); 61 | 62 | if (dir === '+0') { 63 | return dirs; // +0 is a noop 64 | } else if (!dir) { 65 | if (dirs.length > 1) { 66 | dirs = dirs.splice(1, 1).concat(dirs); 67 | } else { 68 | return common.error('no other directory'); 69 | } 70 | } else if (_isStackIndex(dir)) { 71 | var n = _parseStackIndex(dir); 72 | dirs = dirs.slice(n).concat(dirs.slice(0, n)); 73 | } else { 74 | if (options['no-cd']) { 75 | dirs.splice(1, 0, dir); 76 | } else { 77 | dirs.unshift(dir); 78 | } 79 | } 80 | 81 | if (options['no-cd']) { 82 | dirs = dirs.slice(1); 83 | } else { 84 | dir = path.resolve(dirs.shift()); 85 | _cd('', dir); 86 | } 87 | 88 | _dirStack = dirs; 89 | return _dirs(''); 90 | } 91 | exports.pushd = _pushd; 92 | 93 | //@ 94 | //@ ### popd([options,] ['-N' | '+N']) 95 | //@ 96 | //@ Available options: 97 | //@ 98 | //@ + `-n`: Suppresses the normal change of directory when removing directories from the stack, so that only the stack is manipulated. 99 | //@ 100 | //@ Arguments: 101 | //@ 102 | //@ + `+N`: Removes the Nth directory (counting from the left of the list printed by dirs), starting with zero. 103 | //@ + `-N`: Removes the Nth directory (counting from the right of the list printed by dirs), starting with zero. 104 | //@ 105 | //@ Examples: 106 | //@ 107 | //@ ```javascript 108 | //@ echo(process.cwd()); // '/usr' 109 | //@ pushd('/etc'); // '/etc /usr' 110 | //@ echo(process.cwd()); // '/etc' 111 | //@ popd(); // '/usr' 112 | //@ echo(process.cwd()); // '/usr' 113 | //@ ``` 114 | //@ 115 | //@ When no arguments are given, popd removes the top directory from the stack and performs a cd to the new top directory. The elements are numbered from 0 starting at the first directory listed with dirs; i.e., popd is equivalent to popd +0. Returns an array of paths in the stack. 116 | function _popd(options, index) { 117 | if (_isStackIndex(options)) { 118 | index = options; 119 | options = ''; 120 | } 121 | 122 | options = common.parseOptions(options, { 123 | 'n' : 'no-cd' 124 | }); 125 | 126 | if (!_dirStack.length) { 127 | return common.error('directory stack empty'); 128 | } 129 | 130 | index = _parseStackIndex(index || '+0'); 131 | 132 | if (options['no-cd'] || index > 0 || _dirStack.length + index === 0) { 133 | index = index > 0 ? index - 1 : index; 134 | _dirStack.splice(index, 1); 135 | } else { 136 | var dir = path.resolve(_dirStack.shift()); 137 | _cd('', dir); 138 | } 139 | 140 | return _dirs(''); 141 | } 142 | exports.popd = _popd; 143 | 144 | //@ 145 | //@ ### dirs([options | '+N' | '-N']) 146 | //@ 147 | //@ Available options: 148 | //@ 149 | //@ + `-c`: Clears the directory stack by deleting all of the elements. 150 | //@ 151 | //@ Arguments: 152 | //@ 153 | //@ + `+N`: Displays the Nth directory (counting from the left of the list printed by dirs when invoked without options), starting with zero. 154 | //@ + `-N`: Displays the Nth directory (counting from the right of the list printed by dirs when invoked without options), starting with zero. 155 | //@ 156 | //@ Display the list of currently remembered directories. Returns an array of paths in the stack, or a single path if +N or -N was specified. 157 | //@ 158 | //@ See also: pushd, popd 159 | function _dirs(options, index) { 160 | if (_isStackIndex(options)) { 161 | index = options; 162 | options = ''; 163 | } 164 | 165 | options = common.parseOptions(options, { 166 | 'c' : 'clear' 167 | }); 168 | 169 | if (options['clear']) { 170 | _dirStack = []; 171 | return _dirStack; 172 | } 173 | 174 | var stack = _actualDirStack(); 175 | 176 | if (index) { 177 | index = _parseStackIndex(index); 178 | 179 | if (index < 0) { 180 | index = stack.length + index; 181 | } 182 | 183 | common.log(stack[index]); 184 | return stack[index]; 185 | } 186 | 187 | common.log(stack.join(' ')); 188 | 189 | return stack; 190 | } 191 | exports.dirs = _dirs; 192 | -------------------------------------------------------------------------------- /src/echo.js: -------------------------------------------------------------------------------- 1 | var common = require('./common'); 2 | 3 | //@ 4 | //@ ### echo(string [,string ...]) 5 | //@ 6 | //@ Examples: 7 | //@ 8 | //@ ```javascript 9 | //@ echo('hello world'); 10 | //@ var str = echo('hello world'); 11 | //@ ``` 12 | //@ 13 | //@ Prints string to stdout, and returns string with additional utility methods 14 | //@ like `.to()`. 15 | function _echo() { 16 | var messages = [].slice.call(arguments, 0); 17 | console.log.apply(this, messages); 18 | return common.ShellString(messages.join(' ')); 19 | } 20 | module.exports = _echo; 21 | -------------------------------------------------------------------------------- /src/error.js: -------------------------------------------------------------------------------- 1 | var common = require('./common'); 2 | 3 | //@ 4 | //@ ### error() 5 | //@ Tests if error occurred in the last command. Returns `null` if no error occurred, 6 | //@ otherwise returns string explaining the error 7 | function error() { 8 | return common.state.error; 9 | }; 10 | module.exports = error; 11 | -------------------------------------------------------------------------------- /src/exec.js: -------------------------------------------------------------------------------- 1 | var common = require('./common'); 2 | var _tempDir = require('./tempdir'); 3 | var _pwd = require('./pwd'); 4 | var path = require('path'); 5 | var fs = require('fs'); 6 | var child = require('child_process'); 7 | 8 | // Hack to run child_process.exec() synchronously (sync avoids callback hell) 9 | // Uses a custom wait loop that checks for a flag file, created when the child process is done. 10 | // (Can't do a wait loop that checks for internal Node variables/messages as 11 | // Node is single-threaded; callbacks and other internal state changes are done in the 12 | // event loop). 13 | function execSync(cmd, opts) { 14 | var tempDir = _tempDir(); 15 | var stdoutFile = path.resolve(tempDir+'/'+common.randomFileName()), 16 | codeFile = path.resolve(tempDir+'/'+common.randomFileName()), 17 | scriptFile = path.resolve(tempDir+'/'+common.randomFileName()), 18 | sleepFile = path.resolve(tempDir+'/'+common.randomFileName()); 19 | 20 | var options = common.extend({ 21 | silent: common.config.silent 22 | }, opts); 23 | 24 | var previousStdoutContent = ''; 25 | // Echoes stdout changes from running process, if not silent 26 | function updateStdout() { 27 | if (options.silent || !fs.existsSync(stdoutFile)) 28 | return; 29 | 30 | var stdoutContent = fs.readFileSync(stdoutFile, 'utf8'); 31 | // No changes since last time? 32 | if (stdoutContent.length <= previousStdoutContent.length) 33 | return; 34 | 35 | process.stdout.write(stdoutContent.substr(previousStdoutContent.length)); 36 | previousStdoutContent = stdoutContent; 37 | } 38 | 39 | function escape(str) { 40 | return (str+'').replace(/([\\"'])/g, "\\$1").replace(/\0/g, "\\0"); 41 | } 42 | 43 | if (fs.existsSync(scriptFile)) common.unlinkSync(scriptFile); 44 | if (fs.existsSync(stdoutFile)) common.unlinkSync(stdoutFile); 45 | if (fs.existsSync(codeFile)) common.unlinkSync(codeFile); 46 | 47 | var execCommand = '"'+process.execPath+'" '+scriptFile; 48 | var execOptions = { 49 | env: process.env, 50 | cwd: _pwd(), 51 | maxBuffer: 20*1024*1024 52 | }; 53 | 54 | if (typeof child.execSync === 'function') { 55 | var script = [ 56 | "var child = require('child_process')", 57 | " , fs = require('fs');", 58 | "var childProcess = child.exec('"+escape(cmd)+"', {env: process.env, maxBuffer: 20*1024*1024}, function(err) {", 59 | " fs.writeFileSync('"+escape(codeFile)+"', err ? err.code.toString() : '0');", 60 | "});", 61 | "var stdoutStream = fs.createWriteStream('"+escape(stdoutFile)+"');", 62 | "childProcess.stdout.pipe(stdoutStream, {end: false});", 63 | "childProcess.stderr.pipe(stdoutStream, {end: false});", 64 | "childProcess.stdout.pipe(process.stdout);", 65 | "childProcess.stderr.pipe(process.stderr);", 66 | "var stdoutEnded = false, stderrEnded = false;", 67 | "function tryClosing(){ if(stdoutEnded && stderrEnded){ stdoutStream.end(); } }", 68 | "childProcess.stdout.on('end', function(){ stdoutEnded = true; tryClosing(); });", 69 | "childProcess.stderr.on('end', function(){ stderrEnded = true; tryClosing(); });" 70 | ].join('\n'); 71 | 72 | fs.writeFileSync(scriptFile, script); 73 | 74 | if (options.silent) { 75 | execOptions.stdio = 'ignore'; 76 | } else { 77 | execOptions.stdio = [0, 1, 2]; 78 | } 79 | 80 | // Welcome to the future 81 | child.execSync(execCommand, execOptions); 82 | } else { 83 | cmd += ' > '+stdoutFile+' 2>&1'; // works on both win/unix 84 | 85 | var script = [ 86 | "var child = require('child_process')", 87 | " , fs = require('fs');", 88 | "var childProcess = child.exec('"+escape(cmd)+"', {env: process.env, maxBuffer: 20*1024*1024}, function(err) {", 89 | " fs.writeFileSync('"+escape(codeFile)+"', err ? err.code.toString() : '0');", 90 | "});" 91 | ].join('\n'); 92 | 93 | fs.writeFileSync(scriptFile, script); 94 | 95 | child.exec(execCommand, execOptions); 96 | 97 | // The wait loop 98 | // sleepFile is used as a dummy I/O op to mitigate unnecessary CPU usage 99 | // (tried many I/O sync ops, writeFileSync() seems to be only one that is effective in reducing 100 | // CPU usage, though apparently not so much on Windows) 101 | while (!fs.existsSync(codeFile)) { updateStdout(); fs.writeFileSync(sleepFile, 'a'); } 102 | while (!fs.existsSync(stdoutFile)) { updateStdout(); fs.writeFileSync(sleepFile, 'a'); } 103 | } 104 | 105 | // At this point codeFile exists, but it's not necessarily flushed yet. 106 | // Keep reading it until it is. 107 | var code = parseInt('', 10); 108 | while (isNaN(code)) { 109 | code = parseInt(fs.readFileSync(codeFile, 'utf8'), 10); 110 | } 111 | 112 | var stdout = fs.readFileSync(stdoutFile, 'utf8'); 113 | 114 | // No biggie if we can't erase the files now -- they're in a temp dir anyway 115 | try { common.unlinkSync(scriptFile); } catch(e) {} 116 | try { common.unlinkSync(stdoutFile); } catch(e) {} 117 | try { common.unlinkSync(codeFile); } catch(e) {} 118 | try { common.unlinkSync(sleepFile); } catch(e) {} 119 | 120 | // some shell return codes are defined as errors, per http://tldp.org/LDP/abs/html/exitcodes.html 121 | if (code === 1 || code === 2 || code >= 126) { 122 | common.error('', true); // unix/shell doesn't really give an error message after non-zero exit codes 123 | } 124 | // True if successful, false if not 125 | var obj = { 126 | code: code, 127 | output: stdout 128 | }; 129 | return obj; 130 | } // execSync() 131 | 132 | // Wrapper around exec() to enable echoing output to console in real time 133 | function execAsync(cmd, opts, callback) { 134 | var output = ''; 135 | 136 | var options = common.extend({ 137 | silent: common.config.silent 138 | }, opts); 139 | 140 | var c = child.exec(cmd, {env: process.env, maxBuffer: 20*1024*1024}, function(err) { 141 | if (callback) 142 | callback(err ? err.code : 0, output); 143 | }); 144 | 145 | c.stdout.on('data', function(data) { 146 | output += data; 147 | if (!options.silent) 148 | process.stdout.write(data); 149 | }); 150 | 151 | c.stderr.on('data', function(data) { 152 | output += data; 153 | if (!options.silent) 154 | process.stdout.write(data); 155 | }); 156 | 157 | return c; 158 | } 159 | 160 | //@ 161 | //@ ### exec(command [, options] [, callback]) 162 | //@ Available options (all `false` by default): 163 | //@ 164 | //@ + `async`: Asynchronous execution. Defaults to true if a callback is provided. 165 | //@ + `silent`: Do not echo program output to console. 166 | //@ 167 | //@ Examples: 168 | //@ 169 | //@ ```javascript 170 | //@ var version = exec('node --version', {silent:true}).output; 171 | //@ 172 | //@ var child = exec('some_long_running_process', {async:true}); 173 | //@ child.stdout.on('data', function(data) { 174 | //@ /* ... do something with data ... */ 175 | //@ }); 176 | //@ 177 | //@ exec('some_long_running_process', function(code, output) { 178 | //@ console.log('Exit code:', code); 179 | //@ console.log('Program output:', output); 180 | //@ }); 181 | //@ ``` 182 | //@ 183 | //@ Executes the given `command` _synchronously_, unless otherwise specified. 184 | //@ When in synchronous mode returns the object `{ code:..., output:... }`, containing the program's 185 | //@ `output` (stdout + stderr) and its exit `code`. Otherwise returns the child process object, and 186 | //@ the `callback` gets the arguments `(code, output)`. 187 | //@ 188 | //@ **Note:** For long-lived processes, it's best to run `exec()` asynchronously as 189 | //@ the current synchronous implementation uses a lot of CPU. This should be getting 190 | //@ fixed soon. 191 | function _exec(command, options, callback) { 192 | if (!command) 193 | common.error('must specify command'); 194 | 195 | // Callback is defined instead of options. 196 | if (typeof options === 'function') { 197 | callback = options; 198 | options = { async: true }; 199 | } 200 | 201 | // Callback is defined with options. 202 | if (typeof options === 'object' && typeof callback === 'function') { 203 | options.async = true; 204 | } 205 | 206 | options = common.extend({ 207 | silent: common.config.silent, 208 | async: false 209 | }, options); 210 | 211 | if (options.async) 212 | return execAsync(command, options, callback); 213 | else 214 | return execSync(command, options); 215 | } 216 | module.exports = _exec; 217 | -------------------------------------------------------------------------------- /src/find.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs'); 2 | var common = require('./common'); 3 | var _ls = require('./ls'); 4 | 5 | //@ 6 | //@ ### find(path [,path ...]) 7 | //@ ### find(path_array) 8 | //@ Examples: 9 | //@ 10 | //@ ```javascript 11 | //@ find('src', 'lib'); 12 | //@ find(['src', 'lib']); // same as above 13 | //@ find('.').filter(function(file) { return file.match(/\.js$/); }); 14 | //@ ``` 15 | //@ 16 | //@ Returns array of all files (however deep) in the given paths. 17 | //@ 18 | //@ The main difference from `ls('-R', path)` is that the resulting file names 19 | //@ include the base directories, e.g. `lib/resources/file1` instead of just `file1`. 20 | function _find(options, paths) { 21 | if (!paths) 22 | common.error('no path specified'); 23 | else if (typeof paths === 'object') 24 | paths = paths; // assume array 25 | else if (typeof paths === 'string') 26 | paths = [].slice.call(arguments, 1); 27 | 28 | var list = []; 29 | 30 | function pushFile(file) { 31 | if (common.platform === 'win') 32 | file = file.replace(/\\/g, '/'); 33 | list.push(file); 34 | } 35 | 36 | // why not simply do ls('-R', paths)? because the output wouldn't give the base dirs 37 | // to get the base dir in the output, we need instead ls('-R', 'dir/*') for every directory 38 | 39 | paths.forEach(function(file) { 40 | pushFile(file); 41 | 42 | if (fs.statSync(file).isDirectory()) { 43 | _ls('-RA', file+'/*').forEach(function(subfile) { 44 | pushFile(subfile); 45 | }); 46 | } 47 | }); 48 | 49 | return list; 50 | } 51 | module.exports = _find; 52 | -------------------------------------------------------------------------------- /src/grep.js: -------------------------------------------------------------------------------- 1 | var common = require('./common'); 2 | var fs = require('fs'); 3 | 4 | //@ 5 | //@ ### grep([options ,] regex_filter, file [, file ...]) 6 | //@ ### grep([options ,] regex_filter, file_array) 7 | //@ Available options: 8 | //@ 9 | //@ + `-v`: Inverse the sense of the regex and print the lines not matching the criteria. 10 | //@ 11 | //@ Examples: 12 | //@ 13 | //@ ```javascript 14 | //@ grep('-v', 'GLOBAL_VARIABLE', '*.js'); 15 | //@ grep('GLOBAL_VARIABLE', '*.js'); 16 | //@ ``` 17 | //@ 18 | //@ Reads input string from given files and returns a string containing all lines of the 19 | //@ file that match the given `regex_filter`. Wildcard `*` accepted. 20 | function _grep(options, regex, files) { 21 | options = common.parseOptions(options, { 22 | 'v': 'inverse' 23 | }); 24 | 25 | if (!files) 26 | common.error('no paths given'); 27 | 28 | if (typeof files === 'string') 29 | files = [].slice.call(arguments, 2); 30 | // if it's array leave it as it is 31 | 32 | files = common.expand(files); 33 | 34 | var grep = ''; 35 | files.forEach(function(file) { 36 | if (!fs.existsSync(file)) { 37 | common.error('no such file or directory: ' + file, true); 38 | return; 39 | } 40 | 41 | var contents = fs.readFileSync(file, 'utf8'), 42 | lines = contents.split(/\r*\n/); 43 | lines.forEach(function(line) { 44 | var matched = line.match(regex); 45 | if ((options.inverse && !matched) || (!options.inverse && matched)) 46 | grep += line + '\n'; 47 | }); 48 | }); 49 | 50 | return common.ShellString(grep); 51 | } 52 | module.exports = _grep; 53 | -------------------------------------------------------------------------------- /src/ln.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs'); 2 | var path = require('path'); 3 | var common = require('./common'); 4 | var os = require('os'); 5 | 6 | //@ 7 | //@ ### ln(options, source, dest) 8 | //@ ### ln(source, dest) 9 | //@ Available options: 10 | //@ 11 | //@ + `s`: symlink 12 | //@ + `f`: force 13 | //@ 14 | //@ Examples: 15 | //@ 16 | //@ ```javascript 17 | //@ ln('file', 'newlink'); 18 | //@ ln('-sf', 'file', 'existing'); 19 | //@ ``` 20 | //@ 21 | //@ Links source to dest. Use -f to force the link, should dest already exist. 22 | function _ln(options, source, dest) { 23 | options = common.parseOptions(options, { 24 | 's': 'symlink', 25 | 'f': 'force' 26 | }); 27 | 28 | if (!source || !dest) { 29 | common.error('Missing and/or '); 30 | } 31 | 32 | source = path.resolve(process.cwd(), String(source)); 33 | dest = path.resolve(process.cwd(), String(dest)); 34 | 35 | if (!fs.existsSync(source)) { 36 | common.error('Source file does not exist', true); 37 | } 38 | 39 | if (fs.existsSync(dest)) { 40 | if (!options.force) { 41 | common.error('Destination file exists', true); 42 | } 43 | 44 | fs.unlinkSync(dest); 45 | } 46 | 47 | if (options.symlink) { 48 | fs.symlinkSync(source, dest, os.platform() === "win32" ? "junction" : null); 49 | } else { 50 | fs.linkSync(source, dest, os.platform() === "win32" ? "junction" : null); 51 | } 52 | } 53 | module.exports = _ln; 54 | -------------------------------------------------------------------------------- /src/ls.js: -------------------------------------------------------------------------------- 1 | var path = require('path'); 2 | var fs = require('fs'); 3 | var common = require('./common'); 4 | var _cd = require('./cd'); 5 | var _pwd = require('./pwd'); 6 | 7 | //@ 8 | //@ ### ls([options ,] path [,path ...]) 9 | //@ ### ls([options ,] path_array) 10 | //@ Available options: 11 | //@ 12 | //@ + `-R`: recursive 13 | //@ + `-A`: all files (include files beginning with `.`, except for `.` and `..`) 14 | //@ 15 | //@ Examples: 16 | //@ 17 | //@ ```javascript 18 | //@ ls('projs/*.js'); 19 | //@ ls('-R', '/users/me', '/tmp'); 20 | //@ ls('-R', ['/users/me', '/tmp']); // same as above 21 | //@ ``` 22 | //@ 23 | //@ Returns array of files in the given path, or in current directory if no path provided. 24 | function _ls(options, paths) { 25 | options = common.parseOptions(options, { 26 | 'R': 'recursive', 27 | 'A': 'all', 28 | 'a': 'all_deprecated' 29 | }); 30 | 31 | if (options.all_deprecated) { 32 | // We won't support the -a option as it's hard to image why it's useful 33 | // (it includes '.' and '..' in addition to '.*' files) 34 | // For backwards compatibility we'll dump a deprecated message and proceed as before 35 | common.log('ls: Option -a is deprecated. Use -A instead'); 36 | options.all = true; 37 | } 38 | 39 | if (!paths) 40 | paths = ['.']; 41 | else if (typeof paths === 'object') 42 | paths = paths; // assume array 43 | else if (typeof paths === 'string') 44 | paths = [].slice.call(arguments, 1); 45 | 46 | var list = []; 47 | 48 | // Conditionally pushes file to list - returns true if pushed, false otherwise 49 | // (e.g. prevents hidden files to be included unless explicitly told so) 50 | function pushFile(file, query) { 51 | // hidden file? 52 | if (path.basename(file)[0] === '.') { 53 | // not explicitly asking for hidden files? 54 | if (!options.all && !(path.basename(query)[0] === '.' && path.basename(query).length > 1)) 55 | return false; 56 | } 57 | 58 | if (common.platform === 'win') 59 | file = file.replace(/\\/g, '/'); 60 | 61 | list.push(file); 62 | return true; 63 | } 64 | 65 | paths.forEach(function(p) { 66 | if (fs.existsSync(p)) { 67 | var stats = fs.statSync(p); 68 | // Simple file? 69 | if (stats.isFile()) { 70 | pushFile(p, p); 71 | return; // continue 72 | } 73 | 74 | // Simple dir? 75 | if (stats.isDirectory()) { 76 | // Iterate over p contents 77 | fs.readdirSync(p).forEach(function(file) { 78 | if (!pushFile(file, p)) 79 | return; 80 | 81 | // Recursive? 82 | if (options.recursive) { 83 | var oldDir = _pwd(); 84 | _cd('', p); 85 | if (fs.statSync(file).isDirectory()) 86 | list = list.concat(_ls('-R'+(options.all?'A':''), file+'/*')); 87 | _cd('', oldDir); 88 | } 89 | }); 90 | return; // continue 91 | } 92 | } 93 | 94 | // p does not exist - possible wildcard present 95 | 96 | var basename = path.basename(p); 97 | var dirname = path.dirname(p); 98 | // Wildcard present on an existing dir? (e.g. '/tmp/*.js') 99 | if (basename.search(/\*/) > -1 && fs.existsSync(dirname) && fs.statSync(dirname).isDirectory) { 100 | // Escape special regular expression chars 101 | var regexp = basename.replace(/(\^|\$|\(|\)|<|>|\[|\]|\{|\}|\.|\+|\?)/g, '\\$1'); 102 | // Translates wildcard into regex 103 | regexp = '^' + regexp.replace(/\*/g, '.*') + '$'; 104 | // Iterate over directory contents 105 | fs.readdirSync(dirname).forEach(function(file) { 106 | if (file.match(new RegExp(regexp))) { 107 | if (!pushFile(path.normalize(dirname+'/'+file), basename)) 108 | return; 109 | 110 | // Recursive? 111 | if (options.recursive) { 112 | var pp = dirname + '/' + file; 113 | if (fs.lstatSync(pp).isDirectory()) 114 | list = list.concat(_ls('-R'+(options.all?'A':''), pp+'/*')); 115 | } // recursive 116 | } // if file matches 117 | }); // forEach 118 | return; 119 | } 120 | 121 | common.error('no such file or directory: ' + p, true); 122 | }); 123 | 124 | return list; 125 | } 126 | module.exports = _ls; 127 | -------------------------------------------------------------------------------- /src/mkdir.js: -------------------------------------------------------------------------------- 1 | var common = require('./common'); 2 | var fs = require('fs'); 3 | var path = require('path'); 4 | 5 | // Recursively creates 'dir' 6 | function mkdirSyncRecursive(dir) { 7 | var baseDir = path.dirname(dir); 8 | 9 | // Base dir exists, no recursion necessary 10 | if (fs.existsSync(baseDir)) { 11 | fs.mkdirSync(dir, parseInt('0777', 8)); 12 | return; 13 | } 14 | 15 | // Base dir does not exist, go recursive 16 | mkdirSyncRecursive(baseDir); 17 | 18 | // Base dir created, can create dir 19 | fs.mkdirSync(dir, parseInt('0777', 8)); 20 | } 21 | 22 | //@ 23 | //@ ### mkdir([options ,] dir [, dir ...]) 24 | //@ ### mkdir([options ,] dir_array) 25 | //@ Available options: 26 | //@ 27 | //@ + `p`: full path (will create intermediate dirs if necessary) 28 | //@ 29 | //@ Examples: 30 | //@ 31 | //@ ```javascript 32 | //@ mkdir('-p', '/tmp/a/b/c/d', '/tmp/e/f/g'); 33 | //@ mkdir('-p', ['/tmp/a/b/c/d', '/tmp/e/f/g']); // same as above 34 | //@ ``` 35 | //@ 36 | //@ Creates directories. 37 | function _mkdir(options, dirs) { 38 | options = common.parseOptions(options, { 39 | 'p': 'fullpath' 40 | }); 41 | if (!dirs) 42 | common.error('no paths given'); 43 | 44 | if (typeof dirs === 'string') 45 | dirs = [].slice.call(arguments, 1); 46 | // if it's array leave it as it is 47 | 48 | dirs.forEach(function(dir) { 49 | if (fs.existsSync(dir)) { 50 | if (!options.fullpath) 51 | common.error('path already exists: ' + dir, true); 52 | return; // skip dir 53 | } 54 | 55 | // Base dir does not exist, and no -p option given 56 | var baseDir = path.dirname(dir); 57 | if (!fs.existsSync(baseDir) && !options.fullpath) { 58 | common.error('no such file or directory: ' + baseDir, true); 59 | return; // skip dir 60 | } 61 | 62 | if (options.fullpath) 63 | mkdirSyncRecursive(dir); 64 | else 65 | fs.mkdirSync(dir, parseInt('0777', 8)); 66 | }); 67 | } // mkdir 68 | module.exports = _mkdir; 69 | -------------------------------------------------------------------------------- /src/mv.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs'); 2 | var path = require('path'); 3 | var common = require('./common'); 4 | 5 | //@ 6 | //@ ### mv(source [, source ...], dest') 7 | //@ ### mv(source_array, dest') 8 | //@ Available options: 9 | //@ 10 | //@ + `f`: force 11 | //@ 12 | //@ Examples: 13 | //@ 14 | //@ ```javascript 15 | //@ mv('-f', 'file', 'dir/'); 16 | //@ mv('file1', 'file2', 'dir/'); 17 | //@ mv(['file1', 'file2'], 'dir/'); // same as above 18 | //@ ``` 19 | //@ 20 | //@ Moves files. The wildcard `*` is accepted. 21 | function _mv(options, sources, dest) { 22 | options = common.parseOptions(options, { 23 | 'f': 'force' 24 | }); 25 | 26 | // Get sources, dest 27 | if (arguments.length < 3) { 28 | common.error('missing and/or '); 29 | } else if (arguments.length > 3) { 30 | sources = [].slice.call(arguments, 1, arguments.length - 1); 31 | dest = arguments[arguments.length - 1]; 32 | } else if (typeof sources === 'string') { 33 | sources = [sources]; 34 | } else if ('length' in sources) { 35 | sources = sources; // no-op for array 36 | } else { 37 | common.error('invalid arguments'); 38 | } 39 | 40 | sources = common.expand(sources); 41 | 42 | var exists = fs.existsSync(dest), 43 | stats = exists && fs.statSync(dest); 44 | 45 | // Dest is not existing dir, but multiple sources given 46 | if ((!exists || !stats.isDirectory()) && sources.length > 1) 47 | common.error('dest is not a directory (too many sources)'); 48 | 49 | // Dest is an existing file, but no -f given 50 | if (exists && stats.isFile() && !options.force) 51 | common.error('dest file already exists: ' + dest); 52 | 53 | sources.forEach(function(src) { 54 | if (!fs.existsSync(src)) { 55 | common.error('no such file or directory: '+src, true); 56 | return; // skip file 57 | } 58 | 59 | // If here, src exists 60 | 61 | // When copying to '/path/dir': 62 | // thisDest = '/path/dir/file1' 63 | var thisDest = dest; 64 | if (fs.existsSync(dest) && fs.statSync(dest).isDirectory()) 65 | thisDest = path.normalize(dest + '/' + path.basename(src)); 66 | 67 | if (fs.existsSync(thisDest) && !options.force) { 68 | common.error('dest file already exists: ' + thisDest, true); 69 | return; // skip file 70 | } 71 | 72 | if (path.resolve(src) === path.dirname(path.resolve(thisDest))) { 73 | common.error('cannot move to self: '+src, true); 74 | return; // skip file 75 | } 76 | 77 | fs.renameSync(src, thisDest); 78 | }); // forEach(src) 79 | } // mv 80 | module.exports = _mv; 81 | -------------------------------------------------------------------------------- /src/popd.js: -------------------------------------------------------------------------------- 1 | // see dirs.js -------------------------------------------------------------------------------- /src/pushd.js: -------------------------------------------------------------------------------- 1 | // see dirs.js -------------------------------------------------------------------------------- /src/pwd.js: -------------------------------------------------------------------------------- 1 | var path = require('path'); 2 | var common = require('./common'); 3 | 4 | //@ 5 | //@ ### pwd() 6 | //@ Returns the current directory. 7 | function _pwd(options) { 8 | var pwd = path.resolve(process.cwd()); 9 | return common.ShellString(pwd); 10 | } 11 | module.exports = _pwd; 12 | -------------------------------------------------------------------------------- /src/rm.js: -------------------------------------------------------------------------------- 1 | var common = require('./common'); 2 | var fs = require('fs'); 3 | 4 | // Recursively removes 'dir' 5 | // Adapted from https://github.com/ryanmcgrath/wrench-js 6 | // 7 | // Copyright (c) 2010 Ryan McGrath 8 | // Copyright (c) 2012 Artur Adib 9 | // 10 | // Licensed under the MIT License 11 | // http://www.opensource.org/licenses/mit-license.php 12 | function rmdirSyncRecursive(dir, force) { 13 | var files; 14 | 15 | files = fs.readdirSync(dir); 16 | 17 | // Loop through and delete everything in the sub-tree after checking it 18 | for(var i = 0; i < files.length; i++) { 19 | var file = dir + "/" + files[i], 20 | currFile = fs.lstatSync(file); 21 | 22 | if(currFile.isDirectory()) { // Recursive function back to the beginning 23 | rmdirSyncRecursive(file, force); 24 | } 25 | 26 | else if(currFile.isSymbolicLink()) { // Unlink symlinks 27 | if (force || isWriteable(file)) { 28 | try { 29 | common.unlinkSync(file); 30 | } catch (e) { 31 | common.error('could not remove file (code '+e.code+'): ' + file, true); 32 | } 33 | } 34 | } 35 | 36 | else // Assume it's a file - perhaps a try/catch belongs here? 37 | if (force || isWriteable(file)) { 38 | try { 39 | common.unlinkSync(file); 40 | } catch (e) { 41 | common.error('could not remove file (code '+e.code+'): ' + file, true); 42 | } 43 | } 44 | } 45 | 46 | // Now that we know everything in the sub-tree has been deleted, we can delete the main directory. 47 | // Huzzah for the shopkeep. 48 | 49 | var result; 50 | try { 51 | // Retry on windows, sometimes it takes a little time before all the files in the directory are gone 52 | var start = Date.now(); 53 | while (true) { 54 | try { 55 | result = fs.rmdirSync(dir); 56 | if (fs.existsSync(dir)) throw { code: "EAGAIN" } 57 | break; 58 | } catch(er) { 59 | // In addition to error codes, also check if the directory still exists and loop again if true 60 | if (process.platform === "win32" && (er.code === "ENOTEMPTY" || er.code === "EBUSY" || er.code === "EPERM" || er.code === "EAGAIN")) { 61 | if (Date.now() - start > 1000) throw er; 62 | } else if (er.code === "ENOENT") { 63 | // Directory did not exist, deletion was successful 64 | break; 65 | } else { 66 | throw er; 67 | } 68 | } 69 | } 70 | } catch(e) { 71 | common.error('could not remove directory (code '+e.code+'): ' + dir, true); 72 | } 73 | 74 | return result; 75 | } // rmdirSyncRecursive 76 | 77 | // Hack to determine if file has write permissions for current user 78 | // Avoids having to check user, group, etc, but it's probably slow 79 | function isWriteable(file) { 80 | var writePermission = true; 81 | try { 82 | var __fd = fs.openSync(file, 'a'); 83 | fs.closeSync(__fd); 84 | } catch(e) { 85 | writePermission = false; 86 | } 87 | 88 | return writePermission; 89 | } 90 | 91 | //@ 92 | //@ ### rm([options ,] file [, file ...]) 93 | //@ ### rm([options ,] file_array) 94 | //@ Available options: 95 | //@ 96 | //@ + `-f`: force 97 | //@ + `-r, -R`: recursive 98 | //@ 99 | //@ Examples: 100 | //@ 101 | //@ ```javascript 102 | //@ rm('-rf', '/tmp/*'); 103 | //@ rm('some_file.txt', 'another_file.txt'); 104 | //@ rm(['some_file.txt', 'another_file.txt']); // same as above 105 | //@ ``` 106 | //@ 107 | //@ Removes files. The wildcard `*` is accepted. 108 | function _rm(options, files) { 109 | options = common.parseOptions(options, { 110 | 'f': 'force', 111 | 'r': 'recursive', 112 | 'R': 'recursive' 113 | }); 114 | if (!files) 115 | common.error('no paths given'); 116 | 117 | if (typeof files === 'string') 118 | files = [].slice.call(arguments, 1); 119 | // if it's array leave it as it is 120 | 121 | files = common.expand(files); 122 | 123 | files.forEach(function(file) { 124 | if (!fs.existsSync(file)) { 125 | // Path does not exist, no force flag given 126 | if (!options.force) 127 | common.error('no such file or directory: '+file, true); 128 | 129 | return; // skip file 130 | } 131 | 132 | // If here, path exists 133 | 134 | var stats = fs.lstatSync(file); 135 | if (stats.isFile() || stats.isSymbolicLink()) { 136 | 137 | // Do not check for file writing permissions 138 | if (options.force) { 139 | common.unlinkSync(file); 140 | return; 141 | } 142 | 143 | if (isWriteable(file)) 144 | common.unlinkSync(file); 145 | else 146 | common.error('permission denied: '+file, true); 147 | 148 | return; 149 | } // simple file 150 | 151 | // Path is an existing directory, but no -r flag given 152 | if (stats.isDirectory() && !options.recursive) { 153 | common.error('path is a directory', true); 154 | return; // skip path 155 | } 156 | 157 | // Recursively remove existing directory 158 | if (stats.isDirectory() && options.recursive) { 159 | rmdirSyncRecursive(file, options.force); 160 | } 161 | }); // forEach(file) 162 | } // rm 163 | module.exports = _rm; 164 | -------------------------------------------------------------------------------- /src/sed.js: -------------------------------------------------------------------------------- 1 | var common = require('./common'); 2 | var fs = require('fs'); 3 | 4 | //@ 5 | //@ ### sed([options ,] search_regex, replacement, file) 6 | //@ Available options: 7 | //@ 8 | //@ + `-i`: Replace contents of 'file' in-place. _Note that no backups will be created!_ 9 | //@ 10 | //@ Examples: 11 | //@ 12 | //@ ```javascript 13 | //@ sed('-i', 'PROGRAM_VERSION', 'v0.1.3', 'source.js'); 14 | //@ sed(/.*DELETE_THIS_LINE.*\n/, '', 'source.js'); 15 | //@ ``` 16 | //@ 17 | //@ Reads an input string from `file` and performs a JavaScript `replace()` on the input 18 | //@ using the given search regex and replacement string or function. Returns the new string after replacement. 19 | function _sed(options, regex, replacement, file) { 20 | options = common.parseOptions(options, { 21 | 'i': 'inplace' 22 | }); 23 | 24 | if (typeof replacement === 'string' || typeof replacement === 'function') 25 | replacement = replacement; // no-op 26 | else if (typeof replacement === 'number') 27 | replacement = replacement.toString(); // fallback 28 | else 29 | common.error('invalid replacement string'); 30 | 31 | if (!file) 32 | common.error('no file given'); 33 | 34 | if (!fs.existsSync(file)) 35 | common.error('no such file or directory: ' + file); 36 | 37 | var result = fs.readFileSync(file, 'utf8').replace(regex, replacement); 38 | if (options.inplace) 39 | fs.writeFileSync(file, result, 'utf8'); 40 | 41 | return common.ShellString(result); 42 | } 43 | module.exports = _sed; 44 | -------------------------------------------------------------------------------- /src/tempdir.js: -------------------------------------------------------------------------------- 1 | var common = require('./common'); 2 | var os = require('os'); 3 | var fs = require('fs'); 4 | 5 | // Returns false if 'dir' is not a writeable directory, 'dir' otherwise 6 | function writeableDir(dir) { 7 | if (!dir || !fs.existsSync(dir)) 8 | return false; 9 | 10 | if (!fs.statSync(dir).isDirectory()) 11 | return false; 12 | 13 | var testFile = dir+'/'+common.randomFileName(); 14 | try { 15 | fs.writeFileSync(testFile, ' '); 16 | common.unlinkSync(testFile); 17 | return dir; 18 | } catch (e) { 19 | return false; 20 | } 21 | } 22 | 23 | 24 | //@ 25 | //@ ### tempdir() 26 | //@ 27 | //@ Examples: 28 | //@ 29 | //@ ```javascript 30 | //@ var tmp = tempdir(); // "/tmp" for most *nix platforms 31 | //@ ``` 32 | //@ 33 | //@ Searches and returns string containing a writeable, platform-dependent temporary directory. 34 | //@ Follows Python's [tempfile algorithm](http://docs.python.org/library/tempfile.html#tempfile.tempdir). 35 | function _tempDir() { 36 | var state = common.state; 37 | if (state.tempDir) 38 | return state.tempDir; // from cache 39 | 40 | state.tempDir = writeableDir(os.tempDir && os.tempDir()) || // node 0.8+ 41 | writeableDir(process.env['TMPDIR']) || 42 | writeableDir(process.env['TEMP']) || 43 | writeableDir(process.env['TMP']) || 44 | writeableDir(process.env['Wimp$ScrapDir']) || // RiscOS 45 | writeableDir('C:\\TEMP') || // Windows 46 | writeableDir('C:\\TMP') || // Windows 47 | writeableDir('\\TEMP') || // Windows 48 | writeableDir('\\TMP') || // Windows 49 | writeableDir('/tmp') || 50 | writeableDir('/var/tmp') || 51 | writeableDir('/usr/tmp') || 52 | writeableDir('.'); // last resort 53 | 54 | return state.tempDir; 55 | } 56 | module.exports = _tempDir; 57 | -------------------------------------------------------------------------------- /src/test.js: -------------------------------------------------------------------------------- 1 | var common = require('./common'); 2 | var fs = require('fs'); 3 | 4 | //@ 5 | //@ ### test(expression) 6 | //@ Available expression primaries: 7 | //@ 8 | //@ + `'-b', 'path'`: true if path is a block device 9 | //@ + `'-c', 'path'`: true if path is a character device 10 | //@ + `'-d', 'path'`: true if path is a directory 11 | //@ + `'-e', 'path'`: true if path exists 12 | //@ + `'-f', 'path'`: true if path is a regular file 13 | //@ + `'-L', 'path'`: true if path is a symboilc link 14 | //@ + `'-p', 'path'`: true if path is a pipe (FIFO) 15 | //@ + `'-S', 'path'`: true if path is a socket 16 | //@ 17 | //@ Examples: 18 | //@ 19 | //@ ```javascript 20 | //@ if (test('-d', path)) { /* do something with dir */ }; 21 | //@ if (!test('-f', path)) continue; // skip if it's a regular file 22 | //@ ``` 23 | //@ 24 | //@ Evaluates expression using the available primaries and returns corresponding value. 25 | function _test(options, path) { 26 | if (!path) 27 | common.error('no path given'); 28 | 29 | // hack - only works with unary primaries 30 | options = common.parseOptions(options, { 31 | 'b': 'block', 32 | 'c': 'character', 33 | 'd': 'directory', 34 | 'e': 'exists', 35 | 'f': 'file', 36 | 'L': 'link', 37 | 'p': 'pipe', 38 | 'S': 'socket' 39 | }); 40 | 41 | var canInterpret = false; 42 | for (var key in options) 43 | if (options[key] === true) { 44 | canInterpret = true; 45 | break; 46 | } 47 | 48 | if (!canInterpret) 49 | common.error('could not interpret expression'); 50 | 51 | if (options.link) { 52 | try { 53 | return fs.lstatSync(path).isSymbolicLink(); 54 | } catch(e) { 55 | return false; 56 | } 57 | } 58 | 59 | if (!fs.existsSync(path)) 60 | return false; 61 | 62 | if (options.exists) 63 | return true; 64 | 65 | var stats = fs.statSync(path); 66 | 67 | if (options.block) 68 | return stats.isBlockDevice(); 69 | 70 | if (options.character) 71 | return stats.isCharacterDevice(); 72 | 73 | if (options.directory) 74 | return stats.isDirectory(); 75 | 76 | if (options.file) 77 | return stats.isFile(); 78 | 79 | if (options.pipe) 80 | return stats.isFIFO(); 81 | 82 | if (options.socket) 83 | return stats.isSocket(); 84 | } // test 85 | module.exports = _test; 86 | -------------------------------------------------------------------------------- /src/to.js: -------------------------------------------------------------------------------- 1 | var common = require('./common'); 2 | var fs = require('fs'); 3 | var path = require('path'); 4 | 5 | //@ 6 | //@ ### 'string'.to(file) 7 | //@ 8 | //@ Examples: 9 | //@ 10 | //@ ```javascript 11 | //@ cat('input.txt').to('output.txt'); 12 | //@ ``` 13 | //@ 14 | //@ Analogous to the redirection operator `>` in Unix, but works with JavaScript strings (such as 15 | //@ those returned by `cat`, `grep`, etc). _Like Unix redirections, `to()` will overwrite any existing file!_ 16 | function _to(options, file) { 17 | if (!file) 18 | common.error('wrong arguments'); 19 | 20 | if (!fs.existsSync( path.dirname(file) )) 21 | common.error('no such file or directory: ' + path.dirname(file)); 22 | 23 | try { 24 | fs.writeFileSync(file, this.toString(), 'utf8'); 25 | } catch(e) { 26 | common.error('could not write to file (code '+e.code+'): '+file, true); 27 | } 28 | } 29 | module.exports = _to; 30 | -------------------------------------------------------------------------------- /src/toEnd.js: -------------------------------------------------------------------------------- 1 | var common = require('./common'); 2 | var fs = require('fs'); 3 | var path = require('path'); 4 | 5 | //@ 6 | //@ ### 'string'.toEnd(file) 7 | //@ 8 | //@ Examples: 9 | //@ 10 | //@ ```javascript 11 | //@ cat('input.txt').toEnd('output.txt'); 12 | //@ ``` 13 | //@ 14 | //@ Analogous to the redirect-and-append operator `>>` in Unix, but works with JavaScript strings (such as 15 | //@ those returned by `cat`, `grep`, etc). 16 | function _toEnd(options, file) { 17 | if (!file) 18 | common.error('wrong arguments'); 19 | 20 | if (!fs.existsSync( path.dirname(file) )) 21 | common.error('no such file or directory: ' + path.dirname(file)); 22 | 23 | try { 24 | fs.appendFileSync(file, this.toString(), 'utf8'); 25 | } catch(e) { 26 | common.error('could not append to file (code '+e.code+'): '+file, true); 27 | } 28 | } 29 | module.exports = _toEnd; 30 | -------------------------------------------------------------------------------- /src/which.js: -------------------------------------------------------------------------------- 1 | var common = require('./common'); 2 | var fs = require('fs'); 3 | var path = require('path'); 4 | 5 | // Cross-platform method for splitting environment PATH variables 6 | function splitPath(p) { 7 | if (!p) 8 | return []; 9 | 10 | if (common.platform === 'win') 11 | return p.split(';'); 12 | else 13 | return p.split(':'); 14 | } 15 | 16 | function checkPath(path) { 17 | return fs.existsSync(path) && fs.statSync(path).isDirectory() == false; 18 | } 19 | 20 | //@ 21 | //@ ### which(command) 22 | //@ 23 | //@ Examples: 24 | //@ 25 | //@ ```javascript 26 | //@ var nodeExec = which('node'); 27 | //@ ``` 28 | //@ 29 | //@ Searches for `command` in the system's PATH. On Windows looks for `.exe`, `.cmd`, and `.bat` extensions. 30 | //@ Returns string containing the absolute path to the command. 31 | function _which(options, cmd) { 32 | if (!cmd) 33 | common.error('must specify command'); 34 | 35 | var pathEnv = process.env.path || process.env.Path || process.env.PATH, 36 | pathArray = splitPath(pathEnv), 37 | where = null; 38 | 39 | // No relative/absolute paths provided? 40 | if (cmd.search(/\//) === -1) { 41 | // Search for command in PATH 42 | pathArray.forEach(function(dir) { 43 | if (where) 44 | return; // already found it 45 | 46 | var attempt = path.resolve(dir + '/' + cmd); 47 | if (checkPath(attempt)) { 48 | where = attempt; 49 | return; 50 | } 51 | 52 | if (common.platform === 'win') { 53 | var baseAttempt = attempt; 54 | attempt = baseAttempt + '.exe'; 55 | if (checkPath(attempt)) { 56 | where = attempt; 57 | return; 58 | } 59 | attempt = baseAttempt + '.cmd'; 60 | if (checkPath(attempt)) { 61 | where = attempt; 62 | return; 63 | } 64 | attempt = baseAttempt + '.bat'; 65 | if (checkPath(attempt)) { 66 | where = attempt; 67 | return; 68 | } 69 | } // if 'win' 70 | }); 71 | } 72 | 73 | // Command not found anywhere? 74 | if (!checkPath(cmd) && !where) 75 | return null; 76 | 77 | where = where || path.resolve(cmd); 78 | 79 | return common.ShellString(where); 80 | } 81 | module.exports = _which; 82 | -------------------------------------------------------------------------------- /test/.gitignore: -------------------------------------------------------------------------------- 1 | tmp/ 2 | 3 | -------------------------------------------------------------------------------- /test/cat.js: -------------------------------------------------------------------------------- 1 | var shell = require('..'); 2 | 3 | var assert = require('assert'), 4 | fs = require('fs'); 5 | 6 | shell.config.silent = true; 7 | 8 | shell.rm('-rf', 'tmp'); 9 | shell.mkdir('tmp'); 10 | 11 | // 12 | // Invalids 13 | // 14 | 15 | shell.cat(); 16 | assert.ok(shell.error()); 17 | 18 | assert.equal(fs.existsSync('/asdfasdf'), false); // sanity check 19 | shell.cat('/adsfasdf'); // file does not exist 20 | assert.ok(shell.error()); 21 | 22 | // 23 | // Valids 24 | // 25 | 26 | // simple 27 | var result = shell.cat('resources/file1'); 28 | assert.equal(shell.error(), null); 29 | assert.equal(result, 'test1'); 30 | 31 | // multiple files 32 | var result = shell.cat('resources/file2', 'resources/file1'); 33 | assert.equal(shell.error(), null); 34 | assert.equal(result, 'test2\ntest1'); 35 | 36 | // multiple files, array syntax 37 | var result = shell.cat(['resources/file2', 'resources/file1']); 38 | assert.equal(shell.error(), null); 39 | assert.equal(result, 'test2\ntest1'); 40 | 41 | var result = shell.cat('resources/file*.txt'); 42 | assert.equal(shell.error(), null); 43 | assert.ok(result.search('test1') > -1); // file order might be random 44 | assert.ok(result.search('test2') > -1); 45 | 46 | shell.exit(123); 47 | -------------------------------------------------------------------------------- /test/cd.js: -------------------------------------------------------------------------------- 1 | var shell = require('..'); 2 | 3 | var assert = require('assert'), 4 | path = require('path'), 5 | fs = require('fs'); 6 | 7 | shell.config.silent = true; 8 | 9 | // save current dir 10 | var cur = shell.pwd(); 11 | 12 | shell.rm('-rf', 'tmp'); 13 | shell.mkdir('tmp'); 14 | 15 | // 16 | // Invalids 17 | // 18 | 19 | shell.cd(); 20 | assert.ok(shell.error()); 21 | 22 | assert.equal(fs.existsSync('/asdfasdf'), false); // sanity check 23 | shell.cd('/adsfasdf'); // dir does not exist 24 | assert.ok(shell.error()); 25 | 26 | assert.equal(fs.existsSync('resources/file1'), true); // sanity check 27 | shell.cd('resources/file1'); // file, not dir 28 | assert.ok(shell.error()); 29 | 30 | // 31 | // Valids 32 | // 33 | 34 | shell.cd(cur); 35 | shell.cd('tmp'); 36 | assert.equal(shell.error(), null); 37 | assert.equal(path.basename(process.cwd()), 'tmp'); 38 | 39 | shell.cd(cur); 40 | shell.cd('/'); 41 | assert.equal(shell.error(), null); 42 | assert.equal(process.cwd(), path.resolve('/')); 43 | 44 | // cd + other commands 45 | 46 | shell.cd(cur); 47 | shell.rm('-f', 'tmp/*'); 48 | assert.equal(fs.existsSync('tmp/file1'), false); 49 | shell.cd('resources'); 50 | assert.equal(shell.error(), null); 51 | shell.cp('file1', '../tmp'); 52 | assert.equal(shell.error(), null); 53 | shell.cd('../tmp'); 54 | assert.equal(shell.error(), null); 55 | assert.equal(fs.existsSync('file1'), true); 56 | 57 | shell.exit(123); 58 | -------------------------------------------------------------------------------- /test/chmod.js: -------------------------------------------------------------------------------- 1 | var shell = require('..'); 2 | 3 | var assert = require('assert'), 4 | fs = require('fs'); 5 | 6 | shell.config.silent = true; 7 | 8 | // 9 | // Invalids 10 | // 11 | 12 | shell.chmod('blah'); // missing args 13 | assert.ok(shell.error()); 14 | shell.chmod('893', 'resources/chmod'); // invalid permissions - mode must be in octal 15 | assert.ok(shell.error()); 16 | 17 | // 18 | // Valids 19 | // 20 | 21 | // Test files - the bitmasking is to ignore the upper bits. 22 | shell.chmod('755', 'resources/chmod/file1'); 23 | assert.equal(fs.statSync('resources/chmod/file1').mode & parseInt('777', 8), parseInt('755', 8)); 24 | shell.chmod('644', 'resources/chmod/file1'); 25 | assert.equal(fs.statSync('resources/chmod/file1').mode & parseInt('777', 8), parseInt('644', 8)); 26 | 27 | shell.chmod('o+x', 'resources/chmod/file1'); 28 | assert.equal(fs.statSync('resources/chmod/file1').mode & parseInt('007', 8), parseInt('005', 8)); 29 | shell.chmod('644', 'resources/chmod/file1'); 30 | 31 | shell.chmod('+x', 'resources/chmod/file1'); 32 | assert.equal(fs.statSync('resources/chmod/file1').mode & parseInt('777', 8), parseInt('755', 8)); 33 | shell.chmod('644', 'resources/chmod/file1'); 34 | 35 | // Test setuid 36 | shell.chmod('u+s', 'resources/chmod/file1'); 37 | assert.equal(fs.statSync('resources/chmod/file1').mode & parseInt('4000', 8), parseInt('4000', 8)); 38 | shell.chmod('u-s', 'resources/chmod/file1'); 39 | assert.equal(fs.statSync('resources/chmod/file1').mode & parseInt('777', 8), parseInt('644', 8)); 40 | 41 | // according to POSIX standards at http://linux.die.net/man/1/chmod, 42 | // setuid is never cleared from a directory unless explicitly asked for. 43 | shell.chmod('u+s', 'resources/chmod/c'); 44 | shell.chmod('755', 'resources/chmod/c'); 45 | assert.equal(fs.statSync('resources/chmod/c').mode & parseInt('4000', 8), parseInt('4000', 8)); 46 | shell.chmod('u-s', 'resources/chmod/c'); 47 | 48 | // Test setgid 49 | shell.chmod('g+s', 'resources/chmod/file1'); 50 | assert.equal(fs.statSync('resources/chmod/file1').mode & parseInt('2000', 8), parseInt('2000', 8)); 51 | shell.chmod('g-s', 'resources/chmod/file1'); 52 | assert.equal(fs.statSync('resources/chmod/file1').mode & parseInt('777', 8), parseInt('644', 8)); 53 | 54 | // Test sticky bit 55 | shell.chmod('+t', 'resources/chmod/file1'); 56 | assert.equal(fs.statSync('resources/chmod/file1').mode & parseInt('1000', 8), parseInt('1000', 8)); 57 | shell.chmod('-t', 'resources/chmod/file1'); 58 | assert.equal(fs.statSync('resources/chmod/file1').mode & parseInt('777', 8), parseInt('644', 8)); 59 | assert.equal(fs.statSync('resources/chmod/file1').mode & parseInt('1000', 8), 0); 60 | 61 | // Test directories 62 | shell.chmod('a-w', 'resources/chmod/b/a/b'); 63 | assert.equal(fs.statSync('resources/chmod/b/a/b').mode & parseInt('777', 8), parseInt('555', 8)); 64 | shell.chmod('755', 'resources/chmod/b/a/b'); 65 | 66 | // Test recursion 67 | shell.chmod('-R', 'a+w', 'resources/chmod/b'); 68 | assert.equal(fs.statSync('resources/chmod/b/a/b').mode & parseInt('777', 8), parseInt('777', 8)); 69 | shell.chmod('-R', '755', 'resources/chmod/b'); 70 | assert.equal(fs.statSync('resources/chmod/b/a/b').mode & parseInt('777', 8), parseInt('755', 8)); 71 | 72 | // Test symbolic links w/ recursion - WARNING: *nix only 73 | fs.symlinkSync('resources/chmod/b/a', 'resources/chmod/a/b/c/link', 'dir'); 74 | shell.chmod('-R', 'u-w', 'resources/chmod/a/b'); 75 | assert.equal(fs.statSync('resources/chmod/a/b/c').mode & parseInt('700', 8), parseInt('500', 8)); 76 | assert.equal(fs.statSync('resources/chmod/b/a').mode & parseInt('700', 8), parseInt('700', 8)); 77 | shell.chmod('-R', 'u+w', 'resources/chmod/a/b'); 78 | fs.unlinkSync('resources/chmod/a/b/c/link'); 79 | 80 | // Test combinations 81 | shell.chmod('a-rwx', 'resources/chmod/file1'); 82 | assert.equal(fs.statSync('resources/chmod/file1').mode & parseInt('000', 8), parseInt('000', 8)); 83 | shell.chmod('644', 'resources/chmod/file1'); 84 | 85 | shell.chmod('a-rwx,u+r', 'resources/chmod/file1'); 86 | assert.equal(fs.statSync('resources/chmod/file1').mode & parseInt('400', 8), parseInt('400', 8)); 87 | shell.chmod('644', 'resources/chmod/file1'); 88 | 89 | shell.chmod('a-rwx,u+rw', 'resources/chmod/file1'); 90 | assert.equal(fs.statSync('resources/chmod/file1').mode & parseInt('600', 8), parseInt('600', 8)); 91 | shell.chmod('644', 'resources/chmod/file1'); 92 | 93 | shell.chmod('a-rwx,u+rwx', 'resources/chmod/file1'); 94 | assert.equal(fs.statSync('resources/chmod/file1').mode & parseInt('700', 8), parseInt('700', 8)); 95 | shell.chmod('644', 'resources/chmod/file1'); 96 | 97 | shell.chmod('000', 'resources/chmod/file1'); 98 | shell.chmod('u+rw', 'resources/chmod/file1'); 99 | assert.equal(fs.statSync('resources/chmod/file1').mode & parseInt('600', 8), parseInt('600', 8)); 100 | shell.chmod('644', 'resources/chmod/file1'); 101 | 102 | shell.chmod('000', 'resources/chmod/file1'); 103 | shell.chmod('u+wx', 'resources/chmod/file1'); 104 | assert.equal(fs.statSync('resources/chmod/file1').mode & parseInt('300', 8), parseInt('300', 8)); 105 | shell.chmod('644', 'resources/chmod/file1'); 106 | 107 | shell.chmod('000', 'resources/chmod/file1'); 108 | shell.chmod('u+r,g+w,o+x', 'resources/chmod/file1'); 109 | assert.equal(fs.statSync('resources/chmod/file1').mode & parseInt('421', 8), parseInt('421', 8)); 110 | shell.chmod('644', 'resources/chmod/file1'); 111 | 112 | shell.chmod('000', 'resources/chmod/file1'); 113 | shell.chmod('u+rw,g+wx', 'resources/chmod/file1'); 114 | assert.equal(fs.statSync('resources/chmod/file1').mode & parseInt('630', 8), parseInt('630', 8)); 115 | shell.chmod('644', 'resources/chmod/file1'); 116 | 117 | shell.chmod('700', 'resources/chmod/file1'); 118 | shell.chmod('u-x,g+rw', 'resources/chmod/file1'); 119 | assert.equal(fs.statSync('resources/chmod/file1').mode & parseInt('660', 8), parseInt('660', 8)); 120 | shell.chmod('644', 'resources/chmod/file1'); 121 | 122 | shell.chmod('a-rwx,u+rw', 'resources/chmod/file1'); 123 | assert.equal(fs.statSync('resources/chmod/file1').mode & parseInt('600', 8), parseInt('600', 8)); 124 | shell.chmod('a-rwx,u+rw', 'resources/chmod/file1'); 125 | assert.equal(fs.statSync('resources/chmod/file1').mode & parseInt('600', 8), parseInt('600', 8)); 126 | shell.chmod('644', 'resources/chmod/file1'); 127 | 128 | // Support capital X ("entry" permission aka directory-only execute) 129 | 130 | shell.chmod('744', 'resources/chmod/xdir'); 131 | shell.chmod('644', 'resources/chmod/xdir/file'); 132 | shell.chmod('744', 'resources/chmod/xdir/deep'); 133 | shell.chmod('644', 'resources/chmod/xdir/deep/file'); 134 | shell.chmod('-R', 'a+X', 'resources/chmod/xdir'); 135 | 136 | assert.equal(fs.statSync('resources/chmod/xdir').mode & parseInt('755', 8), parseInt('755', 8)); 137 | assert.equal(fs.statSync('resources/chmod/xdir/file').mode & parseInt('644', 8), parseInt('644', 8)); 138 | assert.equal(fs.statSync('resources/chmod/xdir/deep').mode & parseInt('755', 8), parseInt('755', 8)); 139 | assert.equal(fs.statSync('resources/chmod/xdir/deep/file').mode & parseInt('644', 8), parseInt('644', 8)); 140 | 141 | shell.exit(123); 142 | -------------------------------------------------------------------------------- /test/common.js: -------------------------------------------------------------------------------- 1 | var shell = require('..'); 2 | var common = require('../src/common'); 3 | 4 | var assert = require('assert'); 5 | 6 | shell.config.silent = true; 7 | 8 | shell.rm('-rf', 'tmp'); 9 | shell.mkdir('tmp'); 10 | 11 | // 12 | // Invalids 13 | // 14 | 15 | // too few args 16 | assert.throws(function () { 17 | common.expand(); 18 | }, TypeError); 19 | 20 | // should be a list 21 | assert.throws(function () { 22 | common.expand("resources"); 23 | }, TypeError); 24 | 25 | // 26 | // Valids 27 | // 28 | 29 | // single file, array syntax 30 | var result = common.expand(['resources/file1.txt']); 31 | assert.equal(shell.error(), null); 32 | assert.deepEqual(result, ['resources/file1.txt']); 33 | 34 | // multiple file, glob syntax, * for file name 35 | var result = common.expand(['resources/file*.txt']); 36 | assert.equal(shell.error(), null); 37 | assert.deepEqual(result.sort(), ['resources/file1.txt', 'resources/file2.txt'].sort()); 38 | 39 | // multiple file, glob syntax, * for directory name 40 | var result = common.expand(['*/file*.txt']); 41 | assert.equal(shell.error(), null); 42 | assert.deepEqual(result.sort(), ['resources/file1.txt', 'resources/file2.txt'].sort()); 43 | 44 | // multiple file, glob syntax, ** for directory name 45 | var result = common.expand(['**/file*.js']); 46 | assert.equal(shell.error(), null); 47 | assert.deepEqual(result.sort(), ["resources/file1.js","resources/file2.js","resources/ls/file1.js","resources/ls/file2.js"].sort()); 48 | 49 | shell.exit(123); 50 | 51 | 52 | -------------------------------------------------------------------------------- /test/config.js: -------------------------------------------------------------------------------- 1 | var shell = require('..'); 2 | 3 | var assert = require('assert'), 4 | child = require('child_process'); 5 | 6 | // 7 | // config.silent 8 | // 9 | 10 | assert.equal(shell.config.silent, false); // default 11 | 12 | shell.config.silent = true; 13 | assert.equal(shell.config.silent, true); 14 | 15 | shell.config.silent = false; 16 | assert.equal(shell.config.silent, false); 17 | 18 | // 19 | // config.fatal 20 | // 21 | 22 | assert.equal(shell.config.fatal, false); // default 23 | 24 | // 25 | // config.fatal = false 26 | // 27 | shell.mkdir('-p', 'tmp'); 28 | var file = 'tmp/tempscript'+Math.random()+'.js', 29 | script = 'require(\'../../global.js\'); config.silent=true; config.fatal=false; cp("this_file_doesnt_exist", "."); echo("got here");'; 30 | script.to(file); 31 | child.exec('node '+file, function(err, stdout) { 32 | assert.ok(stdout.match('got here')); 33 | 34 | // 35 | // config.fatal = true 36 | // 37 | shell.mkdir('-p', 'tmp'); 38 | var file = 'tmp/tempscript'+Math.random()+'.js', 39 | script = 'require(\'../../global.js\'); config.silent=true; config.fatal=true; cp("this_file_doesnt_exist", "."); echo("got here");'; 40 | script.to(file); 41 | child.exec('node '+file, function(err, stdout) { 42 | assert.ok(!stdout.match('got here')); 43 | 44 | shell.exit(123); 45 | }); 46 | }); 47 | -------------------------------------------------------------------------------- /test/cp.js: -------------------------------------------------------------------------------- 1 | var shell = require('..'); 2 | 3 | var assert = require('assert'), 4 | fs = require('fs'); 5 | 6 | shell.config.silent = true; 7 | 8 | function numLines(str) { 9 | return typeof str === 'string' ? str.match(/\n/g).length : 0; 10 | } 11 | 12 | shell.rm('-rf', 'tmp'); 13 | shell.mkdir('tmp'); 14 | 15 | // 16 | // Invalids 17 | // 18 | 19 | shell.cp(); 20 | assert.ok(shell.error()); 21 | 22 | shell.cp('file1'); 23 | assert.ok(shell.error()); 24 | 25 | shell.cp('-f'); 26 | assert.ok(shell.error()); 27 | 28 | shell.rm('-rf', 'tmp/*'); 29 | shell.cp('-@', 'resources/file1', 'tmp/file1'); // option not supported, files OK 30 | assert.ok(shell.error()); 31 | assert.equal(fs.existsSync('tmp/file1'), false); 32 | 33 | shell.cp('-Z', 'asdfasdf', 'tmp/file2'); // option not supported, files NOT OK 34 | assert.ok(shell.error()); 35 | assert.equal(fs.existsSync('tmp/file2'), false); 36 | 37 | shell.cp('asdfasdf', 'tmp'); // source does not exist 38 | assert.ok(shell.error()); 39 | assert.equal(numLines(shell.error()), 1); 40 | assert.equal(fs.existsSync('tmp/asdfasdf'), false); 41 | 42 | shell.cp('asdfasdf1', 'asdfasdf2', 'tmp'); // sources do not exist 43 | assert.ok(shell.error()); 44 | assert.equal(numLines(shell.error()), 2); 45 | assert.equal(fs.existsSync('tmp/asdfasdf1'), false); 46 | assert.equal(fs.existsSync('tmp/asdfasdf2'), false); 47 | 48 | shell.cp('asdfasdf1', 'asdfasdf2', 'resources/file1'); // too many sources (dest is file) 49 | assert.ok(shell.error()); 50 | 51 | shell.cp('resources/file1', 'resources/file2'); // dest already exists 52 | assert.ok(shell.error()); 53 | 54 | shell.cp('resources/file1', 'resources/file2', 'tmp/a_file'); // too many sources 55 | assert.ok(shell.error()); 56 | assert.equal(fs.existsSync('tmp/a_file'), false); 57 | 58 | // 59 | // Valids 60 | // 61 | 62 | // simple - to dir 63 | shell.cp('resources/file1', 'tmp'); 64 | assert.equal(shell.error(), null); 65 | assert.equal(fs.existsSync('tmp/file1'), true); 66 | 67 | // simple - to file 68 | shell.cp('resources/file2', 'tmp/file2'); 69 | assert.equal(shell.error(), null); 70 | assert.equal(fs.existsSync('tmp/file2'), true); 71 | 72 | // simple - file list 73 | shell.rm('-rf', 'tmp/*'); 74 | shell.cp('resources/file1', 'resources/file2', 'tmp'); 75 | assert.equal(shell.error(), null); 76 | assert.equal(fs.existsSync('tmp/file1'), true); 77 | assert.equal(fs.existsSync('tmp/file2'), true); 78 | 79 | // simple - file list, array syntax 80 | shell.rm('-rf', 'tmp/*'); 81 | shell.cp(['resources/file1', 'resources/file2'], 'tmp'); 82 | assert.equal(shell.error(), null); 83 | assert.equal(fs.existsSync('tmp/file1'), true); 84 | assert.equal(fs.existsSync('tmp/file2'), true); 85 | 86 | shell.cp('resources/file2', 'tmp/file3'); 87 | assert.equal(fs.existsSync('tmp/file3'), true); 88 | shell.cp('-f', 'resources/file2', 'tmp/file3'); // file exists, but -f specified 89 | assert.equal(shell.error(), null); 90 | assert.equal(fs.existsSync('tmp/file3'), true); 91 | 92 | // wildcard 93 | shell.rm('tmp/file1', 'tmp/file2'); 94 | shell.cp('resources/file*', 'tmp'); 95 | assert.equal(shell.error(), null); 96 | assert.equal(fs.existsSync('tmp/file1'), true); 97 | assert.equal(fs.existsSync('tmp/file2'), true); 98 | 99 | //recursive, nothing exists 100 | shell.rm('-rf', 'tmp/*'); 101 | shell.cp('-R', 'resources/cp', 'tmp'); 102 | assert.equal(shell.error(), null); 103 | assert.equal(shell.ls('-R', 'resources/cp') + '', shell.ls('-R', 'tmp/cp') + ''); 104 | 105 | //recursive, nothing exists, source ends in '/' (see Github issue #15) 106 | shell.rm('-rf', 'tmp/*'); 107 | shell.cp('-R', 'resources/cp/', 'tmp/'); 108 | assert.equal(shell.error(), null); 109 | assert.equal(shell.ls('-R', 'resources/cp') + '', shell.ls('-R', 'tmp') + ''); 110 | 111 | //recursive, everything exists, no force flag 112 | shell.rm('-rf', 'tmp/*'); 113 | shell.cp('-R', 'resources/cp', 'tmp'); 114 | shell.cp('-R', 'resources/cp', 'tmp'); 115 | assert.equal(shell.error(), null); // crash test only 116 | 117 | //recursive, everything exists, with force flag 118 | shell.rm('-rf', 'tmp/*'); 119 | shell.cp('-R', 'resources/cp', 'tmp'); 120 | 'changing things around'.to('tmp/cp/dir_a/z'); 121 | assert.notEqual(shell.cat('resources/cp/dir_a/z'), shell.cat('tmp/cp/dir_a/z')); // before cp 122 | shell.cp('-Rf', 'resources/cp', 'tmp'); 123 | assert.equal(shell.error(), null); 124 | assert.equal(shell.cat('resources/cp/dir_a/z'), shell.cat('tmp/cp/dir_a/z')); // after cp 125 | 126 | //recursive, creates dest dir since it's only one level deep (see Github issue #44) 127 | shell.rm('-rf', 'tmp/*'); 128 | shell.cp('-r', 'resources/issue44/*', 'tmp/dir2'); 129 | assert.equal(shell.error(), null); 130 | assert.equal(shell.ls('-R', 'resources/issue44') + '', shell.ls('-R', 'tmp/dir2') + ''); 131 | assert.equal(shell.cat('resources/issue44/main.js'), shell.cat('tmp/dir2/main.js')); 132 | 133 | //recursive, does *not* create dest dir since it's too deep (see Github issue #44) 134 | shell.rm('-rf', 'tmp/*'); 135 | shell.cp('-r', 'resources/issue44/*', 'tmp/dir2/dir3'); 136 | assert.ok(shell.error()); 137 | assert.equal(fs.existsSync('tmp/dir2'), false); 138 | 139 | //preserve mode bits 140 | shell.rm('-rf', 'tmp/*'); 141 | var execBit = parseInt('001', 8); 142 | assert.equal(fs.statSync('resources/cp-mode-bits/executable').mode & execBit, execBit); 143 | shell.cp('resources/cp-mode-bits/executable', 'tmp/executable'); 144 | assert.equal(fs.statSync('resources/cp-mode-bits/executable').mode, fs.statSync('tmp/executable').mode); 145 | 146 | shell.exit(123); 147 | -------------------------------------------------------------------------------- /test/dirs.js: -------------------------------------------------------------------------------- 1 | var shell = require('..'); 2 | 3 | var assert = require('assert'), 4 | path = require('path'); 5 | 6 | shell.config.silent = true; 7 | 8 | var root = path.resolve(); 9 | 10 | shell.pushd('resources/pushd'); 11 | shell.pushd('a'); 12 | 13 | var trail = [ 14 | path.resolve(root, 'resources/pushd/a'), 15 | path.resolve(root, 'resources/pushd'), 16 | root 17 | ]; 18 | 19 | assert.deepEqual(shell.dirs(), trail); 20 | 21 | // Single items 22 | assert.equal(shell.dirs('+0'), trail[0]); 23 | assert.equal(shell.dirs('+1'), trail[1]); 24 | assert.equal(shell.dirs('+2'), trail[2]); 25 | assert.equal(shell.dirs('-0'), trail[2]); 26 | assert.equal(shell.dirs('-1'), trail[1]); 27 | assert.equal(shell.dirs('-2'), trail[0]); 28 | 29 | // Clearing items 30 | assert.deepEqual(shell.dirs('-c'), []); 31 | assert(!shell.error()); 32 | 33 | shell.exit(123); -------------------------------------------------------------------------------- /test/echo.js: -------------------------------------------------------------------------------- 1 | var shell = require('..'); 2 | 3 | var assert = require('assert'), 4 | child = require('child_process'); 5 | 6 | shell.config.silent = true; 7 | 8 | shell.rm('-rf', 'tmp'); 9 | shell.mkdir('tmp'); 10 | 11 | // 12 | // Valids 13 | // 14 | 15 | 16 | // From here on we use child.exec() to intercept the stdout 17 | 18 | 19 | // simple test with defaults 20 | shell.mkdir('-p', 'tmp'); 21 | var file = 'tmp/tempscript'+Math.random()+'.js', 22 | script = 'require(\'../../global.js\'); echo("-asdf", "111");'; // test '-' bug (see issue #20) 23 | script.to(file); 24 | child.exec('node '+file, function(err, stdout) { 25 | assert.ok(stdout === '-asdf 111\n' || stdout === '-asdf 111\nundefined\n'); // 'undefined' for v0.4 26 | 27 | // simple test with silent(true) 28 | shell.mkdir('-p', 'tmp'); 29 | var file = 'tmp/tempscript'+Math.random()+'.js', 30 | script = 'require(\'../../global.js\'); config.silent=true; echo(555);'; 31 | script.to(file); 32 | child.exec('node '+file, function(err, stdout) { 33 | assert.ok(stdout === '555\n' || stdout === '555\nundefined\n'); // 'undefined' for v0.4 34 | 35 | theEnd(); 36 | }); 37 | }); 38 | 39 | function theEnd() { 40 | shell.exit(123); 41 | } 42 | -------------------------------------------------------------------------------- /test/env.js: -------------------------------------------------------------------------------- 1 | var shell = require('..'); 2 | 3 | var assert = require('assert'); 4 | 5 | shell.config.silent = true; 6 | 7 | shell.rm('-rf', 'tmp'); 8 | shell.mkdir('tmp'); 9 | 10 | // 11 | // Valids 12 | // 13 | 14 | assert.equal(shell.env['PATH'], process.env['PATH']); 15 | 16 | shell.env['SHELLJS_TEST'] = 'hello world'; 17 | assert.equal(shell.env['SHELLJS_TEST'], process.env['SHELLJS_TEST']); 18 | 19 | shell.exit(123); 20 | -------------------------------------------------------------------------------- /test/exec.js: -------------------------------------------------------------------------------- 1 | var shell = require('..'); 2 | 3 | var assert = require('assert'), 4 | util = require('util'); 5 | 6 | shell.config.silent = true; 7 | 8 | // 9 | // Invalids 10 | // 11 | 12 | shell.exec(); 13 | assert.ok(shell.error()); 14 | 15 | var result = shell.exec('asdfasdf'); // could not find command 16 | assert.ok(result.code > 0); 17 | 18 | // Test 'fatal' mode for exec, temporarily overriding process.exit 19 | var old_fatal = shell.config.fatal; 20 | var old_exit = process.exit; 21 | 22 | var exitcode = 9999; 23 | process.exit = function (_exitcode) { 24 | exitcode = _exitcode; 25 | }; 26 | 27 | shell.config.fatal = true; 28 | 29 | var result = shell.exec('asdfasdf'); // could not find command 30 | assert.equal(exitcode, 1); 31 | 32 | shell.config.fatal = old_fatal; 33 | process.exit = old_exit; 34 | 35 | // 36 | // Valids 37 | // 38 | 39 | // 40 | // sync 41 | // 42 | 43 | // check if stdout goes to output 44 | var result = shell.exec('node -e \"console.log(1234);\"'); 45 | assert.equal(shell.error(), null); 46 | assert.equal(result.code, 0); 47 | assert.ok(result.output === '1234\n' || result.output === '1234\nundefined\n'); // 'undefined' for v0.4 48 | 49 | // check if stderr goes to output 50 | var result = shell.exec('node -e \"console.error(1234);\"'); 51 | assert.equal(shell.error(), null); 52 | assert.equal(result.code, 0); 53 | assert.ok(result.output === '1234\n' || result.output === '1234\nundefined\n'); // 'undefined' for v0.4 54 | 55 | // check if stdout + stderr go to output 56 | var result = shell.exec('node -e \"console.error(1234); console.log(666);\"'); 57 | assert.equal(shell.error(), null); 58 | assert.equal(result.code, 0); 59 | assert.ok(result.output === '1234\n666\n' || result.output === '1234\n666\nundefined\n'); // 'undefined' for v0.4 60 | 61 | // check exit code 62 | var result = shell.exec('node -e \"process.exit(12);\"'); 63 | assert.equal(shell.error(), null); 64 | assert.equal(result.code, 12); 65 | 66 | // interaction with cd 67 | shell.cd('resources/external'); 68 | var result = shell.exec('node node_script.js'); 69 | assert.equal(shell.error(), null); 70 | assert.equal(result.code, 0); 71 | assert.equal(result.output, 'node_script_1234\n'); 72 | shell.cd('../..'); 73 | 74 | // check quotes escaping 75 | var result = shell.exec( util.format('node -e "console.log(%s);"', "\\\"\\'+\\'_\\'+\\'\\\"") ); 76 | assert.equal(shell.error(), null); 77 | assert.equal(result.code, 0); 78 | assert.equal(result.output, "'+'_'+'\n"); 79 | 80 | // 81 | // async 82 | // 83 | 84 | // no callback 85 | var c = shell.exec('node -e \"console.log(1234)\"', {async:true}); 86 | assert.equal(shell.error(), null); 87 | assert.ok('stdout' in c, 'async exec returns child process object'); 88 | 89 | // 90 | // callback as 2nd argument 91 | // 92 | shell.exec('node -e \"console.log(5678);\"', function(code, output) { 93 | assert.equal(code, 0); 94 | assert.ok(output === '5678\n' || output === '5678\nundefined\n'); // 'undefined' for v0.4 95 | 96 | // 97 | // callback as 3rd argument 98 | // 99 | shell.exec('node -e \"console.log(5566);\"', {async:true}, function(code, output) { 100 | assert.equal(code, 0); 101 | assert.ok(output === '5566\n' || output === '5566\nundefined\n'); // 'undefined' for v0.4 102 | 103 | // 104 | // callback as 3rd argument (slient:true) 105 | // 106 | shell.exec('node -e \"console.log(5678);\"', {silent:true}, function(code, output) { 107 | assert.equal(code, 0); 108 | assert.ok(output === '5678\n' || output === '5678\nundefined\n'); // 'undefined' for v0.4 109 | 110 | shell.exit(123); 111 | 112 | }); 113 | 114 | }); 115 | 116 | }); 117 | 118 | assert.equal(shell.error(), null); 119 | -------------------------------------------------------------------------------- /test/find.js: -------------------------------------------------------------------------------- 1 | var shell = require('..'); 2 | 3 | var assert = require('assert'); 4 | 5 | shell.config.silent = true; 6 | 7 | shell.rm('-rf', 'tmp'); 8 | shell.mkdir('tmp'); 9 | 10 | // 11 | // Invalids 12 | // 13 | 14 | var result = shell.find(); // no paths given 15 | assert.ok(shell.error()); 16 | 17 | // 18 | // Valids 19 | // 20 | 21 | // current path 22 | shell.cd('resources/find'); 23 | var result = shell.find('.'); 24 | assert.equal(shell.error(), null); 25 | assert.equal(result.indexOf('.hidden') > -1, true); 26 | assert.equal(result.indexOf('dir1/dir11/a_dir11') > -1, true); 27 | assert.equal(result.length, 11); 28 | shell.cd('../..'); 29 | 30 | // simple path 31 | var result = shell.find('resources/find'); 32 | assert.equal(shell.error(), null); 33 | assert.equal(result.indexOf('resources/find/.hidden') > -1, true); 34 | assert.equal(result.indexOf('resources/find/dir1/dir11/a_dir11') > -1, true); 35 | assert.equal(result.length, 11); 36 | 37 | // multiple paths - comma 38 | var result = shell.find('resources/find/dir1', 'resources/find/dir2'); 39 | assert.equal(shell.error(), null); 40 | assert.equal(result.indexOf('resources/find/dir1/dir11/a_dir11') > -1, true); 41 | assert.equal(result.indexOf('resources/find/dir2/a_dir1') > -1, true); 42 | assert.equal(result.length, 6); 43 | 44 | // multiple paths - array 45 | var result = shell.find(['resources/find/dir1', 'resources/find/dir2']); 46 | assert.equal(shell.error(), null); 47 | assert.equal(result.indexOf('resources/find/dir1/dir11/a_dir11') > -1, true); 48 | assert.equal(result.indexOf('resources/find/dir2/a_dir1') > -1, true); 49 | assert.equal(result.length, 6); 50 | 51 | shell.exit(123); 52 | -------------------------------------------------------------------------------- /test/grep.js: -------------------------------------------------------------------------------- 1 | var shell = require('..'); 2 | 3 | var assert = require('assert'), 4 | fs = require('fs'); 5 | 6 | shell.config.silent = true; 7 | 8 | shell.rm('-rf', 'tmp'); 9 | shell.mkdir('tmp'); 10 | 11 | // 12 | // Invalids 13 | // 14 | 15 | shell.grep(); 16 | assert.ok(shell.error()); 17 | 18 | shell.grep(/asdf/g); // too few args 19 | assert.ok(shell.error()); 20 | 21 | assert.equal(fs.existsSync('/asdfasdf'), false); // sanity check 22 | shell.grep(/asdf/g, '/asdfasdf'); // no such file 23 | assert.ok(shell.error()); 24 | 25 | // 26 | // Valids 27 | // 28 | 29 | var result = shell.grep('line', 'resources/a.txt'); 30 | assert.equal(shell.error(), null); 31 | assert.equal(result.split('\n').length - 1, 4); 32 | 33 | var result = shell.grep('-v', 'line', 'resources/a.txt'); 34 | assert.equal(shell.error(), null); 35 | assert.equal(result.split('\n').length - 1, 8); 36 | 37 | var result = shell.grep('line one', 'resources/a.txt'); 38 | assert.equal(shell.error(), null); 39 | assert.equal(result, 'This is line one\n'); 40 | 41 | // multiple files 42 | var result = shell.grep(/test/, 'resources/file1.txt', 'resources/file2.txt'); 43 | assert.equal(shell.error(), null); 44 | assert.equal(result, 'test1\ntest2\n'); 45 | 46 | // multiple files, array syntax 47 | var result = shell.grep(/test/, ['resources/file1.txt', 'resources/file2.txt']); 48 | assert.equal(shell.error(), null); 49 | assert.equal(result, 'test1\ntest2\n'); 50 | 51 | // multiple files, glob syntax, * for file name 52 | var result = shell.grep(/test/, 'resources/file*.txt'); 53 | assert.equal(shell.error(), null); 54 | assert.ok(result == 'test1\ntest2\n' || result == 'test2\ntest1\n'); 55 | 56 | // multiple files, glob syntax, * for directory name 57 | var result = shell.grep(/test/, '*/file*.txt'); 58 | assert.equal(shell.error(), null); 59 | assert.ok(result == 'test1\ntest2\n' || result == 'test2\ntest1\n'); 60 | 61 | // multiple files, glob syntax, ** for directory name 62 | var result = shell.grep(/test/, '**/file*.js'); 63 | assert.equal(shell.error(), null); 64 | assert.equal(result, 'test\ntest\ntest\ntest\n'); 65 | 66 | shell.exit(123); 67 | -------------------------------------------------------------------------------- /test/ln.js: -------------------------------------------------------------------------------- 1 | var shell = require('..'); 2 | 3 | var assert = require('assert'), 4 | fs = require('fs'), 5 | path = require('path'); 6 | 7 | shell.config.silent = true; 8 | 9 | shell.rm('-rf', 'tmp'); 10 | shell.mkdir('tmp'); 11 | 12 | // Prepare tmp/ 13 | shell.cp('resources/*', 'tmp'); 14 | 15 | // 16 | // Invalids 17 | // 18 | 19 | shell.ln(); 20 | assert.ok(shell.error()); 21 | 22 | shell.ln('file'); 23 | assert.ok(shell.error()); 24 | 25 | shell.ln('-f'); 26 | assert.ok(shell.error()); 27 | 28 | shell.ln('tmp/file1', 'tmp/file2'); 29 | assert.ok(shell.error()); 30 | 31 | shell.ln('tmp/noexist', 'tmp/linkfile1'); 32 | assert.ok(shell.error()); 33 | 34 | // 35 | // Valids 36 | // 37 | 38 | shell.ln('tmp/file1', 'tmp/linkfile1'); 39 | assert(fs.existsSync('tmp/linkfile1')); 40 | assert.equal( 41 | fs.readFileSync('tmp/file1').toString(), 42 | fs.readFileSync('tmp/linkfile1').toString() 43 | ); 44 | fs.writeFileSync('tmp/file1', 'new content 1'); 45 | assert.equal( 46 | fs.readFileSync('tmp/linkfile1').toString(), 47 | 'new content 1' 48 | ); 49 | 50 | shell.ln('-s', 'tmp/file2', 'tmp/linkfile2'); 51 | assert(fs.existsSync('tmp/linkfile2')); 52 | assert.equal( 53 | fs.readFileSync('tmp/file2').toString(), 54 | fs.readFileSync('tmp/linkfile2').toString() 55 | ); 56 | fs.writeFileSync('tmp/file2', 'new content 2'); 57 | assert.equal( 58 | fs.readFileSync('tmp/linkfile2').toString(), 59 | 'new content 2' 60 | ); 61 | 62 | shell.ln('-f', 'tmp/file1.js', 'tmp/file2.js'); 63 | assert(fs.existsSync('tmp/file2.js')); 64 | assert.equal( 65 | fs.readFileSync('tmp/file1.js').toString(), 66 | fs.readFileSync('tmp/file2.js').toString() 67 | ); 68 | fs.writeFileSync('tmp/file1.js', 'new content js'); 69 | assert.equal( 70 | fs.readFileSync('tmp/file2.js').toString(), 71 | 'new content js' 72 | ); 73 | 74 | shell.ln('-sf', 'tmp/file1.txt', 'tmp/file2.txt'); 75 | assert(fs.existsSync('tmp/file2.txt')); 76 | assert.equal( 77 | fs.readFileSync('tmp/file1.txt').toString(), 78 | fs.readFileSync('tmp/file2.txt').toString() 79 | ); 80 | fs.writeFileSync('tmp/file1.txt', 'new content txt'); 81 | assert.equal( 82 | fs.readFileSync('tmp/file2.txt').toString(), 83 | 'new content txt' 84 | ); 85 | 86 | // Abspath regression 87 | shell.ln('-sf', 'tmp/file1', path.resolve('tmp/abspath')); 88 | assert(fs.existsSync('tmp/abspath')); 89 | assert.equal( 90 | fs.readFileSync('tmp/file1').toString(), 91 | fs.readFileSync('tmp/abspath').toString() 92 | ); 93 | fs.writeFileSync('tmp/file1', 'new content 3'); 94 | assert.equal( 95 | fs.readFileSync('tmp/abspath').toString(), 96 | 'new content 3' 97 | ); 98 | 99 | shell.exit(123); 100 | -------------------------------------------------------------------------------- /test/ls.js: -------------------------------------------------------------------------------- 1 | var shell = require('..'); 2 | 3 | var assert = require('assert'), 4 | fs = require('fs'); 5 | 6 | shell.config.silent = true; 7 | 8 | shell.rm('-rf', 'tmp'); 9 | shell.mkdir('tmp'); 10 | 11 | // 12 | // Invalids 13 | // 14 | 15 | assert.equal(fs.existsSync('/asdfasdf'), false); // sanity check 16 | var result = shell.ls('/asdfasdf'); // no such file or dir 17 | assert.ok(shell.error()); 18 | assert.equal(result.length, 0); 19 | 20 | // 21 | // Valids 22 | // 23 | 24 | var result = shell.ls(); 25 | assert.equal(shell.error(), null); 26 | 27 | var result = shell.ls('/'); 28 | assert.equal(shell.error(), null); 29 | 30 | // no args 31 | shell.cd('resources/ls'); 32 | var result = shell.ls(); 33 | assert.equal(shell.error(), null); 34 | assert.equal(result.indexOf('file1') > -1, true); 35 | assert.equal(result.indexOf('file2') > -1, true); 36 | assert.equal(result.indexOf('file1.js') > -1, true); 37 | assert.equal(result.indexOf('file2.js') > -1, true); 38 | assert.equal(result.indexOf('filename(with)[chars$]^that.must+be-escaped') > -1, true); 39 | assert.equal(result.indexOf('a_dir') > -1, true); 40 | assert.equal(result.length, 6); 41 | shell.cd('../..'); 42 | 43 | // simple arg 44 | var result = shell.ls('resources/ls'); 45 | assert.equal(shell.error(), null); 46 | assert.equal(result.indexOf('file1') > -1, true); 47 | assert.equal(result.indexOf('file2') > -1, true); 48 | assert.equal(result.indexOf('file1.js') > -1, true); 49 | assert.equal(result.indexOf('file2.js') > -1, true); 50 | assert.equal(result.indexOf('filename(with)[chars$]^that.must+be-escaped') > -1, true); 51 | assert.equal(result.indexOf('a_dir') > -1, true); 52 | assert.equal(result.length, 6); 53 | 54 | // no args, 'all' option 55 | shell.cd('resources/ls'); 56 | var result = shell.ls('-A'); 57 | assert.equal(shell.error(), null); 58 | assert.equal(result.indexOf('file1') > -1, true); 59 | assert.equal(result.indexOf('file2') > -1, true); 60 | assert.equal(result.indexOf('file1.js') > -1, true); 61 | assert.equal(result.indexOf('file2.js') > -1, true); 62 | assert.equal(result.indexOf('filename(with)[chars$]^that.must+be-escaped') > -1, true); 63 | assert.equal(result.indexOf('a_dir') > -1, true); 64 | assert.equal(result.indexOf('.hidden_file') > -1, true); 65 | assert.equal(result.indexOf('.hidden_dir') > -1, true); 66 | assert.equal(result.length, 8); 67 | shell.cd('../..'); 68 | 69 | // no args, 'all' option 70 | shell.cd('resources/ls'); 71 | var result = shell.ls('-a'); // (deprecated) backwards compatibility test 72 | assert.equal(shell.error(), null); 73 | assert.equal(result.indexOf('file1') > -1, true); 74 | assert.equal(result.indexOf('file2') > -1, true); 75 | assert.equal(result.indexOf('file1.js') > -1, true); 76 | assert.equal(result.indexOf('file2.js') > -1, true); 77 | assert.equal(result.indexOf('filename(with)[chars$]^that.must+be-escaped') > -1, true); 78 | assert.equal(result.indexOf('a_dir') > -1, true); 79 | assert.equal(result.indexOf('.hidden_file') > -1, true); 80 | assert.equal(result.indexOf('.hidden_dir') > -1, true); 81 | assert.equal(result.length, 8); 82 | shell.cd('../..'); 83 | 84 | // wildcard, simple 85 | var result = shell.ls('resources/ls/*'); 86 | assert.equal(shell.error(), null); 87 | assert.equal(result.indexOf('resources/ls/file1') > -1, true); 88 | assert.equal(result.indexOf('resources/ls/file2') > -1, true); 89 | assert.equal(result.indexOf('resources/ls/file1.js') > -1, true); 90 | assert.equal(result.indexOf('resources/ls/file2.js') > -1, true); 91 | assert.equal(result.indexOf('resources/ls/filename(with)[chars$]^that.must+be-escaped') > -1, true); 92 | assert.equal(result.indexOf('resources/ls/a_dir') > -1, true); 93 | assert.equal(result.length, 6); 94 | 95 | // wildcard, hidden only 96 | var result = shell.ls('resources/ls/.*'); 97 | assert.equal(shell.error(), null); 98 | assert.equal(result.indexOf('resources/ls/.hidden_file') > -1, true); 99 | assert.equal(result.indexOf('resources/ls/.hidden_dir') > -1, true); 100 | assert.equal(result.length, 2); 101 | 102 | // wildcard, mid-file 103 | var result = shell.ls('resources/ls/f*le*'); 104 | assert.equal(shell.error(), null); 105 | assert.equal(result.length, 5); 106 | assert.equal(result.indexOf('resources/ls/file1') > -1, true); 107 | assert.equal(result.indexOf('resources/ls/file2') > -1, true); 108 | assert.equal(result.indexOf('resources/ls/file1.js') > -1, true); 109 | assert.equal(result.indexOf('resources/ls/file2.js') > -1, true); 110 | assert.equal(result.indexOf('resources/ls/filename(with)[chars$]^that.must+be-escaped') > -1, true); 111 | 112 | // wildcard, mid-file with dot (should escape dot for regex) 113 | var result = shell.ls('resources/ls/f*le*.js'); 114 | assert.equal(shell.error(), null); 115 | assert.equal(result.length, 2); 116 | assert.equal(result.indexOf('resources/ls/file1.js') > -1, true); 117 | assert.equal(result.indexOf('resources/ls/file2.js') > -1, true); 118 | 119 | // wildcard, should not do partial matches 120 | var result = shell.ls('resources/ls/*.j'); // shouldn't get .js 121 | assert.equal(shell.error(), null); 122 | assert.equal(result.length, 0); 123 | 124 | // wildcard, all files with extension 125 | var result = shell.ls('resources/ls/*.*'); 126 | assert.equal(shell.error(), null); 127 | assert.equal(result.length, 3); 128 | assert.equal(result.indexOf('resources/ls/file1.js') > -1, true); 129 | assert.equal(result.indexOf('resources/ls/file2.js') > -1, true); 130 | assert.equal(result.indexOf('resources/ls/filename(with)[chars$]^that.must+be-escaped') > -1, true); 131 | 132 | // wildcard, with additional path 133 | var result = shell.ls('resources/ls/f*le*.js', 'resources/ls/a_dir'); 134 | assert.equal(shell.error(), null); 135 | assert.equal(result.length, 4); 136 | assert.equal(result.indexOf('resources/ls/file1.js') > -1, true); 137 | assert.equal(result.indexOf('resources/ls/file2.js') > -1, true); 138 | assert.equal(result.indexOf('b_dir') > -1, true); // no wildcard == no path prefix 139 | assert.equal(result.indexOf('nada') > -1, true); // no wildcard == no path prefix 140 | 141 | // wildcard for both paths 142 | var result = shell.ls('resources/ls/f*le*.js', 'resources/ls/a_dir/*'); 143 | assert.equal(shell.error(), null); 144 | assert.equal(result.length, 4); 145 | assert.equal(result.indexOf('resources/ls/file1.js') > -1, true); 146 | assert.equal(result.indexOf('resources/ls/file2.js') > -1, true); 147 | assert.equal(result.indexOf('resources/ls/a_dir/b_dir') > -1, true); 148 | assert.equal(result.indexOf('resources/ls/a_dir/nada') > -1, true); 149 | 150 | // wildcard for both paths, array 151 | var result = shell.ls(['resources/ls/f*le*.js', 'resources/ls/a_dir/*']); 152 | assert.equal(shell.error(), null); 153 | assert.equal(result.length, 4); 154 | assert.equal(result.indexOf('resources/ls/file1.js') > -1, true); 155 | assert.equal(result.indexOf('resources/ls/file2.js') > -1, true); 156 | assert.equal(result.indexOf('resources/ls/a_dir/b_dir') > -1, true); 157 | assert.equal(result.indexOf('resources/ls/a_dir/nada') > -1, true); 158 | 159 | // recursive, no path 160 | shell.cd('resources/ls'); 161 | var result = shell.ls('-R'); 162 | assert.equal(shell.error(), null); 163 | assert.equal(result.indexOf('a_dir') > -1, true); 164 | assert.equal(result.indexOf('a_dir/b_dir') > -1, true); 165 | assert.equal(result.indexOf('a_dir/b_dir/z') > -1, true); 166 | assert.equal(result.length, 9); 167 | shell.cd('../..'); 168 | 169 | // recusive, path given 170 | var result = shell.ls('-R', 'resources/ls'); 171 | assert.equal(shell.error(), null); 172 | assert.equal(result.indexOf('a_dir') > -1, true); 173 | assert.equal(result.indexOf('a_dir/b_dir') > -1, true); 174 | assert.equal(result.indexOf('a_dir/b_dir/z') > -1, true); 175 | assert.equal(result.length, 9); 176 | 177 | // recusive, path given - 'all' flag 178 | var result = shell.ls('-RA', 'resources/ls'); 179 | assert.equal(shell.error(), null); 180 | assert.equal(result.indexOf('a_dir') > -1, true); 181 | assert.equal(result.indexOf('a_dir/b_dir') > -1, true); 182 | assert.equal(result.indexOf('a_dir/b_dir/z') > -1, true); 183 | assert.equal(result.indexOf('a_dir/.hidden_dir/nada') > -1, true); 184 | assert.equal(result.length, 14); 185 | 186 | // recursive, wildcard 187 | var result = shell.ls('-R', 'resources/ls/*'); 188 | assert.equal(shell.error(), null); 189 | assert.equal(result.indexOf('resources/ls/a_dir') > -1, true); 190 | assert.equal(result.indexOf('resources/ls/a_dir/b_dir') > -1, true); 191 | assert.equal(result.indexOf('resources/ls/a_dir/b_dir/z') > -1, true); 192 | assert.equal(result.length, 9); 193 | 194 | shell.exit(123); 195 | -------------------------------------------------------------------------------- /test/make.js: -------------------------------------------------------------------------------- 1 | var shell = require('..'), 2 | child = require('child_process'), 3 | assert = require('assert'); 4 | 5 | shell.mkdir('-p', 'tmp'); 6 | var file = 'tmp/tempscript'+Math.random()+'.js', 7 | script = 'require(\'../../make.js\');' + 8 | 'target.all=function(){' + 9 | ' echo("first"); '+ 10 | ' cp("this_file_doesnt_exist", ".");' + 11 | ' echo("second");' + 12 | '}'; 13 | 14 | script.to(file); 15 | child.exec('node '+file, function(err, stdout) { 16 | assert.ok(stdout.match('first')); 17 | assert.ok(!stdout.match('second')); // Make should die on errors, so this should never get echoed 18 | 19 | shell.exit(123); 20 | }); 21 | -------------------------------------------------------------------------------- /test/mkdir.js: -------------------------------------------------------------------------------- 1 | var shell = require('..'); 2 | 3 | var assert = require('assert'), 4 | fs = require('fs'); 5 | 6 | shell.config.silent = true; 7 | 8 | function numLines(str) { 9 | return typeof str === 'string' ? str.match(/\n/g).length : 0; 10 | } 11 | 12 | shell.rm('-rf', 'tmp'); 13 | shell.mkdir('tmp'); 14 | 15 | // 16 | // Invalids 17 | // 18 | 19 | shell.mkdir(); 20 | assert.ok(shell.error()); 21 | 22 | var mtime = fs.statSync('tmp').mtime.toString(); 23 | shell.mkdir('tmp'); // dir already exists 24 | assert.ok(shell.error()); 25 | assert.equal(fs.statSync('tmp').mtime.toString(), mtime); // didn't mess with dir 26 | 27 | assert.equal(fs.existsSync('/asdfasdf'), false); // sanity check 28 | shell.mkdir('/asdfasdf/asdfasdf'); // root path does not exist 29 | assert.ok(shell.error()); 30 | assert.equal(fs.existsSync('/asdfasdf'), false); 31 | 32 | // 33 | // Valids 34 | // 35 | 36 | assert.equal(fs.existsSync('tmp/t1'), false); 37 | shell.mkdir('tmp/t1'); // simple dir 38 | assert.equal(shell.error(), null); 39 | assert.equal(fs.existsSync('tmp/t1'), true); 40 | 41 | assert.equal(fs.existsSync('tmp/t2'), false); 42 | assert.equal(fs.existsSync('tmp/t3'), false); 43 | shell.mkdir('tmp/t2', 'tmp/t3'); // multiple dirs 44 | assert.equal(shell.error(), null); 45 | assert.equal(fs.existsSync('tmp/t2'), true); 46 | assert.equal(fs.existsSync('tmp/t3'), true); 47 | 48 | assert.equal(fs.existsSync('tmp/t1'), true); 49 | assert.equal(fs.existsSync('tmp/t4'), false); 50 | shell.mkdir('tmp/t1', 'tmp/t4'); // one dir exists, one doesn't 51 | assert.equal(numLines(shell.error()), 1); 52 | assert.equal(fs.existsSync('tmp/t1'), true); 53 | assert.equal(fs.existsSync('tmp/t4'), true); 54 | 55 | assert.equal(fs.existsSync('tmp/a'), false); 56 | shell.mkdir('-p', 'tmp/a/b/c'); 57 | assert.equal(shell.error(), null); 58 | assert.equal(fs.existsSync('tmp/a/b/c'), true); 59 | shell.rm('-Rf', 'tmp/a'); // revert 60 | 61 | // multiple dirs 62 | shell.mkdir('-p', 'tmp/zzza', 'tmp/zzzb', 'tmp/zzzc'); 63 | assert.equal(shell.error(), null); 64 | assert.equal(fs.existsSync('tmp/zzza'), true); 65 | assert.equal(fs.existsSync('tmp/zzzb'), true); 66 | assert.equal(fs.existsSync('tmp/zzzc'), true); 67 | 68 | // multiple dirs, array syntax 69 | shell.mkdir('-p', ['tmp/yyya', 'tmp/yyyb', 'tmp/yyyc']); 70 | assert.equal(shell.error(), null); 71 | assert.equal(fs.existsSync('tmp/yyya'), true); 72 | assert.equal(fs.existsSync('tmp/yyyb'), true); 73 | assert.equal(fs.existsSync('tmp/yyyc'), true); 74 | 75 | shell.exit(123); 76 | -------------------------------------------------------------------------------- /test/mv.js: -------------------------------------------------------------------------------- 1 | var shell = require('..'); 2 | 3 | var assert = require('assert'), 4 | fs = require('fs'); 5 | 6 | shell.config.silent = true; 7 | 8 | function numLines(str) { 9 | return typeof str === 'string' ? str.match(/\n/g).length : 0; 10 | } 11 | 12 | shell.rm('-rf', 'tmp'); 13 | shell.mkdir('tmp'); 14 | 15 | // Prepare tmp/ 16 | shell.cp('resources/*', 'tmp'); 17 | 18 | // 19 | // Invalids 20 | // 21 | 22 | shell.mv(); 23 | assert.ok(shell.error()); 24 | 25 | shell.mv('file1'); 26 | assert.ok(shell.error()); 27 | 28 | shell.mv('-f'); 29 | assert.ok(shell.error()); 30 | 31 | shell.mv('-Z', 'tmp/file1', 'tmp/file1'); // option not supported 32 | assert.ok(shell.error()); 33 | assert.equal(fs.existsSync('tmp/file1'), true); 34 | 35 | shell.mv('asdfasdf', 'tmp'); // source does not exist 36 | assert.ok(shell.error()); 37 | assert.equal(numLines(shell.error()), 1); 38 | assert.equal(fs.existsSync('tmp/asdfasdf'), false); 39 | 40 | shell.mv('asdfasdf1', 'asdfasdf2', 'tmp'); // sources do not exist 41 | assert.ok(shell.error()); 42 | assert.equal(numLines(shell.error()), 2); 43 | assert.equal(fs.existsSync('tmp/asdfasdf1'), false); 44 | assert.equal(fs.existsSync('tmp/asdfasdf2'), false); 45 | 46 | shell.mv('asdfasdf1', 'asdfasdf2', 'tmp/file1'); // too many sources (dest is file) 47 | assert.ok(shell.error()); 48 | 49 | shell.mv('tmp/file1', 'tmp/file2'); // dest already exists 50 | assert.ok(shell.error()); 51 | 52 | shell.mv('tmp/file1', 'tmp/file2', 'tmp/a_file'); // too many sources (exist, but dest is file) 53 | assert.ok(shell.error()); 54 | assert.equal(fs.existsSync('tmp/a_file'), false); 55 | 56 | shell.mv('tmp/file*', 'tmp/file1'); // can't use wildcard when dest is file 57 | assert.ok(shell.error()); 58 | assert.equal(fs.existsSync('tmp/file1'), true); 59 | assert.equal(fs.existsSync('tmp/file2'), true); 60 | assert.equal(fs.existsSync('tmp/file1.js'), true); 61 | assert.equal(fs.existsSync('tmp/file2.js'), true); 62 | 63 | // 64 | // Valids 65 | // 66 | 67 | shell.cd('tmp'); 68 | 69 | // handles self OK 70 | shell.mkdir('tmp2'); 71 | shell.mv('*', 'tmp2'); // has to handle self (tmp2 --> tmp2) without throwing error 72 | assert.ok(shell.error()); // there's an error, but not fatal 73 | assert.equal(fs.existsSync('tmp2/file1'), true); // moved OK 74 | shell.mv('tmp2/*', '.'); // revert 75 | assert.equal(fs.existsSync('file1'), true); // moved OK 76 | 77 | shell.mv('file1', 'file3'); // one source 78 | assert.equal(shell.error(), null); 79 | assert.equal(fs.existsSync('file1'), false); 80 | assert.equal(fs.existsSync('file3'), true); 81 | shell.mv('file3', 'file1'); // revert 82 | assert.equal(shell.error(), null); 83 | assert.equal(fs.existsSync('file1'), true); 84 | 85 | // two sources 86 | shell.rm('-rf', 't'); 87 | shell.mkdir('-p', 't'); 88 | shell.mv('file1', 'file2', 't'); 89 | assert.equal(shell.error(), null); 90 | assert.equal(fs.existsSync('file1'), false); 91 | assert.equal(fs.existsSync('file2'), false); 92 | assert.equal(fs.existsSync('t/file1'), true); 93 | assert.equal(fs.existsSync('t/file2'), true); 94 | shell.mv('t/*', '.'); // revert 95 | assert.equal(fs.existsSync('file1'), true); 96 | assert.equal(fs.existsSync('file2'), true); 97 | 98 | // two sources, array style 99 | shell.rm('-rf', 't'); 100 | shell.mkdir('-p', 't'); 101 | shell.mv(['file1', 'file2'], 't'); // two sources 102 | assert.equal(shell.error(), null); 103 | assert.equal(fs.existsSync('file1'), false); 104 | assert.equal(fs.existsSync('file2'), false); 105 | assert.equal(fs.existsSync('t/file1'), true); 106 | assert.equal(fs.existsSync('t/file2'), true); 107 | shell.mv('t/*', '.'); // revert 108 | assert.equal(fs.existsSync('file1'), true); 109 | assert.equal(fs.existsSync('file2'), true); 110 | 111 | shell.mv('file*.js', 't'); // wildcard 112 | assert.equal(shell.error(), null); 113 | assert.equal(fs.existsSync('file1.js'), false); 114 | assert.equal(fs.existsSync('file2.js'), false); 115 | assert.equal(fs.existsSync('t/file1.js'), true); 116 | assert.equal(fs.existsSync('t/file2.js'), true); 117 | shell.mv('t/*', '.'); // revert 118 | assert.equal(fs.existsSync('file1.js'), true); 119 | assert.equal(fs.existsSync('file2.js'), true); 120 | 121 | shell.mv('-f', 'file1', 'file2'); // dest exists, but -f given 122 | assert.equal(shell.error(), null); 123 | assert.equal(fs.existsSync('file1'), false); 124 | assert.equal(fs.existsSync('file2'), true); 125 | 126 | shell.exit(123); 127 | -------------------------------------------------------------------------------- /test/popd.js: -------------------------------------------------------------------------------- 1 | var shell = require('..'); 2 | 3 | var assert = require('assert'), 4 | path = require('path'); 5 | 6 | shell.config.silent = true; 7 | 8 | var root = path.resolve(), trail; 9 | 10 | function reset() { 11 | shell.dirs('-c'); 12 | shell.cd(root); 13 | } 14 | 15 | // Valid 16 | shell.pushd('resources/pushd'); 17 | trail = shell.popd(); 18 | assert.equal(shell.error(), null); 19 | assert.equal(process.cwd(), trail[0]); 20 | assert.deepEqual(trail, [ root ]); 21 | 22 | shell.pushd('resources/pushd'); 23 | shell.pushd('a'); 24 | trail = shell.popd(); 25 | assert.equal(shell.error(), null); 26 | assert.equal(process.cwd(), trail[0]); 27 | assert.deepEqual(trail, [ 28 | path.resolve(root, 'resources/pushd'), 29 | root 30 | ]); 31 | 32 | shell.pushd('b'); 33 | trail = shell.popd(); 34 | assert.equal(shell.error(), null); 35 | assert.equal(process.cwd(), trail[0]); 36 | assert.deepEqual(trail, [ 37 | path.resolve(root, 'resources/pushd'), 38 | root 39 | ]); 40 | 41 | shell.pushd('b'); 42 | shell.pushd('c'); 43 | trail = shell.popd(); 44 | assert.equal(shell.error(), null); 45 | assert.equal(process.cwd(), trail[0]); 46 | assert.deepEqual(trail, [ 47 | path.resolve(root, 'resources/pushd/b'), 48 | path.resolve(root, 'resources/pushd'), 49 | root 50 | ]); 51 | 52 | trail = shell.popd(); 53 | assert.equal(shell.error(), null); 54 | assert.equal(process.cwd(), trail[0]); 55 | assert.deepEqual(trail, [ 56 | path.resolve(root, 'resources/pushd'), 57 | root 58 | ]); 59 | 60 | trail = shell.popd(); 61 | assert.equal(shell.error(), null); 62 | assert.equal(trail.length, 1); 63 | assert.equal(process.cwd(), trail[0]); 64 | assert.deepEqual(trail, [ root ]); 65 | 66 | // Valid by index 67 | shell.pushd('resources/pushd'); 68 | trail = shell.popd('+0'); 69 | assert.equal(shell.error(), null); 70 | assert.equal(process.cwd(), trail[0]); 71 | assert.deepEqual(trail, [ root ]); 72 | 73 | shell.pushd('resources/pushd'); 74 | trail = shell.popd('+1'); 75 | assert.equal(shell.error(), null); 76 | assert.equal(process.cwd(), trail[0]); 77 | assert.deepEqual(trail, [ path.resolve(root, 'resources/pushd') ]); 78 | 79 | reset(); shell.pushd('resources/pushd'); 80 | trail = shell.popd('-0'); 81 | assert.equal(shell.error(), null); 82 | assert.equal(process.cwd(), trail[0]); 83 | assert.deepEqual(trail, [ path.resolve(root, 'resources/pushd') ]); 84 | 85 | reset(); shell.pushd('resources/pushd'); 86 | trail = shell.popd('-1'); 87 | assert.equal(shell.error(), null); 88 | assert.equal(process.cwd(), trail[0]); 89 | assert.deepEqual(trail, [ root ]); 90 | 91 | 92 | reset(); shell.pushd('resources/pushd'); 93 | trail = shell.popd('-n'); 94 | assert.equal(shell.error(), null); 95 | assert.equal(process.cwd(), trail[0]); 96 | assert.deepEqual(trail, [ path.resolve(root, 'resources/pushd') ]); 97 | 98 | // Invalid 99 | trail = shell.popd(); 100 | assert.ok(shell.error('popd: directory stack empty\n')); 101 | 102 | // Test that the root dir is not stored 103 | shell.cd('resources/pushd'); 104 | shell.pushd('b'); 105 | trail = shell.popd(); 106 | assert.equal(shell.error(), null); 107 | assert.equal(trail[0], path.resolve(root, 'resources/pushd')); 108 | assert.equal(process.cwd(), trail[0]); 109 | shell.popd(); 110 | assert.ok(shell.error(), null); 111 | 112 | shell.cd(root); 113 | 114 | shell.exit(123); -------------------------------------------------------------------------------- /test/pushd.js: -------------------------------------------------------------------------------- 1 | var shell = require('..'); 2 | 3 | var assert = require('assert'), 4 | path = require('path'); 5 | 6 | shell.config.silent = true; 7 | 8 | var root = path.resolve(), trail; 9 | 10 | function reset() { 11 | shell.dirs('-c'); 12 | shell.cd(root); 13 | } 14 | 15 | // Push valid directories 16 | trail = shell.pushd('resources/pushd'); 17 | assert.equal(shell.error(), null); 18 | assert.equal(process.cwd(), trail[0]); 19 | assert.deepEqual(trail, [ 20 | path.resolve(root, 'resources/pushd'), 21 | root 22 | ]); 23 | 24 | trail = shell.pushd('a'); 25 | assert.equal(shell.error(), null); 26 | assert.equal(process.cwd(), trail[0]); 27 | assert.deepEqual(trail, [ 28 | path.resolve(root, 'resources/pushd/a'), 29 | path.resolve(root, 'resources/pushd'), 30 | root 31 | ]); 32 | 33 | trail = shell.pushd('../b'); 34 | assert.equal(shell.error(), null); 35 | assert.equal(process.cwd(), trail[0]); 36 | assert.deepEqual(trail, [ 37 | path.resolve(root, 'resources/pushd/b'), 38 | path.resolve(root, 'resources/pushd/a'), 39 | path.resolve(root, 'resources/pushd'), 40 | root 41 | ]); 42 | 43 | trail = shell.pushd('c'); 44 | assert.equal(shell.error(), null); 45 | assert.equal(process.cwd(), trail[0]); 46 | assert.deepEqual(trail, [ 47 | path.resolve(root, 'resources/pushd/b/c'), 48 | path.resolve(root, 'resources/pushd/b'), 49 | path.resolve(root, 'resources/pushd/a'), 50 | path.resolve(root, 'resources/pushd'), 51 | root 52 | ]); 53 | 54 | // Push stuff around with positive indices 55 | trail = shell.pushd('+0'); 56 | assert.equal(shell.error(), null); 57 | assert.equal(process.cwd(), trail[0]); 58 | assert.deepEqual(trail, [ 59 | path.resolve(root, 'resources/pushd/b/c'), 60 | path.resolve(root, 'resources/pushd/b'), 61 | path.resolve(root, 'resources/pushd/a'), 62 | path.resolve(root, 'resources/pushd'), 63 | root 64 | ]); 65 | 66 | trail = shell.pushd('+1'); 67 | assert.equal(shell.error(), null); 68 | assert.equal(process.cwd(), trail[0]); 69 | assert.deepEqual(trail, [ 70 | path.resolve(root, 'resources/pushd/b'), 71 | path.resolve(root, 'resources/pushd/a'), 72 | path.resolve(root, 'resources/pushd'), 73 | root, 74 | path.resolve(root, 'resources/pushd/b/c') 75 | ]); 76 | 77 | trail = shell.pushd('+2'); 78 | assert.equal(shell.error(), null); 79 | assert.equal(process.cwd(), trail[0]); 80 | assert.deepEqual(trail, [ 81 | path.resolve(root, 'resources/pushd'), 82 | root, 83 | path.resolve(root, 'resources/pushd/b/c'), 84 | path.resolve(root, 'resources/pushd/b'), 85 | path.resolve(root, 'resources/pushd/a') 86 | ]); 87 | 88 | trail = shell.pushd('+3'); 89 | assert.equal(shell.error(), null); 90 | assert.equal(process.cwd(), trail[0]); 91 | assert.deepEqual(trail, [ 92 | path.resolve(root, 'resources/pushd/b'), 93 | path.resolve(root, 'resources/pushd/a'), 94 | path.resolve(root, 'resources/pushd'), 95 | root, 96 | path.resolve(root, 'resources/pushd/b/c') 97 | ]); 98 | 99 | trail = shell.pushd('+4'); 100 | assert.equal(shell.error(), null); 101 | assert.equal(process.cwd(), trail[0]); 102 | assert.deepEqual(trail, [ 103 | path.resolve(root, 'resources/pushd/b/c'), 104 | path.resolve(root, 'resources/pushd/b'), 105 | path.resolve(root, 'resources/pushd/a'), 106 | path.resolve(root, 'resources/pushd'), 107 | root 108 | ]); 109 | 110 | // Push stuff around with negative indices 111 | trail = shell.pushd('-0'); 112 | assert.equal(shell.error(), null); 113 | assert.equal(process.cwd(), trail[0]); 114 | assert.deepEqual(trail, [ 115 | root, 116 | path.resolve(root, 'resources/pushd/b/c'), 117 | path.resolve(root, 'resources/pushd/b'), 118 | path.resolve(root, 'resources/pushd/a'), 119 | path.resolve(root, 'resources/pushd') 120 | ]); 121 | 122 | trail = shell.pushd('-1'); 123 | assert.equal(shell.error(), null); 124 | assert.equal(process.cwd(), trail[0]); 125 | assert.deepEqual(trail, [ 126 | path.resolve(root, 'resources/pushd/a'), 127 | path.resolve(root, 'resources/pushd'), 128 | root, 129 | path.resolve(root, 'resources/pushd/b/c'), 130 | path.resolve(root, 'resources/pushd/b') 131 | ]); 132 | 133 | trail = shell.pushd('-2'); 134 | assert.equal(shell.error(), null); 135 | assert.equal(process.cwd(), trail[0]); 136 | assert.deepEqual(trail, [ 137 | root, 138 | path.resolve(root, 'resources/pushd/b/c'), 139 | path.resolve(root, 'resources/pushd/b'), 140 | path.resolve(root, 'resources/pushd/a'), 141 | path.resolve(root, 'resources/pushd') 142 | ]); 143 | 144 | trail = shell.pushd('-3'); 145 | assert.equal(shell.error(), null); 146 | assert.equal(process.cwd(), trail[0]); 147 | assert.deepEqual(trail, [ 148 | path.resolve(root, 'resources/pushd/b/c'), 149 | path.resolve(root, 'resources/pushd/b'), 150 | path.resolve(root, 'resources/pushd/a'), 151 | path.resolve(root, 'resources/pushd'), 152 | root 153 | ]); 154 | 155 | trail = shell.pushd('-4'); 156 | assert.equal(shell.error(), null); 157 | assert.equal(process.cwd(), trail[0]); 158 | assert.deepEqual(trail, [ 159 | path.resolve(root, 'resources/pushd/b/c'), 160 | path.resolve(root, 'resources/pushd/b'), 161 | path.resolve(root, 'resources/pushd/a'), 162 | path.resolve(root, 'resources/pushd'), 163 | root 164 | ]); 165 | 166 | // Push without changing directory or resolving paths 167 | reset(); trail = shell.pushd('-n', 'resources/pushd'); 168 | assert.equal(shell.error(), null); 169 | assert.equal(process.cwd(), trail[0]); 170 | assert.deepEqual(trail, [ 171 | root, 172 | 'resources/pushd' 173 | ]); 174 | 175 | trail = shell.pushd('-n', 'resources/pushd/a'); 176 | assert.equal(shell.error(), null); 177 | assert.equal(process.cwd(), trail[0]); 178 | assert.deepEqual(trail, [ 179 | root, 180 | 'resources/pushd/a', 181 | 'resources/pushd' 182 | ]); 183 | 184 | // Push invalid directory 185 | shell.pushd('does/not/exist'); 186 | assert.equal(shell.error(), 'pushd: no such file or directory: ' + path.resolve('.', 'does/not/exist') + '\n'); 187 | assert.equal(process.cwd(), trail[0]); 188 | 189 | // Push without arguments should swap top two directories when stack length is 2 190 | reset(); trail = shell.pushd('resources/pushd'); 191 | assert.equal(shell.error(), null); 192 | assert.equal(trail.length, 2); 193 | assert.equal(path.relative(root, trail[0]), 'resources/pushd'); 194 | assert.equal(trail[1], root); 195 | assert.equal(process.cwd(), trail[0]); 196 | trail = shell.pushd(); 197 | assert.equal(shell.error(), null); 198 | assert.equal(trail.length, 2); 199 | assert.equal(trail[0], root); 200 | assert.equal(path.relative(root, trail[1]), 'resources/pushd'); 201 | assert.equal(process.cwd(), trail[0]); 202 | 203 | // Push without arguments should swap top two directories when stack length is > 2 204 | trail = shell.pushd('resources/pushd/a'); 205 | assert.equal(shell.error(), null); 206 | assert.equal(trail.length, 3); 207 | assert.equal(path.relative(root, trail[0]), 'resources/pushd/a'); 208 | assert.equal(trail[1], root); 209 | assert.equal(path.relative(root, trail[2]), 'resources/pushd'); 210 | assert.equal(process.cwd(), trail[0]); 211 | 212 | trail = shell.pushd(); 213 | assert.equal(shell.error(), null); 214 | assert.equal(trail.length, 3); 215 | assert.equal(trail[0], root); 216 | assert.equal(path.relative(root, trail[1]), 'resources/pushd/a'); 217 | assert.equal(path.relative(root, trail[2]), 'resources/pushd'); 218 | assert.equal(process.cwd(), trail[0]); 219 | 220 | // Push without arguments invalid when stack is empty 221 | reset(); shell.pushd(); 222 | assert.equal(shell.error(), 'pushd: no other directory\n'); 223 | 224 | shell.exit(123); -------------------------------------------------------------------------------- /test/pwd.js: -------------------------------------------------------------------------------- 1 | var shell = require('..'); 2 | 3 | var assert = require('assert'), 4 | path = require('path'); 5 | 6 | shell.config.silent = true; 7 | 8 | shell.rm('-rf', 'tmp'); 9 | shell.mkdir('tmp'); 10 | 11 | // 12 | // Valids 13 | // 14 | 15 | var _pwd = shell.pwd(); 16 | assert.equal(shell.error(), null); 17 | assert.equal(_pwd, path.resolve('.')); 18 | 19 | shell.cd('tmp'); 20 | var _pwd = shell.pwd(); 21 | assert.equal(shell.error(), null); 22 | assert.equal(path.basename(_pwd), 'tmp'); 23 | 24 | shell.exit(123); 25 | -------------------------------------------------------------------------------- /test/resources/a.txt: -------------------------------------------------------------------------------- 1 | This is line one 2 | This is line two 3 | 4 | This is line four 5 | . 6 | . 7 | More content here 8 | . 9 | . 10 | 11 | This is line eleven 12 | -------------------------------------------------------------------------------- /test/resources/badlink: -------------------------------------------------------------------------------- 1 | not_existed_file -------------------------------------------------------------------------------- /test/resources/chmod/a/b/c/.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/shelljs/a38ef4557e8658dec9409a034b31c70d8646e7de/test/resources/chmod/a/b/c/.gitignore -------------------------------------------------------------------------------- /test/resources/chmod/b/a/b/.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/shelljs/a38ef4557e8658dec9409a034b31c70d8646e7de/test/resources/chmod/b/a/b/.gitignore -------------------------------------------------------------------------------- /test/resources/chmod/c/a/b/.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/shelljs/a38ef4557e8658dec9409a034b31c70d8646e7de/test/resources/chmod/c/a/b/.gitignore -------------------------------------------------------------------------------- /test/resources/chmod/file1: -------------------------------------------------------------------------------- 1 | this is test file 1 2 | default state should be 0644 (rw-r--r--) 3 | -------------------------------------------------------------------------------- /test/resources/chmod/xdir/deep/file: -------------------------------------------------------------------------------- 1 | a file 2 | -------------------------------------------------------------------------------- /test/resources/chmod/xdir/file: -------------------------------------------------------------------------------- 1 | a file 2 | -------------------------------------------------------------------------------- /test/resources/cp-mode-bits/executable: -------------------------------------------------------------------------------- 1 | asdf -------------------------------------------------------------------------------- /test/resources/cp/a: -------------------------------------------------------------------------------- 1 | asdf 2 | -------------------------------------------------------------------------------- /test/resources/cp/b: -------------------------------------------------------------------------------- 1 | asdf 2 | -------------------------------------------------------------------------------- /test/resources/cp/dir_a/z: -------------------------------------------------------------------------------- 1 | asdf 2 | -------------------------------------------------------------------------------- /test/resources/cp/dir_b/dir_b_a/dir_b_a_a/z: -------------------------------------------------------------------------------- 1 | asdf 2 | -------------------------------------------------------------------------------- /test/resources/external/node_script.js: -------------------------------------------------------------------------------- 1 | console.log('node_script_1234'); 2 | 3 | -------------------------------------------------------------------------------- /test/resources/file1: -------------------------------------------------------------------------------- 1 | test1 -------------------------------------------------------------------------------- /test/resources/file1.js: -------------------------------------------------------------------------------- 1 | test 2 | -------------------------------------------------------------------------------- /test/resources/file1.txt: -------------------------------------------------------------------------------- 1 | test1 2 | -------------------------------------------------------------------------------- /test/resources/file2: -------------------------------------------------------------------------------- 1 | test2 -------------------------------------------------------------------------------- /test/resources/file2.js: -------------------------------------------------------------------------------- 1 | test 2 | -------------------------------------------------------------------------------- /test/resources/file2.txt: -------------------------------------------------------------------------------- 1 | test2 2 | -------------------------------------------------------------------------------- /test/resources/find/.hidden: -------------------------------------------------------------------------------- 1 | asdf 2 | -------------------------------------------------------------------------------- /test/resources/find/a: -------------------------------------------------------------------------------- 1 | asdf 2 | -------------------------------------------------------------------------------- /test/resources/find/b: -------------------------------------------------------------------------------- 1 | asdf 2 | -------------------------------------------------------------------------------- /test/resources/find/broken_link: -------------------------------------------------------------------------------- 1 | non_existent -------------------------------------------------------------------------------- /test/resources/find/dir1/a_dir1: -------------------------------------------------------------------------------- 1 | asdf 2 | -------------------------------------------------------------------------------- /test/resources/find/dir1/dir11/a_dir11: -------------------------------------------------------------------------------- 1 | asdf 2 | -------------------------------------------------------------------------------- /test/resources/find/dir2/a_dir1: -------------------------------------------------------------------------------- 1 | asdf 2 | -------------------------------------------------------------------------------- /test/resources/issue44/main.js: -------------------------------------------------------------------------------- 1 | 123 -------------------------------------------------------------------------------- /test/resources/link: -------------------------------------------------------------------------------- 1 | file1 -------------------------------------------------------------------------------- /test/resources/ls/.hidden_dir/nada: -------------------------------------------------------------------------------- 1 | asdf 2 | -------------------------------------------------------------------------------- /test/resources/ls/.hidden_file: -------------------------------------------------------------------------------- 1 | asdf 2 | -------------------------------------------------------------------------------- /test/resources/ls/a_dir/.hidden_dir/nada: -------------------------------------------------------------------------------- 1 | nada -------------------------------------------------------------------------------- /test/resources/ls/a_dir/b_dir/z: -------------------------------------------------------------------------------- 1 | asdf 2 | -------------------------------------------------------------------------------- /test/resources/ls/a_dir/nada: -------------------------------------------------------------------------------- 1 | asdf 2 | -------------------------------------------------------------------------------- /test/resources/ls/file1: -------------------------------------------------------------------------------- 1 | test 2 | -------------------------------------------------------------------------------- /test/resources/ls/file1.js: -------------------------------------------------------------------------------- 1 | test 2 | -------------------------------------------------------------------------------- /test/resources/ls/file2: -------------------------------------------------------------------------------- 1 | test 2 | -------------------------------------------------------------------------------- /test/resources/ls/file2.js: -------------------------------------------------------------------------------- 1 | test 2 | -------------------------------------------------------------------------------- /test/resources/ls/filename(with)[chars$]^that.must+be-escaped: -------------------------------------------------------------------------------- 1 | asdf 2 | -------------------------------------------------------------------------------- /test/resources/pushd/a/dummy: -------------------------------------------------------------------------------- 1 | meh -------------------------------------------------------------------------------- /test/resources/pushd/b/c/dummy: -------------------------------------------------------------------------------- 1 | meh -------------------------------------------------------------------------------- /test/resources/rm/a_dir/a_file: -------------------------------------------------------------------------------- 1 | asdf 2 | -------------------------------------------------------------------------------- /test/resources/rm/link_to_a_dir: -------------------------------------------------------------------------------- 1 | a_dir -------------------------------------------------------------------------------- /test/rm.js: -------------------------------------------------------------------------------- 1 | var shell = require('..'); 2 | 3 | var assert = require('assert'), 4 | path = require('path'), 5 | fs = require('fs'); 6 | 7 | shell.config.silent = true; 8 | 9 | shell.rm('-rf', 'tmp'); 10 | shell.mkdir('tmp'); 11 | 12 | // 13 | // Invalids 14 | // 15 | 16 | shell.rm(); 17 | assert.ok(shell.error()); 18 | 19 | shell.rm('asdfasdf'); // file does not exist 20 | assert.ok(shell.error()); 21 | 22 | shell.rm('-f'); // no file 23 | assert.ok(shell.error()); 24 | 25 | shell.rm('-@', 'resources/file1'); // invalid option 26 | assert.ok(shell.error()); 27 | assert.equal(fs.existsSync('resources/file1'), true); 28 | 29 | // 30 | // Valids 31 | // 32 | 33 | // file does not exist, but -f specified 34 | shell.rm('-f', 'asdfasdf'); 35 | assert.equal(shell.error(), null); 36 | 37 | // simple rm 38 | shell.cp('-f', 'resources/file1', 'tmp/file1'); 39 | assert.equal(fs.existsSync('tmp/file1'), true); 40 | shell.rm('tmp/file1'); 41 | assert.equal(shell.error(), null); 42 | assert.equal(fs.existsSync('tmp/file1'), false); 43 | 44 | // recursive dir removal - small-caps '-r' 45 | shell.mkdir('-p', 'tmp/a/b/c'); 46 | assert.equal(fs.existsSync('tmp/a/b/c'), true); 47 | shell.rm('-rf', 'tmp/a'); 48 | assert.equal(shell.error(), null); 49 | assert.equal(fs.existsSync('tmp/a'), false); 50 | 51 | // recursive dir removal - capital '-R' 52 | shell.mkdir('-p', 'tmp/a/b/c'); 53 | assert.equal(fs.existsSync('tmp/a/b/c'), true); 54 | shell.rm('-Rf', 'tmp/a'); 55 | assert.equal(shell.error(), null); 56 | assert.equal(fs.existsSync('tmp/a'), false); 57 | 58 | // recursive dir removal - absolute path 59 | shell.mkdir('-p', 'tmp/a/b/c'); 60 | assert.equal(fs.existsSync('tmp/a/b/c'), true); 61 | shell.rm('-Rf', path.resolve('./tmp/a')); 62 | assert.equal(shell.error(), null); 63 | assert.equal(fs.existsSync('tmp/a'), false); 64 | 65 | // wildcard 66 | shell.cp('-f', 'resources/file*', 'tmp'); 67 | assert.equal(shell.error(), null); 68 | assert.equal(fs.existsSync('tmp/file1'), true); 69 | assert.equal(fs.existsSync('tmp/file2'), true); 70 | assert.equal(fs.existsSync('tmp/file1.js'), true); 71 | assert.equal(fs.existsSync('tmp/file2.js'), true); 72 | shell.rm('tmp/file*'); 73 | assert.equal(shell.error(), null); 74 | assert.equal(fs.existsSync('tmp/file1'), false); 75 | assert.equal(fs.existsSync('tmp/file2'), false); 76 | assert.equal(fs.existsSync('tmp/file1.js'), false); 77 | assert.equal(fs.existsSync('tmp/file2.js'), false); 78 | 79 | // recursive dir removal 80 | shell.mkdir('-p', 'tmp/a/b/c'); 81 | shell.mkdir('-p', 'tmp/b'); 82 | shell.mkdir('-p', 'tmp/c'); 83 | shell.mkdir('-p', 'tmp/.hidden'); 84 | assert.equal(fs.existsSync('tmp/a/b/c'), true); 85 | assert.equal(fs.existsSync('tmp/b'), true); 86 | assert.equal(fs.existsSync('tmp/c'), true); 87 | assert.equal(fs.existsSync('tmp/.hidden'), true); 88 | shell.rm('-rf', 'tmp/*'); 89 | assert.equal(shell.error(), null); 90 | var contents = fs.readdirSync('tmp'); 91 | assert.equal(contents.length, 1); 92 | assert.equal(contents[0], '.hidden'); // shouldn't remove hiddden if no .* given 93 | 94 | // recursive dir removal 95 | shell.mkdir('-p', 'tmp/a/b/c'); 96 | shell.mkdir('-p', 'tmp/b'); 97 | shell.mkdir('-p', 'tmp/c'); 98 | shell.mkdir('-p', 'tmp/.hidden'); 99 | assert.equal(fs.existsSync('tmp/a/b/c'), true); 100 | assert.equal(fs.existsSync('tmp/b'), true); 101 | assert.equal(fs.existsSync('tmp/c'), true); 102 | assert.equal(fs.existsSync('tmp/.hidden'), true); 103 | shell.rm('-rf', 'tmp/*', 'tmp/.*'); 104 | assert.equal(shell.error(), null); 105 | var contents = fs.readdirSync('tmp'); 106 | assert.equal(contents.length, 0); 107 | 108 | // recursive dir removal - array-syntax 109 | shell.mkdir('-p', 'tmp/a/b/c'); 110 | shell.mkdir('-p', 'tmp/b'); 111 | shell.mkdir('-p', 'tmp/c'); 112 | shell.mkdir('-p', 'tmp/.hidden'); 113 | assert.equal(fs.existsSync('tmp/a/b/c'), true); 114 | assert.equal(fs.existsSync('tmp/b'), true); 115 | assert.equal(fs.existsSync('tmp/c'), true); 116 | assert.equal(fs.existsSync('tmp/.hidden'), true); 117 | shell.rm('-rf', ['tmp/*', 'tmp/.*']); 118 | assert.equal(shell.error(), null); 119 | var contents = fs.readdirSync('tmp'); 120 | assert.equal(contents.length, 0); 121 | 122 | // removal of a read-only file (unforced) 123 | shell.mkdir('-p', 'tmp/readonly'); 124 | 'asdf'.to('tmp/readonly/file1'); 125 | fs.chmodSync('tmp/readonly/file1', '0444'); // -r--r--r-- 126 | shell.rm('tmp/readonly/file1'); 127 | assert.equal(fs.existsSync('tmp/readonly/file1'), true); // bash's rm always asks before removing read-only files 128 | // here we just assume "no" 129 | 130 | // removal of a read-only file (forced) 131 | shell.mkdir('-p', 'tmp/readonly'); 132 | 'asdf'.to('tmp/readonly/file2'); 133 | fs.chmodSync('tmp/readonly/file2', '0444'); // -r--r--r-- 134 | shell.rm('-f', 'tmp/readonly/file2'); 135 | assert.equal(fs.existsSync('tmp/readonly/file2'), false); 136 | 137 | // removal of a tree containing read-only files (unforced) 138 | shell.mkdir('-p', 'tmp/tree2'); 139 | 'asdf'.to('tmp/tree2/file1'); 140 | 'asdf'.to('tmp/tree2/file2'); 141 | fs.chmodSync('tmp/tree2/file1', '0444'); // -r--r--r-- 142 | shell.rm('-r', 'tmp/tree2'); 143 | assert.equal(fs.existsSync('tmp/tree2/file1'), true); 144 | assert.equal(fs.existsSync('tmp/tree2/file2'), false); 145 | 146 | // removal of a tree containing read-only files (forced) 147 | shell.mkdir('-p', 'tmp/tree'); 148 | 'asdf'.to('tmp/tree/file1'); 149 | 'asdf'.to('tmp/tree/file2'); 150 | fs.chmodSync('tmp/tree/file1', '0444'); // -r--r--r-- 151 | shell.rm('-rf', 'tmp/tree'); 152 | assert.equal(fs.existsSync('tmp/tree'), false); 153 | 154 | // removal of a sub-tree containing read-only and hidden files - rm('dir/*') 155 | shell.mkdir('-p', 'tmp/tree3'); 156 | shell.mkdir('-p', 'tmp/tree3/subtree'); 157 | shell.mkdir('-p', 'tmp/tree3/.hidden'); 158 | 'asdf'.to('tmp/tree3/subtree/file'); 159 | 'asdf'.to('tmp/tree3/.hidden/file'); 160 | 'asdf'.to('tmp/tree3/file'); 161 | fs.chmodSync('tmp/tree3/file', '0444'); // -r--r--r-- 162 | fs.chmodSync('tmp/tree3/subtree/file', '0444'); // -r--r--r-- 163 | fs.chmodSync('tmp/tree3/.hidden/file', '0444'); // -r--r--r-- 164 | shell.rm('-rf', 'tmp/tree3/*', 'tmp/tree3/.*'); // erase dir contents 165 | assert.equal(shell.ls('tmp/tree3').length, 0); 166 | 167 | // removal of a sub-tree containing read-only and hidden files - rm('dir') 168 | shell.mkdir('-p', 'tmp/tree4'); 169 | shell.mkdir('-p', 'tmp/tree4/subtree'); 170 | shell.mkdir('-p', 'tmp/tree4/.hidden'); 171 | 'asdf'.to('tmp/tree4/subtree/file'); 172 | 'asdf'.to('tmp/tree4/.hidden/file'); 173 | 'asdf'.to('tmp/tree4/file'); 174 | fs.chmodSync('tmp/tree4/file', '0444'); // -r--r--r-- 175 | fs.chmodSync('tmp/tree4/subtree/file', '0444'); // -r--r--r-- 176 | fs.chmodSync('tmp/tree4/.hidden/file', '0444'); // -r--r--r-- 177 | shell.rm('-rf', 'tmp/tree4'); // erase dir contents 178 | assert.equal(fs.existsSync('tmp/tree4'), false); 179 | 180 | // remove symbolic link to a dir 181 | shell.rm('-rf', 'tmp'); 182 | shell.cp('-R', 'resources/rm', 'tmp'); 183 | shell.rm('-f', 'tmp/rm/link_to_a_dir'); 184 | assert.equal(shell.error(), null); 185 | assert.equal(fs.existsSync('tmp/rm/link_to_a_dir'), false); 186 | assert.equal(fs.existsSync('tmp/rm/a_dir'), true); 187 | 188 | shell.exit(123); 189 | -------------------------------------------------------------------------------- /test/sed.js: -------------------------------------------------------------------------------- 1 | var shell = require('..'); 2 | 3 | var assert = require('assert'), 4 | fs = require('fs'); 5 | 6 | shell.config.silent = true; 7 | 8 | shell.rm('-rf', 'tmp'); 9 | shell.mkdir('tmp'); 10 | 11 | // 12 | // Invalids 13 | // 14 | 15 | shell.sed(); 16 | assert.ok(shell.error()); 17 | 18 | shell.sed(/asdf/g); // too few args 19 | assert.ok(shell.error()); 20 | 21 | shell.sed(/asdf/g, 'nada'); // too few args 22 | assert.ok(shell.error()); 23 | 24 | assert.equal(fs.existsSync('/asdfasdf'), false); // sanity check 25 | shell.sed(/asdf/g, 'nada', '/asdfasdf'); // no such file 26 | assert.ok(shell.error()); 27 | 28 | // 29 | // Valids 30 | // 31 | 32 | shell.cp('-f', 'resources/file1', 'tmp/file1'); 33 | var result = shell.sed('test1', 'hello', 'tmp/file1'); // search string 34 | assert.equal(shell.error(), null); 35 | assert.equal(result, 'hello'); 36 | 37 | var result = shell.sed(/test1/, 'hello', 'tmp/file1'); // search regex 38 | assert.equal(shell.error(), null); 39 | assert.equal(result, 'hello'); 40 | 41 | var result = shell.sed(/test1/, 1234, 'tmp/file1'); // numeric replacement 42 | assert.equal(shell.error(), null); 43 | assert.equal(result, '1234'); 44 | 45 | var replaceFun = function (match) { 46 | return match.toUpperCase() + match; 47 | }; 48 | var result = shell.sed(/test1/, replaceFun, 'tmp/file1'); // replacement function 49 | assert.equal(shell.error(), null); 50 | assert.equal(result, 'TEST1test1'); 51 | 52 | var result = shell.sed('-i', /test1/, 'hello', 'tmp/file1'); 53 | assert.equal(shell.error(), null); 54 | assert.equal(result, 'hello'); 55 | assert.equal(shell.cat('tmp/file1'), 'hello'); 56 | 57 | shell.exit(123); 58 | -------------------------------------------------------------------------------- /test/tempdir.js: -------------------------------------------------------------------------------- 1 | var shell = require('..'); 2 | 3 | var assert = require('assert'), 4 | fs = require('fs'); 5 | 6 | shell.config.silent = true; 7 | 8 | shell.rm('-rf', 'tmp'); 9 | shell.mkdir('tmp'); 10 | 11 | // 12 | // Valids 13 | // 14 | 15 | var tmp = shell.tempdir(); 16 | assert.equal(shell.error(), null); 17 | assert.equal(fs.existsSync(tmp), true); 18 | 19 | shell.exit(123); 20 | -------------------------------------------------------------------------------- /test/test.js: -------------------------------------------------------------------------------- 1 | var shell = require('..'); 2 | 3 | var assert = require('assert'); 4 | 5 | shell.config.silent = true; 6 | 7 | shell.rm('-rf', 'tmp'); 8 | shell.mkdir('tmp'); 9 | 10 | // 11 | // Invalids 12 | // 13 | 14 | var result = shell.test(); // no expression given 15 | assert.ok(shell.error()); 16 | 17 | var result = shell.test('asdf'); // bad expression 18 | assert.ok(shell.error()); 19 | 20 | var result = shell.test('f', 'resources/file1'); // bad expression 21 | assert.ok(shell.error()); 22 | 23 | var result = shell.test('-f'); // no file 24 | assert.ok(shell.error()); 25 | 26 | // 27 | // Valids 28 | // 29 | 30 | //exists 31 | var result = shell.test('-e', 'resources/file1'); 32 | assert.equal(shell.error(), null); 33 | assert.equal(result, true);//true 34 | 35 | var result = shell.test('-e', 'resources/404'); 36 | assert.equal(shell.error(), null); 37 | assert.equal(result, false); 38 | 39 | //directory 40 | var result = shell.test('-d', 'resources'); 41 | assert.equal(shell.error(), null); 42 | assert.equal(result, true);//true 43 | 44 | var result = shell.test('-f', 'resources'); 45 | assert.equal(shell.error(), null); 46 | assert.equal(result, false); 47 | 48 | var result = shell.test('-L', 'resources'); 49 | assert.equal(shell.error(), null); 50 | assert.equal(result, false); 51 | 52 | //file 53 | var result = shell.test('-d', 'resources/file1'); 54 | assert.equal(shell.error(), null); 55 | assert.equal(result, false); 56 | 57 | var result = shell.test('-f', 'resources/file1'); 58 | assert.equal(shell.error(), null); 59 | assert.equal(result, true);//true 60 | 61 | var result = shell.test('-L', 'resources/file1'); 62 | assert.equal(shell.error(), null); 63 | assert.equal(result, false); 64 | 65 | //link 66 | var result = shell.test('-d', 'resources/link'); 67 | assert.equal(shell.error(), null); 68 | assert.equal(result, false); 69 | 70 | var result = shell.test('-f', 'resources/link'); 71 | assert.equal(shell.error(), null); 72 | assert.equal(result, true);//true 73 | 74 | var result = shell.test('-L', 'resources/link'); 75 | assert.equal(shell.error(), null); 76 | assert.equal(result, true);//true 77 | 78 | var result = shell.test('-L', 'resources/badlink'); 79 | assert.equal(shell.error(), null); 80 | assert.equal(result, true);//true 81 | 82 | var result = shell.test('-L', 'resources/404'); 83 | assert.equal(shell.error(), null); 84 | assert.equal(result, false);//false 85 | 86 | shell.exit(123); 87 | -------------------------------------------------------------------------------- /test/to.js: -------------------------------------------------------------------------------- 1 | var shell = require('..'); 2 | 3 | var assert = require('assert'), 4 | fs = require('fs'); 5 | 6 | shell.config.silent = true; 7 | 8 | shell.rm('-rf', 'tmp'); 9 | shell.mkdir('tmp'); 10 | 11 | // 12 | // Invalids 13 | // 14 | 15 | 'hello world'.to(); 16 | assert.ok(shell.error()); 17 | 18 | assert.equal(fs.existsSync('/asdfasdf'), false); // sanity check 19 | 'hello world'.to('/asdfasdf/file'); 20 | assert.ok(shell.error()); 21 | 22 | // 23 | // Valids 24 | // 25 | 26 | 'hello world'.to('tmp/to1'); 27 | var result = shell.cat('tmp/to1'); 28 | assert.equal(shell.error(), null); 29 | assert.equal(result, 'hello world'); 30 | 31 | shell.exit(123); 32 | -------------------------------------------------------------------------------- /test/toEnd.js: -------------------------------------------------------------------------------- 1 | var shell = require('..'); 2 | 3 | var assert = require('assert'), 4 | fs = require('fs'); 5 | 6 | shell.config.silent = true; 7 | 8 | shell.rm('-rf', 'tmp'); 9 | shell.mkdir('tmp'); 10 | 11 | // 12 | // Invalids 13 | // 14 | 15 | 'hello world'.toEnd(); 16 | assert.ok(shell.error()); 17 | 18 | assert.equal(fs.existsSync('/asdfasdf'), false); // sanity check 19 | assert.ok(shell.error()); 20 | // 21 | // Valids 22 | // 23 | 24 | assert.equal(fs.existsSync('tmp/toEnd1'), false); //Check file toEnd() creates does not already exist 25 | 'hello '.toEnd('tmp/toEnd1'); 26 | assert.equal(fs.existsSync('tmp/toEnd1'), true); //Check that file was created 27 | 'world'.toEnd('tmp/toEnd1'); //Write some more to the file 28 | var result = shell.cat('tmp/toEnd1'); 29 | assert.equal(shell.error(), null); 30 | assert.equal(result, 'hello world'); //Check that the result is what we expect 31 | 32 | shell.exit(123); 33 | -------------------------------------------------------------------------------- /test/which.js: -------------------------------------------------------------------------------- 1 | var shell = require('..'); 2 | 3 | var assert = require('assert'), 4 | fs = require('fs'); 5 | 6 | shell.config.silent = true; 7 | 8 | shell.rm('-rf', 'tmp'); 9 | shell.mkdir('tmp'); 10 | 11 | // 12 | // Invalids 13 | // 14 | 15 | shell.which(); 16 | assert.ok(shell.error()); 17 | 18 | var result = shell.which('asdfasdfasdfasdfasdf'); // what are the odds... 19 | assert.equal(shell.error(), null); 20 | assert.equal(result, null); 21 | 22 | // 23 | // Valids 24 | // 25 | 26 | var result = shell.which('node'); 27 | assert.equal(shell.error(), null); 28 | assert.equal(fs.existsSync(result), true); 29 | 30 | shell.exit(123); 31 | --------------------------------------------------------------------------------