diff --git a/core/lib/Drupal/Core/Console/Command/HashPasswordCommand.php b/core/lib/Drupal/Core/Console/Command/HashPasswordCommand.php
new file mode 100644
index 0000000000..1ec03a7fe8
--- /dev/null
+++ b/core/lib/Drupal/Core/Console/Command/HashPasswordCommand.php
@@ -0,0 +1,50 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Console\Command\HashPasswordCommand.
+ */
+
+namespace Drupal\Core\Console\Command;
+
+use Drupal\Core\Password\PasswordInterface;
+use Symfony\Component\Console\Command\Command;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Output\OutputInterface;
+
+class HashPasswordCommand extends Command {
+
+  /**
+   * The password hashing service object.
+   *
+   * @var \Drupal\Core\Password\PasswordInterface
+   */
+  protected $password;
+
+  /**
+   * Generates a HashPasswordCommand object.
+   *
+   * @param \Drupal\Core\Password\PasswordInterface $password
+   *   The password hashing service object.
+   */
+  public function __construct(PasswordInterface $password) {
+    parent::__construct();
+    $this->password = $password;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function configure() {
+    $this->setName('hash-password')
+      ->setDescription('Hash a password');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function execute(InputInterface $input, OutputInterface $output) {
+    $output->writeln($this->password->hash($input->getFirstArgument()));
+  }
+
+}
diff --git a/core/scripts/dump-database-d8-mysql.php b/core/scripts/bootstrap.php
similarity index 61%
copy from core/scripts/dump-database-d8-mysql.php
copy to core/scripts/bootstrap.php
index 73f7b94fad..dd7b1511ab 100644
--- a/core/scripts/dump-database-d8-mysql.php
+++ b/core/scripts/bootstrap.php
@@ -1,27 +1,12 @@
-#!/usr/bin/env php
 <?php
 
-/**
- * @file
- * A command line application to dump a database to a generation script.
- */
-
-use Drupal\Core\Command\DbDumpApplication;
 use Drupal\Core\DrupalKernel;
 use Drupal\Core\Site\Settings;
 use Symfony\Component\HttpFoundation\Request;
 
-if (PHP_SAPI !== 'cli') {
-  return;
-}
-
 // Bootstrap.
 $autoloader = require __DIR__ . '/../../autoload.php';
 require_once __DIR__ . '/../includes/bootstrap.inc';
 $request = Request::createFromGlobals();
 Settings::initialize(dirname(dirname(__DIR__)), DrupalKernel::findSitePath($request), $autoloader);
 $kernel = DrupalKernel::createFromRequest($request, $autoloader, 'prod')->boot();
-
-// Run the database dump command.
-$application = new DbDumpApplication();
-$application->run();
diff --git a/core/scripts/dump-database-d8-mysql.php b/core/scripts/dump-database-d8-mysql.php
index 73f7b94fad..c03aa4dfe2 100644
--- a/core/scripts/dump-database-d8-mysql.php
+++ b/core/scripts/dump-database-d8-mysql.php
@@ -10,18 +10,13 @@
 use Drupal\Core\DrupalKernel;
 use Drupal\Core\Site\Settings;
 use Symfony\Component\HttpFoundation\Request;
+use Drupal\Core\Database\Database;
 
 if (PHP_SAPI !== 'cli') {
   return;
 }
 
-// Bootstrap.
-$autoloader = require __DIR__ . '/../../autoload.php';
-require_once __DIR__ . '/../includes/bootstrap.inc';
-$request = Request::createFromGlobals();
-Settings::initialize(dirname(dirname(__DIR__)), DrupalKernel::findSitePath($request), $autoloader);
-$kernel = DrupalKernel::createFromRequest($request, $autoloader, 'prod')->boot();
-
+include_once __DIR__ . '/bootstrap.php';
 // Run the database dump command.
 $application = new DbDumpApplication();
 $application->run();
diff --git a/core/scripts/password-hash.php b/core/scripts/password-hash.php
new file mode 100644
index 0000000000..1c077a3afe
--- /dev/null
+++ b/core/scripts/password-hash.php
@@ -0,0 +1,19 @@
+#!/usr/bin/env php
+<?php
+
+use Drupal\Core\Console\Command\HashPasswordCommand;
+use Symfony\Component\Console\Application;
+
+if (PHP_SAPI !== 'cli') {
+  return;
+}
+
+include_once __DIR__ . '/bootstrap.php';
+
+/** @var \Drupal\Core\Password\PasswordInterface $password */
+$password = $kernel->getContainer()->get('password');
+$application = (new Application())
+  ->add(new HashPasswordCommand($password))
+    ->getApplication()
+  ->setDefaultCommand('hash-password', true)
+  ->run();
diff --git a/core/scripts/password-hash.sh b/core/scripts/password-hash.sh
deleted file mode 100755
index cf5e9e3824..0000000000
--- a/core/scripts/password-hash.sh
+++ /dev/null
@@ -1,70 +0,0 @@
-#!/usr/bin/env php
-<?php
-
-/**
- * Drupal hash script - to generate a hash from a plaintext password
- *
- * @param password1 [password2 [password3 ...]]
- *  Plain-text passwords in quotes (or with spaces backslash escaped).
- */
-
-use Drupal\Core\DrupalKernel;
-use Symfony\Component\HttpFoundation\Request;
-
-if (PHP_SAPI !== 'cli') {
-  return;
-}
-
-if (version_compare(PHP_VERSION, '5.4.5') < 0) {
-  $version  = PHP_VERSION;
-  echo <<<EOF
-
-ERROR: This script requires at least PHP version 5.4.5. You invoked it with
-       PHP version {$version}.
-\n
-EOF;
-  exit;
-}
-
-$script = basename(array_shift($_SERVER['argv']));
-
-if (in_array('--help', $_SERVER['argv']) || empty($_SERVER['argv'])) {
-  echo <<<EOF
-
-Generate Drupal password hashes from the shell.
-
-Usage:        {$script} [OPTIONS] "<plan-text password>"
-Example:      {$script} "mynewpassword"
-
-All arguments are long options.
-
-  --help      Print this page.
-
-  "<password1>" ["<password2>" ["<password3>" ...]]
-
-              One or more plan-text passwords enclosed by double quotes. The
-              output hash may be manually entered into the
-              {users_field_data}.pass field to change a password via SQL to a
-              known value.
-
-
-EOF;
-  exit;
-}
-
-// Password list to be processed.
-$passwords = $_SERVER['argv'];
-
-$autoloader = require __DIR__ . '/../../autoload.php';
-
-$request = Request::createFromGlobals();
-$kernel = DrupalKernel::createFromRequest($request, $autoloader, 'prod', FALSE);
-$kernel->boot();
-
-$password_hasher = $kernel->getContainer()->get('password');
-
-foreach ($passwords as $password) {
-  print("\npassword: $password \t\thash: ". $password_hasher->hash($password) ."\n");
-}
-print("\n");
-
diff --git a/core/tests/Drupal/Tests/Core/Console/Command/HashPasswordCommandTest.php b/core/tests/Drupal/Tests/Core/Console/Command/HashPasswordCommandTest.php
new file mode 100644
index 0000000000..e926aad457
--- /dev/null
+++ b/core/tests/Drupal/Tests/Core/Console/Command/HashPasswordCommandTest.php
@@ -0,0 +1,45 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Tests\Core\Console\Command\HashPasswordCommandTest.
+ */
+
+namespace Drupal\Tests\Core\Console\Command;
+
+use Drupal\Core\Console\Command\HashPasswordCommand;
+use Drupal\Core\Password\PasswordInterface;
+use Drupal\Tests\UnitTestCase;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Output\OutputInterface;
+
+/**
+ * @coversDefaultClass \Drupal\Core\Console\Command\HashPasswordCommand
+ * @group Console
+ */
+class HashPasswordCommandTest extends UnitTestCase {
+
+  /**
+   * @covers ::execute
+   */
+  public function testExecute() {
+    $password = $this->randomMachineName();
+    $hash = $this->randomMachineName();
+
+    $input = $this->prophesize(InputInterface::CLASS);
+    $input->getFirstArgument()->willReturn($password)->shouldBeCalled();
+
+    $output = $this->prophesize(OutputInterface::CLASS);
+    $output->writeln($hash)->shouldBeCalled();
+
+    $hasher = $this->prophesize(PasswordInterface::CLASS);
+    $hasher->hash($password)->willReturn($hash)->shouldBeCalled();
+
+    $command = new HashPasswordCommand($hasher->reveal());
+
+    $method = new \ReflectionMethod($command, 'execute');
+    $method->setAccessible(TRUE);
+    $method->invoke($command, $input->reveal(), $output->reveal());
+  }
+
+}
