491

I want to execute mongo commands in shell script, e.g. in a script test.sh:

#!/bin/sh
mongo myDbName
db.mycollection.findOne()
show collections

When I execute this script via ./test.sh, then the connection to MongoDB is established, but the following commands are not executed.

How to execute other commands through shell script test.sh?

B--rian
  • 5,578
  • 10
  • 38
  • 89
StackOverFlow
  • 4,486
  • 12
  • 52
  • 87

22 Answers22

564

You can also evaluate a command using the --eval flag, if it is just a single command.

mongo --eval "printjson(db.serverStatus())"

Please note: if you are using Mongo operators, starting with a $ sign, you'll want to surround the eval argument in single quotes to keep the shell from evaluating the operator as an environment variable:

mongo --eval 'db.mycollection.update({"name":"foo"},{$set:{"this":"that"}});' myDbName

Otherwise you may see something like this:

mongo --eval "db.test.update({\"name\":\"foo\"},{$set:{\"this\":\"that\"}});"
> E QUERY    SyntaxError: Unexpected token :
Sahil Gulati
  • 15,028
  • 4
  • 24
  • 42
theTuxRacer
  • 13,709
  • 7
  • 42
  • 59
  • 41
    For `.find()` operations, you need to call an operation on the result object to print the documents, such as `toArray()` or `shellPrint()`. e.g., `mongo userdb --eval "printjson(db.users.find().toArray())"` – Gingi Jun 19 '14 at 15:05
  • 4
    I had to specify the connection string like mongo :/db --eval "printjson(db.serverStatus())" or mongo :/db < mongoCommands.js to prevent it from always connecting to "test" – dev Sep 24 '14 at 18:46
  • 10
    Thanks @Gingi - my preferred method is `mongo mydb --eval "db.users.find({a:'b'}).pretty().shellPrint()"` ... simples :) – Matt Fletcher Dec 16 '14 at 12:46
  • 4
    i wish i would get ALL of the results, instead of seeing it primt out "type 'it' for more" – Randy L Jun 29 '15 at 20:24
  • 2
    While this works, the problem is that not only the result of the query is sent but also noise like connection status messages: MongoDB shell version: 2.6.0 connecting to: myhost DBQuery: mydb.mycollection -> { } MongoDB shell version: 2.6.0 connecting to: myhost `query result` more noise – Kev Jan 12 '16 at 10:20
  • 3
    How do I define my servername and dbname? – colintobing Feb 20 '16 at 03:32
  • 11
    @Amida you can do `mongo --eval "db.version()" --quiet` for example to avoid printing all the noise you say – Fermin Silva Dec 29 '16 at 08:58
  • if you want valid JSON output, use JSON.stringify() instead of printjson() – stackoverflowsucks Feb 20 '17 at 02:00
  • `mongo --eval "db.test.update({\"name\":\"foo\"},{$set:{\"this\":\"that\"}});" > E QUERY SyntaxError: Unexpected token :` If you see this error, please do not try to solve this with surround the text $set in single quotes or double quotes, that cause your data lost because script will understand `$set` as new property and without $set, your document is overridden by new document with some fields like this `"": this: "that"`. – Trung Le Nguyen Nhat Apr 10 '17 at 09:39
  • double quote version works if you escape any $ operators like this: `mongo --eval "db.${Collection}.updateMany({},{ \$set: {${Field}:${Value}}});"` Allows to include shell tokens inline as well – user323774 Feb 01 '18 at 18:40
  • 2
    @colintobing `mongo dbname --eval 'db.test.findOne()'` – JMess May 18 '18 at 21:47
  • will this exit mongo shell gracefully ? – Shu Jul 30 '20 at 06:19
374

Put your mongo script into a .js file.

Then execute mongo < yourFile.js

Ex:

demo.js //file has your script

use sample  //db name
show collections

keep this file in "c:\db-scripts"

Then in cmd prompt go to "c:\db-scripts"

C:\db-scripts>mongo < demo.js

This will execute the code in mongo and shows the output

C:\db-scripts>mongo < demo.js
Mongo shell version: 3.0.4
Connecting to: test
switched to db sample
users   //collection name
tasks   //collection name
bye
C:\db-scripts>
Prasanth Jaya
  • 4,407
  • 2
  • 23
  • 33
