diff --git a/ChangeLog.md b/ChangeLog.md
--- a/ChangeLog.md
+++ b/ChangeLog.md
@@ -1,5 +1,11 @@
 # dnf-repo releases
 
+## 0.2 (2022-08-12)
+- support multiple repos per repo file (eg needed for eln)
+- support multiple repo actions (eg "-d rawhide -e fedora")
+- --exact repo match option (eg for 'fedora')
+- --debug now shows what sudo does, like --dryrun
+
 ## 0.1 (2022-06-20)
 - initial version with basic functionality: --add-copr, --add-koji,
   --disable repo, --enable repo, --enable/disable-{testing,modular}
diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -16,14 +16,14 @@
 
 ```shellsession
 $ dnf-repo --version
-0.1
+0.2
 $ dnf-repo --help
 DNF wrapper repo tool
 
-Usage: dnf-repo [--version] [-n|--dryrun] [-D|--debug] [-s|--save]
+Usage: dnf-repo [--version] [-n|--dryrun] [-D|--debug] [--exact] [-s|--save]
                 [(-c|--add-copr COPR) | (-k|--add-koji REPO) |
                   (-d|--disable REPOPAT) | (-e|--enable REPOPAT) |
-                  (-x|--expire REPOPAT) | (-E|--delete-repo REPOPAT)]
+                  (-x|--expire REPOPAT) | (-E|--delete-repofile REPOPAT)]
                 [(-t|--enable-testing) | (-T|--disable-testing)]
                 [(-m|--enable-modular) | (-M|--disable-modular)] [DNFARGS]
   see https://github.com/juhp/dnf-repo#readme
@@ -33,41 +33,61 @@
   --version                Show version
   -n,--dryrun              Dry run
   -D,--debug               Debug output
+  --exact                  Match repo names exactly
   -s,--save                Save the repo enable/disable state
   -c,--add-copr COPR       Create repo file for copr repo
   -k,--add-koji REPO       Create repo file for koji repo
   -d,--disable REPOPAT     Disable repos
   -e,--enable REPOPAT      Enable repos
   -x,--expire REPOPAT      Expire repo cache
-  -E,--delete-repo REPOPAT Remove unwanted .repo file
+  -E,--delete-repofile REPOPAT
+                           Remove unwanted .repo file
   -t,--enable-testing      Enable testing repos
   -T,--disable-testing     Disable testing repos
   -m,--enable-modular      Enable modular repos
   -M,--disable-modular     Disable modular repos
 ```
 
-## Usage
+## Usage examples
 List repos:
 ```shellsession
 $ dnf-repo
 ```
 
-Update with --enable-testing:
+Update with testing repos:
 ```shellsession
-$ sudo dnf-repo -t update
+$ dnf-repo -t update
 ```
 
-List disabled copr repos:
+List disabled copr repos (actually shows "copr" repos that would be enabled):
 ```shellsession
 $ dnf-repo -e copr
 ```
 
-Disable copr repos for update:
+Disable all copr repos for update:
 ```shellsession
-$ sudo dnf-repo -d copr update
+$ dnf-repo -d copr update
 ```
 
 Disable modular repos permanently:
 ```shellsession
-$ sudo dnf-repo --disable-modular --save
+$ dnf-repo --disable-modular --save
 ```
+(or equivalently `dnf-repo -M -s`).
+
+Switch a system from Rawhide to F37:
+```shellsession
+$ dnf-repo --exact -d rawhide -e fedora distrosync --releasever 37 fedora\*
+```
+
+Note that sudo is used implicitly when needed:
+there is no need to run dnf-repo with sudo.
+
+## Installation
+
+A copr repo is available:
+<https://copr.fedorainfracloud.org/coprs/petersen/dnf-repo/>
+
+## Building
+
+Use {cabal,stack,cabal-rpm} install.
diff --git a/dnf-repo.cabal b/dnf-repo.cabal
--- a/dnf-repo.cabal
+++ b/dnf-repo.cabal
@@ -1,5 +1,5 @@
 name:                dnf-repo
-version:             0.1
+version:             0.2
 synopsis:            DNF wrapper tool to control repos
 description:
         A command-line wrapper of the dnf package manager to
@@ -20,7 +20,7 @@
                      ||  == 8.8.4
                      ||  == 8.10.7
                      ||  == 9.0.2
-                     ||  == 9.2.3
+                     ||  == 9.2.4
 data-dir:            data
 data-files:          copr.fedorainfracloud.orgCOLONOWNERCOLONREPO.repo
                      koji-REPO.repo
@@ -42,7 +42,7 @@
                        extra,
                        filepath,
                        simple-cmd,
