diff --git src/wp-includes/formatting.php src/wp-includes/formatting.php
index 44376e5..b1f997f 100644
--- src/wp-includes/formatting.php
+++ src/wp-includes/formatting.php
@@ -486,7 +486,16 @@ function shortcode_unautop( $pee ) {
 		return $pee;
 	}
 
-	$tagregexp = join( '|', array_map( 'preg_quote', array_keys( $shortcode_tags ) ) );
+	/**
+	 * Filter the list of shortcodes to run through the 'unautop' routine.
+	 *
+	 * @since 4.1.0
+	 *
+	 * @param array $shortcode_tags Shortcode tags to run through the 'unautop' routine.
+	 */
+	$unautop_shortcode_tags = apply_filters( 'unautop_shortcodes', array_keys( $shortcode_tags ) );
+
+	$tagregexp = join( '|', array_map( 'preg_quote', $unautop_shortcode_tags ) );
 	$spaces = wp_spaces_regexp();
 
 	$pattern =
diff --git tests/phpunit/tests/shortcode.php tests/phpunit/tests/shortcode.php
index 7e8b971..8ead9f2 100644
--- tests/phpunit/tests/shortcode.php
+++ tests/phpunit/tests/shortcode.php
@@ -407,4 +407,19 @@ EOF;
 		$this->assertTrue( has_shortcode( $content_nested, 'gallery' ) );
 		remove_shortcode( 'foo' );
 	}
+
+	/**
+	 * @ticket 12061
+	 */
+	function test_unautop_shortcodes_filter() {
+		// trim() is necessary because the shortcode_unautop(wpautop()) combo causes whitespace that we don't care about
+		$this->assertEquals( '_shortcode_hyphen', trim( do_shortcode( shortcode_unautop( wpautop( '[hyphen]' ) ) ) ) );
+		// Filter 'hyphen' out of the shortcodes that are unautop'd and test again
+		add_filter( 'unautop_shortcodes', array( $this, '_shortcode_unautop_filter' ) );
+		$this->assertEquals( '<p>_shortcode_hyphen</p>', trim( do_shortcode( shortcode_unautop( wpautop( '[hyphen]' ) ) ) ) );
+	}
+
+	function _shortcode_unautop_filter( $shortcodes ) {
+		return array_diff( $shortcodes, array( 'hyphen' ) );
+	}
 }
