This question has been around for over 10 years, and WordPress core still doesn't offer a way to add or remove terms in bulk.
The best solution depends on the size of your term list:
For short lists that fit on a few table pages, follow the accepted answer by gmazzap and dynamically add the remove option to your bulk actions: https://github.com/gmazzap/GMMediaTags
For updating thousands of terms at once, the challenge isn't just adding the "remove" option but also processing a large number of terms without:
Manually going through hundreds of pages.
Overloading your server with large queries.
To address these issues for very large tables, I've developed a plugin that I'm considering adding to the free WordPress marketplace. For now, I'm focusing on supporting and improving it with premium users.
This plugin takes a different approach by moving the workflow outside the table. Tasks are created individually for a specific post type, taxonomy, or user. Items are pre-selected using filters, and you can add or remove terms according to the predefined action.
Once the task is scheduled for each selected item, it is processed either via JavaScript in the frontend or as a CRON job in the background.
If you're interested, check it out: https://code.recuweb.com/get/bulk-task-editor/
Using this method, you can also add custom functions to perform even more complex tasks.
Use the following template to implement a custom post type task.
Register the task:
add_action( 'rewbe_post_type_actions', function($actions,$post_type){
if( $post_type == 'your-custom-post-type' ){
$actions[] = array(
'label' => 'Name of your task',
'id' => 'bulk_task_name',
'fields' => array(
array(
'name' => 'var_1',
'type' => 'select',
'options' => array(
'value_1' => 'Option 1',
'value_2' => 'Option 2',
'value_3' => 'Option 3',
),
),
array(
'name' => 'var_2',
'type' => 'text',
),
),
);
}
return $actions;
},10,2);
Add the callback function of your task:
add_action('rewbe_do_post_{bulk_task_name}',function($post,$args){
if( !empty($args['var_1']) && !empty($args['var_2']) ){
$var_1 = sanitize_title($args['var_1']);
$var_2 = sanitize_text_field($args['var_2']);
// your logic here
}
return $post;
},10,2);