`[!]` (восклицательный знак в скобках) подстановочный знак в bash

10

Я сталкивался с шаблонами с подстановками и символами подстановки, и меня это особенно интересует [!].

Эта конструкция похожа на [!]конструкцию, за исключением того, что вместо совпадения с любыми символами в скобках она будет соответствовать любому символу, если она не указана между [и ].

rm myfile [!192]

Вышеуказанное, я считаю, удалит все / все файлы, за исключением любых / всех файлов, которые имеют 192 в названии.

Однако меня беспокоит правильное использование этого с расширением файла и, в частности, несколькими условиями.

Каков будет правильный синтаксис в такой ситуации?

rm myfile [!.gif .csv. mp3] 

или

rm myfile [!.gif !.csv !.mp3]

Меня беспокоит то, что точка может быть смещена, и поэтому любой файл с .(который наверняка будет любым из них?) Будет подвергаться манипулированию, когда я пытаюсь вызвать манипулирование конкретными файлами.

Эта конструкция похожа на [ ]конструкцию, за исключением того, что вместо совпадения с любыми символами в скобках она будет соответствовать любому символу, если она не указана между [и ].

(цитируется по адресу http://www.tldp.org/LDP/GNU-Linux-Tools-Summary/html/x11655.htm )

Теперь; для меня это говорит о том, что тогда единственное число !является достаточным, и все значения после него содержатся в пределах диапазона.

IronUhlan
источник
5
в качестве общего совета, используйте ls my-patternили, echo my-commandчтобы проверить, все ли сделано так, как вы хотите, перед запускомrm
Wayne_Yux
Также помните, что многие, если не большинство файлов, не имеют расширений. Расширения в системах Linux обычно не имеют значения и не используются для определения типа файла большинством программ.
тердон
2
Обратите внимание, что ваша первая цитата неверно процитирована, теперь, похоже, в основном говорится, что [!]она похожа, но не похожа[!]
ilkkachu
2
Стоит отметить, что он ^имеет то же значение, что и !в этом контексте, поэтому [^a]исключает aточно так же, как это [!a]делает: если первый символ после [это a! или ^ тогда любой символ, не заключенный в это, совпадает. ( man bash)
десерт
3
rm myfile [!192]удалит myfileлюбой файл односимвольный, а не по имени 1, 9или 2.
Кевин

Ответы:

16

Ссылаясь на man 7 glob:

An  expression  "[!...]"  matches  a single character, namely any character
that is not matched by the expression obtained by removing the first '!' from it.
(Thus, "[!]a-]" matches any single character except ']', 'a' and '-'.)

Акцент на односимвольной части здесь []не может быть использован для целых строк в Bash Pattern Matching.


bash«S Скоба расширение может быть использовано для сопоставления строк, однако нет никакого способа (я знаю) , чтобы отрицать их. Например

1.{gif,csv,mp3}

расширен до

1.gif 1.csv 1.mp3

независимо от того, существуют ли эти файлы.


Как указал wchargin , shoptможно использовать расширенные операторы сопоставления с образцом, одним из которых является !(). После запуска shopt -s extglob, например

1.!(gif|csv|mp3)

расширен до

1.jpg 1.bmp 1.png

если эти файлы существуют в текущем каталоге. Для получения дополнительной информации man bash(раздел РАСШИРЕНИЕ / Расширение пути) и Расширение пути (глобализация) .


Я всегда буду использовать то find, что вы пытаетесь сделать , что предлагает отрицание и многое другое, например, следующим образом:

find . -maxdepth 1 -type f ! -name "*.gif" -a ! -name "*.csv" -a ! -name "*.mp3"

Это просто перечисляет результаты, если вы хотите удалить их, просто добавьте -deleteопцию в конец команды. Как всегда при удалении действий: всегда сначала проверяйте внимательно .

findпредлагает массу полезных опций, очень хорошо объяснил man find.

Десерт
источник
1
Обратите внимание, что эта findкоманда является рекурсивной ( edit: как она была первоначально показана - я мог бы оставить этот комментарий для дополнительной релевантной, но несущественной информации, которую он передает). Чтобы найти файлы, находящиеся только в текущем каталоге, но не в его подкаталогах - и, таким образом, удалить эти файлы только в том случае, если -deleteдобавлено действие, - вы можете добавить -maxdepth 1сразу после find .. Это привело бы его в соответствие с эффектом глобализации, поскольку глобусы не являются рекурсивными в Bash, если не включена и не используется globstarопция оболочки **. Показанный в вопросе шар является нерекурсивным во всех оболочках.
Элия ​​Каган
2
«нет никакого способа (я знаю) , чтобы свести на нет их» -с shopt -s extglob, вы можете использовать echo 1.!(gif|csv|mp3), который печатает все , 1.extгде extнет gif, csvили mp3. (См. Подраздел «Сопоставление с образцом» man bash.)
wchargin
10

Я думаю, что вы неправильно поняли использование скобок. [abc]соответствует один из символов a, bили c. [!abc]соответствует одному символу, который не является a, bили c. Итак, команда

rm myfile [192]

удалит myfileи файлы 1, 9и 2потому что у вас есть пробел между myfile и скобкой. В отличие от команды

rm myfile[192]

удалит файлы myfile1, myfile9и myfile2.

pLumo
источник
правда. Лучше использовать find.
pLumo
На самом деле [] используется для диапазона, [!] Один символ
IronUhlan
1
@IronUhlan, они оба берут диапазон или список символов. Просто другой - это отрицание.
ilkkachu
7

Скобки [ ]обозначают класс персонажа. Любой отдельный символ внутри них может совпадать в данной позиции. Так

file[192]

соответствует трем возможным именам:

file1
file2
file9

Это не соответствует file192, потому что это имеет дело только с единственным символом после file.

Мы также можем использовать диапазоны в скобках. [0-9]соответствует любой единственной цифре в данной позиции. Нам нужны две цифры [0-9][0-9]. Для цифры, за которой следует заглавная буква, мы могли бы использовать [0-9][A-Z]...

! указывает на отрицание.

file[!192]

будет соответствовать файлам, которые имеют какой-либо один символ после, fileкроме 1или 9или 2. Например, он будет соответствовать file7и filesи file%(и многим другим), но нет file192 , потому что в нем есть два дополнительных символа.

Для вашего случая использования я предлагаю использовать findвместо [!]. У него есть notоператор.

Он также рекурсивный , что означает, что по умолчанию он входит во все подкаталоги. Вы можете ограничить его текущим каталогом -maxdepth 1, если вы этого хотите. Подстановочные знаки оболочки (globbing) нерекурсивны (хотя рекурсивное globbing с **может быть включено).

Предполагая, что вы не хотите избегать удаления символических ссылок, мы можем добавить -type dк отмененным тестам, чтобы избежать удаления любых каталогов. Спасибо Элии Каган за это предложение.

find /path -maxdepth 1 -not \( -type d -or -iname "*.gif" -or -iname "*.csv" -or -iname "*.mp3" \)

Если вы видите, что вы хотите, вы можете запустить команду еще раз с -delete

find /path -maxdepth 1  -not \( -type d -or -iname "*.gif" -or -iname "*.csv" -or -iname "*.mp3" \) -delete
Занна
источник
2
И я думаю, что вы также можете сделать несколько диапазонов одновременно. Например, шестнадцатеричная цифра может быть[0-9a-fA-F]
Уилсон
@ Занна, да! Я на самом деле использовал find :) Не знал, что у него оператор «not» :)
IronUhlan