-                       simple-cmd-args
+                       simple-cmd-args >= 0.1.8
   default-language:    Haskell2010
   ghc-options:         -Wall
   if impl(ghc >= 8.0)
diff --git a/src/ExpireRepos.hs b/src/ExpireRepos.hs
--- a/src/ExpireRepos.hs
+++ b/src/ExpireRepos.hs
@@ -8,12 +8,12 @@
 expiredFile :: FilePath
 expiredFile = "/var/cache/dnf/expired_repos.json"
 
-expireRepos :: Bool -> [String] -> IO ()
-expireRepos _ [] = error' "no repos to expire given"
-expireRepos dryrun repos = do
+expireRepos :: Bool -> Bool -> [String] -> IO ()
+expireRepos _ _ [] = error' "no repos to expire given"
+expireRepos dryrun debug repos = do
   old <- read <$> readFile expiredFile :: IO [String]
   let expired = nub $ old ++ repos
-  doSudo dryrun "sed" ["-i", "-e",
+  doSudo dryrun debug "sed" ["-i", "-e",
                        "s/" ++ renderShow old ++ "/" ++ renderShow expired ++ "/",
                        expiredFile]
   putStrLn $ "expired now: " ++ show expired
diff --git a/src/Main.hs b/src/Main.hs
--- a/src/Main.hs
+++ b/src/Main.hs
@@ -1,4 +1,4 @@
-{-# LANGUAGE CPP #-}
+{-# LANGUAGE CPP, LambdaCase #-}
 
 -- SPDX-License-Identifier: BSD-3-Clause
 
@@ -30,8 +30,9 @@
     runMain
     <$> switchWith 'n' "dryrun" "Dry run"
     <*> switchWith 'D' "debug" "Debug output"
+    <*> switchLongWith "exact" "Match repo names exactly"
     <*> switchWith 's' "save" "Save the repo enable/disable state"
-    <*> modeOpt
+    <*> many modeOpt
     <*> optional testingOpt
     <*> optional modularOpt
     <*> many (strArg "DNFARGS")
@@ -42,8 +43,7 @@
       DisableRepo <$> strOptionWith 'd' "disable" "REPOPAT" "Disable repos" <|>
       EnableRepo <$> strOptionWith 'e' "enable" "REPOPAT" "Enable repos" <|>
       ExpireRepo <$> strOptionWith 'x' "expire" "REPOPAT" "Expire repo cache" <|>
-      DeleteRepo <$> strOptionWith 'E' "delete-repofile" "REPOPAT" "Remove unwanted .repo file" <|>
-      pure Default
+      DeleteRepo <$> strOptionWith 'E' "delete-repofile" "REPOPAT" "Remove unwanted .repo file"
 
     testingOpt =
       flagWith' EnableTesting 't' "enable-testing" "Enable testing repos" <|>
@@ -63,35 +63,38 @@
 -- FIXME --enable-all-coprs (for updating etc)
 -- FIXME confirm repos if many
 -- FIXME --disable-non-cores (modular,testing,cisco, etc)
-runMain :: Bool -> Bool -> Bool -> Mode -> Maybe Testing -> Maybe Modular
+runMain :: Bool -> Bool -> Bool -> Bool -> [Mode]
+        -> Maybe Testing -> Maybe Modular
         -> [String] -> IO ()
-runMain dryrun debug save mode mtesting mmodular args = do
+runMain dryrun debug exact save modes mtesting mmodular args = do
   hSetBuffering stdout NoBuffering
   withCurrentDirectory "/etc/yum.repos.d" $ do
-    case mode of
-      AddCopr copr -> addCoprRepo copr
-      AddKoji repo -> addKojiRepo repo
-      _ -> return ()
+    forM_ modes $
+      \case
+        AddCopr copr -> addCoprRepo copr
+        AddKoji repo -> addKojiRepo repo
+        _ -> return ()
     repofiles <- filesWithExtension "." "repo"
 --    when debug $ print repofiles
-    nameStates <- sort <$> mapM readRepo repofiles
-    let repoActs = mapMaybe (selectRepo debug mode mtesting mmodular) nameStates
+    nameStates <- sort <$> concatMapM readRepos repofiles
+    let repoActs = concatMap (selectRepo exact modes mtesting mmodular) nameStates
     unless (null repoActs) $ do
       mapM_ print repoActs
       putStrLn ""
-    case mode of
-      ExpireRepo _ -> do
-        putStrLn ""
-        expireRepos dryrun $ mapMaybe expiring repoActs
-      DeleteRepo _ ->
-        mapM_ deleteRepos $ mapMaybe deleting repoActs
-      _ -> return ()
+    forM_ modes $
+      \case
+        ExpireRepo _ -> do
+          putStrLn ""
+          expireRepos dryrun debug $ mapMaybe expiring repoActs
+        DeleteRepo _ ->
+          mapM_ deleteRepos $ mapMaybe deleting repoActs
+        _ -> return ()
     when save $
       if null repoActs
         then putStrLn "no changes to save\n"
         else do
         prompt_ "Press Enter to save repo enabled state"
-        doSudo dryrun "dnf" $
+        doSudo dryrun debug "dnf" $
           "config-manager" :
           concatMap saveRepo repoActs
     if null args
@@ -101,7 +104,7 @@
       sleep 1
       putStrLn ""
       let repoargs = concatMap changeRepo repoActs
-        in doSudo dryrun "dnf" $ repoargs ++ args
+        in doSudo dryrun debug "dnf" $ repoargs ++ args
     where
       -- FIXME pull non-fedora copr repo file
       -- FIXME delete created copr repo file if repo doesn't exist
@@ -123,7 +126,7 @@
             withTempDir $ \ tmpdir -> do
               let tmpfile = tmpdir </> repofile
               unless dryrun $ writeFile tmpfile repodef
-              doSudo dryrun "cp" [tmpfile, repofile]
+              doSudo dryrun debug "cp" [tmpfile, repofile]
 
       addKojiRepo :: String -> IO ()
       addKojiRepo repo = do
@@ -137,7 +140,7 @@
         withTempDir $ \ tmpdir -> do
           let tmpfile = tmpdir </> repofile
           unless dryrun $ writeFile tmpfile repodef
-          doSudo dryrun "cp" [tmpfile, repofile]
+          doSudo dryrun debug "cp" [tmpfile, repofile]
 
       listRepos :: [RepoState] -> IO ()
       listRepos repoStates = do
@@ -157,7 +160,7 @@
           Just owner -> error' $ repofile +-+ "owned by" +-+ owner
           Nothing -> do
             ok <- yesno $ "Remove " ++ takeFileName repofile
-            when ok $ doSudo dryrun "rm" [repofile]
+            when ok $ doSudo dryrun debug "rm" [repofile]
 
 #if !MIN_VERSION_simple_cmd(0,2,4)
 filesWithExtension :: FilePath -> String -> IO [FilePath]
diff --git a/src/Sudo.hs b/src/Sudo.hs
--- a/src/Sudo.hs
+++ b/src/Sudo.hs
@@ -1,9 +1,14 @@
 module Sudo (doSudo) where
 
+import Control.Monad (when)
 import SimpleCmd (cmdN, sudo_)
 
 -- FIXME make this silent (simple-cmd-0.2.7) unless debug
-doSudo :: Bool -> String -> [String] -> IO ()
-doSudo dryrun c args = do
-  (if dryrun then cmdN else sudo_) c args
+doSudo :: Bool -> Bool -> String -> [String] -> IO ()
+doSudo dryrun debug c args = do
+  if dryrun
+    then cmdN c args
+    else do
+    when debug $ cmdN c args
+    sudo_ c args
   putStrLn ""
diff --git a/src/YumRepoFile.hs b/src/YumRepoFile.hs
--- a/src/YumRepoFile.hs
+++ b/src/YumRepoFile.hs
@@ -2,7 +2,7 @@
 
 module YumRepoFile (
   Mode(..),
-  readRepo,
+  readRepos,
   RepoState,
   selectRepo,
   Modular(..),
@@ -22,7 +22,7 @@
 
 data Mode = AddCopr String | AddKoji String
           | EnableRepo String | DisableRepo String
-          | ExpireRepo String | DeleteRepo String | Default
+          | ExpireRepo String | DeleteRepo String
   deriving Eq
 
 data ChangeEnable = Disable String | Enable String | Expire String
@@ -61,87 +61,85 @@
 data Testing = EnableTesting | DisableTesting
   deriving Eq
 
-selectRepo :: Bool -> Mode -> Maybe Testing -> Maybe Modular
-           -> RepoState -> Maybe ChangeEnable
-selectRepo _debug mode mtesting mmodular (name,enabled,file) =
-  case mode of
-    AddCopr repo -> if repo `isSuffixOf` name && not enabled
-                    then Just (Enable name)
-                    else selectOther
-    AddKoji repo -> if repo `isSuffixOf` name && not enabled
-                    then Just (Enable name)
-                    else selectOther
-    EnableRepo pat -> if pat `isInfixOf` name && not enabled
-                  then Just (Enable name)
-                  else selectOther
-    DisableRepo pat -> if pat `isInfixOf` name && enabled
-                   then Just (Disable name)
-                   else selectOther
-    ExpireRepo pat -> if pat `isInfixOf` name
-                  then Just (Expire name)
-                  else Nothing
-    DeleteRepo pat -> if pat `isInfixOf` name
-                  then if enabled
-                       then error' $ "disable repo before deleting: " ++ name
-                       else Just (Delete file)
-                  else Nothing
-    _ -> selectOther
+selectRepo :: Bool -> [Mode] -> Maybe Testing -> Maybe Modular
+           -> RepoState -> [ChangeEnable]
+selectRepo exact modes mtesting mmodular (name,enabled,file) =
+  case modes of
+    [] -> selectOther
+    (mode:modes') ->
+      case mode of
+        AddCopr repo -> if repo `isSuffixOf` name && not enabled
+                        then [Enable name]
+                        else selectOther
+        AddKoji repo -> if repo `isSuffixOf` name && not enabled
+                        then [Enable name]
+                        else selectOther
+        EnableRepo pat -> if pat `matchesRepo` name && not enabled
+                      then [Enable name]
+                      else selectOther
+        DisableRepo pat -> if pat `matchesRepo` name && enabled
+                       then [Disable name]
+                       else selectOther
+        ExpireRepo pat -> [Expire name | pat `matchesRepo` name]
+        DeleteRepo pat -> if pat `matchesRepo` name
+                      then if enabled
+                           then error' $ "disable repo before deleting: " ++ name
+                           else [Delete file]
+                      else []
+      ++ selectRepo exact modes' mtesting mmodular (name,enabled,file)
   where
-    selectOther :: Maybe ChangeEnable
+    matchesRepo = if exact then (==) else isInfixOf
+
+    selectOther :: [ChangeEnable]
     selectOther
       | "modular" `isSuffixOf` name =
         case mmodular of
-          Nothing -> Nothing
+          Nothing -> []
           Just include ->
             case include of
               EnableModular | not enabled ->
                                if "testing" `isInfixOf` name
-                               then if mtesting == Just EnableTesting
-                                    then Just (Enable name)
-                                    else Nothing
-                               else Just (Enable name)
-              DisableModular | enabled -> Just (Disable name)
-              _ -> Nothing
+                               then
+                                 [Enable name | mtesting == Just EnableTesting]
+                               else [Enable name]
+              DisableModular | enabled -> [Disable name]
+              _ -> []
       | "testing" `isInfixOf` name =
           case mtesting of
-            Nothing -> Nothing
+            Nothing -> []
             Just include ->
               case include of
-                EnableTesting | not enabled -> Just (Enable name)
-                DisableTesting | enabled -> Just (Disable name)
-                _ -> Nothing
-      | otherwise = Nothing
+                EnableTesting | not enabled -> [Enable name]
+                DisableTesting | enabled -> [Disable name]
+                _ -> []
+      | otherwise = []
 
-readRepo :: FilePath -> IO RepoState
-readRepo file =
-  parseRepo file . lines <$> readFile file
+readRepos :: FilePath -> IO [RepoState]
+readRepos file =
+  parseRepos file . lines <$> readFile file
 
 -- was called parseIni
-parseRepo :: FilePath -> [String] -> RepoState
-parseRepo file [] = error' $ "empty ini file: " ++ file
-parseRepo file (l:ls) =
-  case trim l of
-    "" -> parseRepo file ls
-    ('#':_) -> parseRepo file ls
-    sec ->
-      let section = secName sec
-          enabled =
-            case dropWhile (not . ("enabled=" `isPrefixOf`)) ls of
+parseRepos :: FilePath -> [String] -> [RepoState]
+parseRepos file ls =
+  case nextSection ls of
+    Nothing -> []
+    Just (section,rest) ->
+      let (enabled,more) =
+            case dropWhile (not . ("enabled=" `isPrefixOf`)) rest of
               [] -> error' $ "no enabled field for " ++ section
-              (e:_) ->
+              (e:more') ->
                 case trim e of
-                  "enabled=1" -> True
-                  "enabled=0" -> False
+                  "enabled=1" -> (True,more')
+                  "enabled=0" -> (False,more')
                   _ -> error' $ "unknown enabled state " ++ e ++ " for " ++ section
-      in (section,enabled,file)
+      in (section,enabled,file) : parseRepos file more
   where
-    secName :: String -> String
-    secName sec =
-      if ' ' `elem` sec
-      then error' $ "section contains space: " ++ sec
-      else case sec of
+    nextSection :: [String] -> Maybe (String,[String])
+    nextSection [] = Nothing
+    nextSection (l:ls') =
+      case l of
         ('[' : rest) ->
           if last rest == ']'
-          then init rest
-          else error' $ "bad section " ++ sec ++ " in " ++ file
-        _ -> error' $ "section not found in " ++ file
+          then Just (init rest, ls')
+          else error' $ "bad section " ++ l ++ " in " ++ file
+        _ -> nextSection ls'