Matt
  • 43,482
  • 6
  • 101
  • 102
  • 16
    Quite literally.. you take the exact same commands you would input to the mongo shell, save those commands to a `.js` file, and pass it as a parameter to the `mongo` command. – Matt Jan 29 '11 at 16:03
  • 8
    It's worth noting anything you set in an --eval statement (if supplied) to the mongo interpreter will remain in scope when your script (if supplied) is executed. You can use this make your scripts more reusable, eg. mongo --eval "somevar='someval';" db_name script_that_uses_somevar.js – Andrew J Oct 26 '11 at 04:26
  • 12
    @Matt -- Note that the non-JavaScript commands that the shell provides are not available in an executed JavaScript file, so `use dbName` and `show dbs` will work from the shell's internal prompt but not from inside a `.js` file. There are JavaScript equivalents for the non-JavaScript commands, so this isn't a limitation, just something you need to be aware of. – Tad Marshall Nov 28 '11 at 11:13
  • 6
    If you need to specify a name of a database, user name, password, you can do `mongo dbName -u userName -p "password with spaces" scriptToRun.js` – KevinL Apr 20 '15 at 16:11
  • 2
    This doesn't work if you want a dynamic behavior, i.e. pass arguments: `mongo < demo.js arg1 arg2 arg3` - how would you read them in the `js` file? – cprn Jul 26 '16 at 12:41
  • 1
    is there a way to keep the mongo prompt open? AKA I don't want mongo to say "bye" to me. – Alexander Mills Feb 24 '17 at 23:30
  • 2
    use `mongo --quiet ` for not print bye and version. – arulraj.net Mar 29 '19 at 05:23
  • i had a bunch of remove commands one per line db.c3.remove({"name" : "Suriname"}) db.c3.remove({"name" : "Greenland"}) worked after i put a semi colon at end of every line. on ubuntu with \n as new line char (i created the file from Java code) – tgkprog Sep 24 '19 at 09:49
  • Is it required to use Javascript? I'm making a mongo script to delete and then reload data into a collection. But my we don't use .js much, we use Golang, and some bash scripts. – ineedtoknow Apr 22 '20 at 22:52
  • @ineedtoknow yes. see https://docs.mongodb.com/manual/reference/program/mongo/#mongo-shell-file - you could still write a Go program to do this, it's just not related to mongo CLI – Matt Apr 23 '20 at 05:23
  • How do you do this in powershell? I get a `The '<' operator is reserved for future use.` error – user2619824 Jul 04 '20 at 21:08
  • `& cmd.exe /c "mongo < demo.js"` is the answer – user2619824 Jul 04 '20 at 21:11
  • 1
    I prefer entering the mongo prompt open as @AlexanderMills asks, by entering the mongo environment and running in there `load("filename.js")` which runs the script normally, ending with `true` and keeps the prompt open. – Zap Dec 10 '20 at 09:52
114

This works for me under Linux:

mongo < script.js

For newer version of mongodb

mongosh < script.js
indianMonk
  • 17
  • 6
Antonin Brettsnajdr
  • 4,073
  • 2
  • 20
  • 14
70

Put this in a file called test.js:

db.mycollection.findOne()
db.getCollectionNames().forEach(function(collection) {
  print(collection);
});

then run it with mongo myDbName test.js.

Theo
  • 131,503
  • 21
  • 160
  • 205
46

There is an official documentation page about this as well.

Examples from that page include:

mongo server:27017/dbname --quiet my_commands.js
mongo test --eval "printjson(db.getCollectionNames())"
Apurv
  • 3,723
  • 3
  • 30
  • 51
thaddeusmt
  • 15,410
  • 9
  • 67
  • 67
41

The shell script below also worked nicely for me... definite had to use the redirect that Antonin mentioned at first... that gave me the idea to test the here document.

function testMongoScript {
    mongo <<EOF
    use mydb
    db.leads.findOne()
    db.leads.find().count()
EOF
}
David H. Young
  • 519
  • 4
  • 4
27

In case you have authentication enabled:

mongo -u username -p password --authenticationDatabase auth_db_name < your_script.js

