Виключіть із * командного рядка


14

Існує маса ситуацій, коли використання A *практично неминуче - наприклад, 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'}чи так?


3
Якщо ви хочете зберегти лише один або два файли, тоді найпростішим і найпростішим способом було б переміщення цих файлів в інше місце, протріть усі файли, що залишилися, і перемістіть ті, які ви зберегли назад. Якщо ви хочете зберегти набагато більше файлів, я б використовував findцю --deleteопцію (не потрібно виконувати rmкожен файл. Це зайві накладні витрати).
Геннес

Це було б можливістю, але воно майже настільки складне, як і те, що я згадав. Я знаю, що я міг би поєднати команди з чимось подібним mv -t /tmp one two three && rm -rf * && mv -t . /tmp/one /tmp/two /tmp/three, але я вважаю за краще рішення, що дає можливість чітко виключити щось із *. Звичайно, будуть ситуації, коли переміщення або копіювання файлів до іншого пункту призначення не буде можливим.
Девід

2
Саме тому я і не поставив це як відповідь. Лише як менш складний спосіб робити речі (три дуже прості команди проти однієї дещо складнішої). Я ненавиджу складність у поєднанні з rm.
Геннес

Відповіді:


33

Для Bash є варіант оболонки називається extglob, яка за замовчуванням відключена , щоб зберегти сумісність зі стандартним синтаксисом оболонки. Extglob доповнює синтаксис додатковими операторами , як !(), ?(), @()і деякі інші.

Щоб увімкнути extglob, введіть shopt -s extglob. Щоб увімкнути його для поточного типу користувача echo 'shopt -s extglob' >> ~/.bashrc.

Для прикладу rm: З цим extglobможна використовувати

rm -rf !(one|two|three)

Здається, гарне рішення, але я також не хочу вмикати та вимикати щоразу, коли потрапляю в ситуацію, як описана.
Девід

3
@David: ти можеш поставити shoptріч у свою ~/.bashrc, вона не може зашкодити.
enzotib

Якщо я правильно зрозумів, опція extglob взагалі не змінює ефекту *, а натомість додає аналогічні показники (як !), які мають додаткові функції? У цьому випадку це було б прекрасне рішення.
Девід

1
@David: Саме так.
choroba

1
@David Щоб зберегти сумісність зі стандартним синтаксисом оболонки . Вірогідні й інші ситуації, коли поведінка може змінитися.
Герріт

4

Я побудував, крім саме цього (вибачте, що я ще не міг додати документацію).

У вас є папка на зразок наступної:

$ 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:

#!/usr/bin/env bash

except "$@" | xargs -0 ls

3
Це приємно, але насправді не потрібно, коли ви знаєте про extglob - askubuntu.com/a/329233/138369
Девід

1

В якості альтернативи 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' ./*
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.