Существует множество ситуаций, когда использование *
практически неизбежно, например, rm -rf *
в папке, содержащей тысячи вложенных папок и файлов.
Но что, если вы хотите исключить только один или два файла или папки из команды rm
? Я гуглил свой путь и нашел только довольно сложные решения, такие как find . -depth -not \( -name 'one' -o -name 'two' \
-o -name 'three' \) -exec rm {} \;
, как указано здесь .
Есть ли возможность сделать это более простым способом - без этого обхода find
? Например. rm -rf --exclude='one' --exclude='two' --exclude='three' *
как в rsync или просто rm -rf -e 'one','two','three' *
?
Может быть, даже общая возможность исключить вещи из *
(поэтому другие команды, такие как cp
, mv
, ... не имеют реализовать самостоятельно)? Что-то вроде *{'one','two','three'}
или около того?
Для Bash существует опция оболочки под названием extglob
, которая по умолчанию деактивирована , чтобы сохранить совместимость со стандартным синтаксисом оболочки. Extglob дополняет синтаксис дополнительными операторами, такими как !()
, ?()
, @()
и некоторыми другими.
Чтобы включить extglob
, введите shopt -s extglob
. Чтобы оставить его включенным для текущего типа пользователя echo 'shopt -s extglob' >> ~/.bashrc
.
Например, с помощью extglob
вы можете использовать
rm -rf !(one|two|three)
. Я построил за исключением именно для этого (извините, что я еще не смог добавить документацию).
Если у вас есть папка, подобная следующей:
$ ls
a b c d
Набрав except b d
, вы получите a
и c
$ except b d
ac
Теперь вы можете передать это rm
.
except b d | xargs -0 rm
Это может быть трудно запомнить, так почему бы просто не создать скрипт поверх исключений, называемых rm-except
:
#!/usr/bin/env bash
except "$@" | xargs -0 rm
Аналогично легко это ls-except
:
В качестве альтернативы extglob (хотя это очень хороший ответ, и каждый должен иметь shopt -s extglob globstar
в своем .bashrc), вы можете использовать глобальную переменную $ GLOBIGNORE . Допустим, вы хотите получить все файлы, кроме 'foo.txt' и 'bar baz.txt':
GLOBIGNORE=foo.txt:'bar baz.txt'
... однако, это включит параметр оболочки dotglob
, что означает, что *
будет сопоставлять файлы, начинающиеся с точки (которые обычно скрыты). Таким образом, вам на самом деле нужны две команды:
GLOBIGNORE=foo.txt:'bar baz.txt'
shopt -u dotglob
Поскольку это глобальная переменная, она будет влиять на каждый глобус, который вы используете, до тех пор, пока $ GLOBSTAR не будет сброшен при выходе или с помощью
GLOBIGNORE=
Он также будет работать только с литеральными строками, переданными в переменную. Вы можете понять, что я имею в виду, установив $ GLOBIGNORE и посмотрев на разницу между этими командами:
printf '%s\n' *
printf '%s\n' ./*