For newer version

mongosh -u username -p password --authenticationDatabase auth_db_name < your_script.js
indianMonk
  • 17
  • 6
Moses Xu
  • 2,140
  • 4
  • 24
  • 35
24

I use the "heredoc" syntax, which David Young mentions. But there is a catch:

#!/usr/bin/sh

mongo <db> <<EOF
db.<collection>.find({
  fieldName: { $exists: true }
})
.forEach( printjson );
EOF

The above will NOT work, because the phrase "$exists" will be seen by the shell and substituted with the value of the environment variable named "exists." Which, likely, doesn't exist, so after shell expansion, it becomes:

#!/usr/bin/sh

mongo <db> <<EOF
db.<collection>.find({
  fieldName: { : true }
})
.forEach( printjson );
EOF

In order to have it pass through you have two options. One is ugly, one is quite nice. First, the ugly one: escape the $ signs:

#!/usr/bin/sh

mongo <db> <<EOF
db.<collection>.find({
  fieldName: { \$exists: true }
})
.forEach( printjson );
EOF

I do NOT recommend this, because it is easy to forget to escape.

The other option is to escape the EOF, like this:

#!/usr/bin/sh

mongo <db> <<\EOF
db.<collection>.find({
  fieldName: { $exists: true }
})
.forEach( printjson );
EOF

Now, you can put all the dollar signs you want in your heredoc, and the dollar signs are ignored. The down side: That doesn't work if you need to put shell parameters/variables in your mongo script.

Another option you can play with is to mess with your shebang. For example,

#!/bin/env mongo
<some mongo stuff>

There are several problems with this solution:

  1. It only works if you are trying to make a mongo shell script executable from the command line. You can't mix regular shell commands with mongo shell commands. And all you save by doing so is not having to type "mongo" on the command line... (reason enough, of course)

  2. It functions exactly like "mongo <some-js-file>" which means it does not let you use the "use <db>" command.

I have tried adding the database name to the shebang, which you would think would work. Unfortunately, the way the system processes the shebang line, everything after the first space is passed as a single parameter (as if quoted) to the env command, and env fails to find and run it.

Instead, you have to embed the database change within the script itself, like so:

#!/bin/env mongo
db = db.getSiblingDB('<db>');
<your script>

As with anything in life, "there is more than one way to do it!"

John Arrowwood
  • 2,370
  • 2
  • 21
  • 32
  • 1
    If you quote your HEREDOC delimiter the shell will not interpret nor substitute anything, eg instead of blah blah << HEREDOC_EOF stuff EOF Use blah blah << "EOF" stuf EOF – Ram Sep 18 '20 at 02:44
  • this is a very good answer !! how can i pass shell variable to the query ? – Youssef Boudaya May 25 '21 at 15:29
  • The answer is to not escape your HEREDOC. Then, you will be able to substitute within it. Catch is, you will have to escape any literal dollar signs. – John Arrowwood Jun 16 '21 at 18:12
23

In my setup I have to use:

mongo --host="the.server.ip:port" databaseName theScript.js 

For newer version of mongodb

mongosh --host="the.server.ip:port" databaseName theScript.js 

indianMonk
  • 17
  • 6
Edz
  • 558
  • 4
  • 8
17

How about this:

echo "db.mycollection.findOne()" | mongo myDbName
echo "show collections" | mongo myDbName
Mark Butler
  • 4,361
  • 2
  • 39
  • 39
17

Create a script file; write commands:

#!/bin/sh
mongo < file.js

For newer versions mongosh < file.js

In file.js write your mongo query:

db.collection.find({"myValue":null}).count();
indianMonk
  • 17
  • 6
GSK
  • 553
  • 5
  • 11
17

In my case, I can conveniently use \n as separator for the next mongo command I want to execute then pipe them to mongo

echo $'use your_db\ndb.yourCollection.find()' | mongo

Newer version of mongodb

echo $'use your_db\ndb.yourCollection.find()' | mongosh
indianMonk
  • 17
  • 6
Ardhi
  • 2,855
  • 1
  • 22
  • 31
16

Thank you printf! In a Linux environment, here's a better way to have only one file run the show. Say you have two files, mongoCmds.js with multiple commands:

use someDb
db.someColl.find()

and then the driver shell file, runMongoCmds.sh

mongo < mongoCmds.js

Newer version of mongodb

mongosh < mongoCmds.js

Instead, have just one file, runMongoCmds.sh containing

printf "use someDb\ndb.someColl.find()" | mongo

Bash's printf is much more robust than echo and allows for the \n between commands to force them on multiple lines.

indianMonk
  • 17
  • 6
tgoneil
  • 1,522
  • 3
  • 19
  • 30
15

As suggested by theTuxRacer, you can use the eval command, for those who are missing it like I was, you can also add in your db name if you are not trying to preform operation on the default db.

mongo <dbname> --eval "printjson(db.something.find())"

Newer version of mongodb

mongosh <dbname> --eval "printjson(db.something.find())"
indianMonk
  • 17
  • 6
Matt Clark
  • 27,671
  • 19
  • 68
  • 123
10
mongo <<EOF
use <db_name>
db.getCollection("<collection_name>").find({})
EOF
Erdem ÖZDEMİR
  • 835
  • 9
  • 7
4

--shell flag can also be used for javascript files

 mongo --shell /path/to/jsfile/test.js 
Jackson Harry
  • 308
  • 1
  • 2
  • 15
  • I think this just leaves the shell open after js executed? `mongo /path/to/jsfile/test.js` will exectute the js too. – UpTheCreek Jan 18 '19 at 11:43
4
mongo db_name --eval "db.user_info.find().forEach(function(o) {print(o._id);})"
Talespin_Kit
  • 20,830
  • 29
  • 89
  • 135
4

Recently migrated from mongodb to Postgres. This is how I used the scripts.

mongo < scripts.js > inserts.sql

Read the scripts.js and output redirect to inserts.sql.

scripts.js looks like this

use myDb;
var string = "INSERT INTO table(a, b) VALUES";
db.getCollection('collectionName').find({}).forEach(function (object) {
    string += "('" + String(object.description) + "','" + object.name + "'),";
});
print(string.substring(0, string.length - 1), ";");

inserts.sql looks like this

INSERT INTO table(a, b) VALUES('abc', 'Alice'), ('def', 'Bob'), ('ghi', 'Claire');
mythicalcoder
  • 3,143
  • 1
  • 32
  • 42
  • 2
    Very interesting, but off topic with regard to the original question. – jlyonsmith Apr 16 '18 at 22:18
  • 1
    `How to execute mongo commands through shell scripts?` This is not off-topic. In fact, I reached this question due to the title. So people like me benefit reading such an answer. Also, I am giving a very useful method in the example rather than a toy example. – mythicalcoder Apr 19 '18 at 15:57
3

If you want to handle it with one line it's an easy way.

file.sh --> db.EXPECTED_COLLECTION.remove("_id":1234)

cat file.sh | mongo <EXPECTED_COLLECTION>
Erçin Akçay
  • 1,383
  • 4
  • 18
  • 34
3

Single shell script solution with ability to pass mongo arguments (--quiet, dbname, etc):

#!/usr/bin/env -S mongo --quiet localhost:27017/test

cur = db.myCollection.find({});
while(cur.hasNext()) {
  printjson(cur.next());
}

The -S flag might not work on all platforms.

yǝsʞǝla
  • 16,272
  • 2
  • 44
  • 65
  • 1
    It seems odd to me that this is not more widely appreciated... You could also just shebang the full path to a specific mongo, right? – Paul Hodges Jun 15 '22 at 19:46
1

When using a replicaset, writes must be done on the PRIMARY, so I usually use syntax like this which avoids having to figure out which host is the master:

mongo -host myReplicaset/anyKnownReplica

RSX
  • 376
  • 4
  • 14
0

The legacy mongo shell has been removed from MongoDB with the version 6 release in 2022

There is a new way to execute shell scripts using the new shell, mongosh

From the shell documentation on writing scripts:

  • You can use mongosh to execute a script from the command line without entering the mongosh console
  • To specify the filename, use the --file or -f parameter to specify the filename
  • You may also need to specify connection information
boardtc
  • 1,304
  • 1
  • 15
  • 20